网易首页 > 网易号 > 正文 申请入驻

车辆UDS诊断CAN规范——从协议原理到C++实现

0
分享至


一、UDS诊断协议概述 1.1 什么是UDS

UDS(Unified Diagnostic Services,统一诊断服务)是汽车电子领域标准的诊断通信协议,定义于ISO 14229标准中。UDS的核心目标是快速、准确地判断车辆或某个ECU的故障类型及故障原因,为维修和诊断提供可靠依据。

UDS诊断协议采用客户端/服务器(Client/Server)架构,诊断工具(Tester)作为客户端发送服务请求,ECU作为服务器返回响应(肯定响应或否定响应)。UDS规范共包含8个部分,其中与CAN总线相关的核心部分是ISO 14229-1(规范和要求)和ISO 14229-3(CAN实现的统一诊断服务,即UDSonCAN)。

1.2 UDS协议在OSI模型中的定位

UDS协议栈在OSI七层模型中的分层如下:

OSI层

协议/规范

应用层

ISO 14229-1

UDS诊断服务定义

表示层

ISO 14229-1

诊断服务参数编码

会话层

ISO 14229-2

会话层服务

传输层

ISO 15765-2 (ISO-TP)

多帧传输、流控

网络层

ISO 15765-2

寻址、分段与重组

数据链路层

ISO 11898-1

CAN协议

物理层

ISO 11898-2/3

CAN物理层

ISO 14229协议定义了最多4095个字节的诊断数据,而传统CAN帧数据段最多只有8个字节,二者之间存在数据长度不匹配的问题。ISO 15765-2(又称ISO-TP)正是为了解决这一问题而设计的传输层协议。

二、传输层:ISO 15765-2(ISO-TP)

ISO-TP是专门为CAN总线上的诊断通信定义的传输协议,广泛应用于汽车行业。其核心功能是对应用层的I-PDU(交互层协议数据单元)进行分段(Segmentation)与重组(Reassembly),使其适应CAN总线数据帧的负载限制。

2.1 寻址模式

ISO-TP支持两种寻址模式:

  • 物理寻址(Physical Addressing):1对1通信,诊断工具与特定ECU之间通信

  • 功能寻址(Functional Addressing):1对N通信,诊断工具向一组ECU发送请求

寻址格式分为三种:

  • 常规寻址(Normal):地址仅由CAN ID表示

  • 扩展寻址(Extended):地址由CAN ID + CAN数据段第一个字节共同表示

  • 混合寻址(Mixed):CAN ID不同但附加字节相同

2.2 帧类型

ISO-TP定义了四种帧类型,通过N_PCI(网络协议控制信息)的第一个字节的高4位来区分:

帧类型

PCI字节高4位

单帧(SF)

0x0

数据≤7字节,单帧传输

首帧(FF)

0x1

多帧传输的第一帧,包含总数据长度

连续帧(CF)

0x2

多帧传输的后续数据帧

流控帧(FC)

0x3

接收方控制发送方速率的帧

单帧(SF):当诊断数据总长度≤7字节时,使用单帧一次传输完成。

多帧传输:当数据长度>7字节时,启动多帧传输流程:

  1. 发送方发送首帧(FF),告知待传输的PDU总长度

  2. 接收方回复流控帧(FC),告知:

  • Block Size(BS):连续帧的最大数量(0表示无限制)

  • STmin:连续帧之间的最小时间间隔

发送方开始发送连续帧(CF),每帧包含数据片段

每发送完一个Block,等待接收方的新流控帧

三、应用层:UDS诊断服务(ISO 14229-1) 3.1 服务标识符(SID)

ISO 14229协议定义了26个标准诊断服务,每个服务由唯一的SID(Service Identifier)标识。这些服务分为6大类:

功能单元

典型服务

诊断与通信管理

0x10(会话控制)、0x11(ECU复位)、0x3E(保活)

故障码传输

0x14(清除DTC)、0x19(读取DTC)

数据传输

0x22(按ID读数据)、0x2E(按ID写数据)

输入输出控制

0x2F(IO控制)

例程控制

0x31(例程控制)

刷写与传输

0x34(请求下载)、0x35(请求上传)、0x36(数据传输)


3.2 请求与响应格式

诊断请求(Request)格式为:SID + Sub-Function(可选)+ Parameter(可选)

肯定响应(Positive Response):SID + 0x40(即SID的最高位置1)

  • 例如:请求0x10服务,肯定响应的第一个字节为0x50

否定响应(Negative Response):0x7F + SID + NRC

  • 例如:请求0x10服务被拒绝,响应为0x7F 0x10 NRC

3.3 常用NRC(否定响应码)

NRC

名称

0x10

GeneralReject

通用拒绝

0x11

ServiceNotSupported

服务不支持

0x12

SubFunctionNotSupported

子功能不支持

0x13

IncorrectMessageLength

消息长度错误

0x22

ConditionsNotCorrect

条件不满足

0x31

RequestOutOfRange

请求超出范围

0x33

SecurityAccessDenied

安全访问被拒绝

0x7F

ServiceNotSupportedInActiveSession

当前会话不支持该服务


四、C++代码实现

以下代码实现了一个完整的UDS on CAN诊断协议栈,包含ISO-TP传输层和UDS应用层。

4.1 ISO-TP帧类型定义

// isotp.hpp - ISO 15765-2 传输层协议实现
#ifndef ISOTP_HPP
#define ISOTP_HPP

#include
#include
#include
#include
#include

namespace uds {

// ISO-TP 帧类型(PCI高4位)
enum class ISOTPFrameType : uint8_t {
SingleFrame = 0x0, // 单帧
FirstFrame = 0x1, // 首帧
ConsecutiveFrame = 0x2, // 连续帧
FlowControl = 0x3 // 流控帧
};

// 流控状态(FC帧的FS字段)
enum class FlowStatus : uint8_t {
ContinueToSend = 0x0, // 继续发送
Wait = 0x1, // 等待
Overflow = 0x2 // 溢出
};

// CAN帧结构(标准11位ID)
struct CanFrame {
uint32_t id; // CAN ID
std::array data; // 数据段
uint8_t dlc; // 数据长度
bool isExtendedId = false; // 是否扩展ID
CanFrame() : data{0}, dlc(0) {}
};

// ISO-TP配置
struct ISOTPConfig {
uint32_t txId; // 发送CAN ID
uint32_t rxId; // 接收CAN ID
uint8_t bs; // Block Size (0=无限制)
uint8_t stmin; // 最小帧间隔 (ms)
uint32_t timeoutMs; // 超时时间
};

// ISO-TP 传输层类
class ISOTP {
public:
using SendCanCallback = std::function;
using ReceiveCallback = std::function&)>;
ISOTP(const ISOTPConfig& config, SendCanCallback sendFn)
: m_config(config), m_sendCan(sendFn) {}
// 发送数据(自动分段)
bool send(const std::vector& data);
// 接收CAN帧处理(由上层调用)
void receiveFrame(const CanFrame& frame);
// 设置接收完成回调
void setReceiveCallback(ReceiveCallback cb) { m_recvCallback = cb; }
private:
// 发送单帧
bool sendSingleFrame(const std::vector& data);
// 发送多帧(首帧 + 连续帧)
bool sendMultiFrame(const std::vector& data);
// 处理接收到的帧
void handleSingleFrame(const CanFrame& frame);
void handleFirstFrame(const CanFrame& frame);
void handleConsecutiveFrame(const CanFrame& frame);
void handleFlowControl(const CanFrame& frame);
// 发送流控帧
void sendFlowControl(FlowStatus status);
ISOTPConfig m_config;
SendCanCallback m_sendCan;
ReceiveCallback m_recvCallback;
// 接收状态机
enum class RecvState { Idle, Receiving };
RecvState m_recvState = RecvState::Idle;
std::vector m_recvBuffer;
uint32_t m_expectedLength = 0;
uint8_t m_nextSn = 0; // 期望的连续帧序号
};

} // namespace uds

#endif // ISOTP_HPP
4.2 ISO-TP核心实现

// isotp.cpp
#include "isotp.hpp"
#include
#include
#include

namespace uds {

bool ISOTP::send(const std::vector& data) {
if (data.empty()) return false;
// 计算PCI开销(单帧1字节PCI,多帧首帧2字节PCI)
const size_t maxSinglePayload = 7; // 8字节CAN数据 - 1字节PCI
const size_t maxFirstPayload = 6; // 8字节CAN数据 - 2字节PCI
if (data.size() <= maxSinglePayload) {
return sendSingleFrame(data);
} else {
return sendMultiFrame(data);
}
}

bool ISOTP::sendSingleFrame(const std::vector& data) {
CanFrame frame;
frame.id = m_config.txId;
frame.dlc = static_cast(1 + data.size());
// PCI字节: 高4位=0x0(单帧),低4位=数据长度
frame.data[0] = static_cast((static_cast(ISOTPFrameType::SingleFrame) << 4)
| data.size());
std::copy(data.begin(), data.end(), frame.data.begin() + 1);
return m_sendCan(frame);
}

bool ISOTP::sendMultiFrame(const std::vector& data) {
if (data.size() > 4095) return false; // UDS最大4095字节
// 1. 发送首帧 (First Frame)
CanFrame ff;
ff.id = m_config.txId;
ff.dlc = 8;
// PCI: 高4位=0x1(首帧),低4位=数据长度高4位
ff.data[0] = static_cast((static_cast(ISOTPFrameType::FirstFrame) << 4)
| ((data.size() >> 8) & 0x0F));
ff.data[1] = static_cast(data.size() & 0xFF); // 数据长度低8位
// 首帧最多携带6字节数据
size_t firstPayload = std::min(6, data.size());
std::copy(data.begin(), data.begin() + firstPayload, ff.data.begin() + 2);
if (!m_sendCan(ff)) return false;
// 2. 等待流控帧(实际应用中需要超时机制)
// 此处为简化示例,假设已收到FC
// 3. 发送连续帧
size_t offset = firstPayload;
uint8_t sn = 1; // 连续帧序号从1开始
size_t remaining = data.size() - offset;
while (remaining > 0) {
CanFrame cf;
cf.id = m_config.txId;
cf.dlc = 8;
// PCI: 高4位=0x2(连续帧),低4位=序号(0-15)
cf.data[0] = static_cast((static_cast(ISOTPFrameType::ConsecutiveFrame) << 4)
| (sn & 0x0F));
size_t chunkSize = std::min(7, remaining); // 连续帧最多7字节数据
std::copy(data.begin() + offset, data.begin() + offset + chunkSize, cf.data.begin() + 1);
// 填充剩余字节为0xCC(实际可根据规范填充)
for (size_t i = 1 + chunkSize; i < 8; ++i) {
cf.data[i] = 0xCC;
}
if (!m_sendCan(cf)) return false;
offset += chunkSize;
remaining -= chunkSize;
sn = (sn + 1) & 0x0F; // 序号循环0-15
// STmin延时
if (m_config.stmin > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(m_config.stmin));
}
}
return true;
}

void ISOTP::receiveFrame(const CanFrame& frame) {
if (frame.dlc < 1) return;
uint8_t pciType = (frame.data[0] >> 4) & 0x0F;
switch (static_cast (pciType)) {
case ISOTPFrameType::SingleFrame:
handleSingleFrame(frame);
break;
case ISOTPFrameType::FirstFrame:
handleFirstFrame(frame);
break;
case ISOTPFrameType::ConsecutiveFrame:
handleConsecutiveFrame(frame);
break;
case ISOTPFrameType::FlowControl:
handleFlowControl(frame);
break;
default:
break;
}
}

void ISOTP::handleSingleFrame(const CanFrame& frame) {
uint8_t len = frame.data[0] & 0x0F; // 低4位为数据长度
if (frame.dlc < 1 + len) return;
std::vector data(frame.data.begin() + 1, frame.data.begin() + 1 + len);
if (m_recvCallback) {
m_recvCallback(data);
}
}

void ISOTP::handleFirstFrame(const CanFrame& frame) {
if (frame.dlc < 2) return;
m_expectedLength = (static_cast(frame.data[0] & 0x0F) << 8) | frame.data[1];
m_recvBuffer.clear();
m_recvBuffer.reserve(m_expectedLength);
// 首帧携带的数据(最多6字节)
size_t payloadLen = std::min(6, m_expectedLength);
m_recvBuffer.insert(m_recvBuffer.end(), frame.data.begin() + 2, frame.data.begin() + 2 + payloadLen);
m_nextSn = 1; // 期望下一个连续帧序号为1
m_recvState = RecvState::Receiving;
// 发送流控帧(允许继续发送)
sendFlowControl(FlowStatus::ContinueToSend);
}

void ISOTP::handleConsecutiveFrame(const CanFrame& frame) {
if (m_recvState != RecvState::Receiving) return;
uint8_t sn = frame.data[0] & 0x0F;
if (sn != m_nextSn) {
// 序号不匹配,丢弃(实际应触发错误处理)
return;
}
// 提取数据(最多7字节)
size_t remaining = m_expectedLength - m_recvBuffer.size();
size_t payloadLen = std::min(7, remaining);
m_recvBuffer.insert(m_recvBuffer.end(), frame.data.begin() + 1, frame.data.begin() + 1 + payloadLen);
m_nextSn = (m_nextSn + 1) & 0x0F;
// 检查是否接收完成
if (m_recvBuffer.size() >= m_expectedLength) {
m_recvState = RecvState::Idle;
if (m_recvCallback) {
m_recvCallback(m_recvBuffer);
}
}
}

void ISOTP::handleFlowControl(const CanFrame& frame) {
// 流控帧处理(发送方收到FC后更新流控参数)
if (frame.dlc < 3) return;
uint8_t fs = frame.data[0] & 0x0F; // Flow Status
uint8_t bs = frame.data[1]; // Block Size
uint8_t stmin = frame.data[2]; // STmin
// 更新发送参数
m_config.bs = bs;
m_config.stmin = stmin;
// 根据FS决定后续动作
if (static_cast (fs) == FlowStatus::Overflow) {
// 溢出:发送方应停止发送
}
}

void ISOTP::sendFlowControl(FlowStatus status) {
CanFrame fc;
fc.id = m_config.rxId; // 使用接收ID作为响应
fc.dlc = 3;
fc.data[0] = static_cast((static_cast(ISOTPFrameType::FlowControl) << 4)
| static_cast(status));
fc.data[1] = m_config.bs; // Block Size
fc.data[2] = m_config.stmin; // STmin
m_sendCan(fc);
}

} // namespace uds
4.3 UDS应用层服务实现

// uds_services.hpp - UDS诊断服务定义
#ifndef UDS_SERVICES_HPP
#define UDS_SERVICES_HPP

#include
#include
#include
#include

namespace uds {

// UDS服务标识符
enum class UDSService : uint8_t {
DiagnosticSessionControl = 0x10,
ECUReset = 0x11,
ClearDiagnosticInformation = 0x14,
ReadDTCInformation = 0x19,
ReadDataByIdentifier = 0x22,
ReadMemoryByAddress = 0x23,
SecurityAccess = 0x27,
CommunicationControl = 0x28,
WriteDataByIdentifier = 0x2E,
InputOutputControl = 0x2F,
RoutineControl = 0x31,
RequestDownload = 0x34,
RequestUpload = 0x35,
TransferData = 0x36,
RequestTransferExit = 0x37,
TesterPresent = 0x3E
};

// 否定响应码
enum class NRC : uint8_t {
PositiveResponse = 0x00,
GeneralReject = 0x10,
ServiceNotSupported = 0x11,
SubFunctionNotSupported = 0x12,
IncorrectMessageLength = 0x13,
ConditionsNotCorrect = 0x22,
RequestOutOfRange = 0x31,
SecurityAccessDenied = 0x33,
ServiceNotSupportedInActiveSession = 0x7F
};

// 诊断会话类型(0x10服务子功能)
enum class DiagnosticSession : uint8_t {
DefaultSession = 0x01,
ProgrammingSession = 0x02,
ExtendedDiagnosticSession = 0x03,
SafetySystemSession = 0x04
};

// UDS请求
struct UDSRequest {
uint8_t sid;
std::vector data; // 子功能 + 参数
};

// UDS响应
struct UDSResponse {
bool isPositive;
uint8_t sid;
std::vector data;
NRC nrc;
};

// UDS服务处理器接口
class UDSServiceHandler {
public:
virtual ~UDSServiceHandler() = default;
virtual UDSResponse handleRequest(const UDSRequest& req) = 0;
};

// UDS服务调度器
class UDSServiceDispatcher {
public:
using ServiceHandler = std::function const UDSRequest&)>;

void registerService(UDSService sid, ServiceHandler handler) ;
UDSResponse dispatch(const UDSRequest& req) ;

private :
std :: unordered_map < uint8_t , ServiceHandler> m_handlers;
};

} // namespace uds

#endif // UDS_SERVICES_HPP
4.4 UDS服务实现示例

// uds_services.cpp
#include "uds_services.hpp"
#include
#include

namespace uds {

// 0x10 诊断会话控制服务
class DiagnosticSessionControlHandler : public UDSServiceHandler {
public:
explicit DiagnosticSessionControlHandler(DiagnosticSession& currentSession)
: m_currentSession(currentSession) {}
UDSResponse handleRequest(const UDSRequest& req) override {
UDSResponse resp;
resp.sid = req.sid;
// 检查请求长度(至少1个子功能字节)
if (req.data.empty()) {
resp.isPositive = false;
resp.nrc = NRC::IncorrectMessageLength;
return resp;
}
uint8_t subFunction = req.data[0];
auto requestedSession = static_cast (subFunction);
// 检查子功能是否支持
if (requestedSession < DiagnosticSession::DefaultSession ||
requestedSession > DiagnosticSession::SafetySystemSession) {
resp.isPositive = false;
resp.nrc = NRC::SubFunctionNotSupported;
return resp;
}
// 执行会话切换
DiagnosticSession oldSession = m_currentSession;
m_currentSession = requestedSession;
// 构造肯定响应: SID+0x40 + 子功能 + 会话参数
resp.isPositive = true;
resp.data.push_back(subFunction); // 回显子功能
// 添加会话参数(示例:会话超时时间等)
// 实际可根据协议添加更多参数
std::cout << "Session switched from 0x" << std::hex << (int)oldSession
<< " to 0x" << (int)requestedSession << std::dec << std::endl;
return resp;
}
private:
DiagnosticSession& m_currentSession;
};

// 0x22 按标识符读取数据服务
class ReadDataByIdentifierHandler : public UDSServiceHandler {
public:
using DataProvider = std::function(uint16_t)>;
explicit ReadDataByIdentifierHandler(DataProvider provider)
: m_dataProvider(provider) {}
UDSResponse handleRequest(const UDSRequest& req) override {
UDSResponse resp;
resp.sid = req.sid;
// 请求至少2字节(DID高字节+低字节)
if (req.data.size() < 2) {
resp.isPositive = false;
resp.nrc = NRC::IncorrectMessageLength;
return resp;
}
// 提取DID(Data Identifier)
uint16_t did = (static_cast(req.data[0]) << 8) | req.data[1];
// 获取数据
std::vector value = m_dataProvider(did);
if (value.empty()) {
resp.isPositive = false;
resp.nrc = NRC::RequestOutOfRange;
return resp;
}
// 构造肯定响应: SID+0x40 + DID + Data
resp.isPositive = true;
resp.data.push_back(req.data[0]); // DID高字节
resp.data.push_back(req.data[1]); // DID低字节
resp.data.insert(resp.data.end(), value.begin(), value.end());
return resp;
}
private:
DataProvider m_dataProvider;
};

// 0x2E 按标识符写入数据服务
class WriteDataByIdentifierHandler : public UDSServiceHandler {
public:
using DataWriter = std::function&)>;
explicit WriteDataByIdentifierHandler(DataWriter writer)
: m_dataWriter(writer) {}
UDSResponse handleRequest(const UDSRequest& req) override {
UDSResponse resp;
resp.sid = req.sid;
if (req.data.size() < 3) {
resp.isPositive = false;
resp.nrc = NRC::IncorrectMessageLength;
return resp;
}
uint16_t did = (static_cast(req.data[0]) << 8) | req.data[1];
std::vector value(req.data.begin() + 2, req.data.end());
if (!m_dataWriter(did, value)) {
resp.isPositive = false;
resp.nrc = NRC::ConditionsNotCorrect;
return resp;
}
// 肯定响应: SID+0x40 + DID
resp.isPositive = true;
resp.data.push_back(req.data[0]);
resp.data.push_back(req.data[1]);
return resp;
}
private:
DataWriter m_dataWriter;
};

// 0x3E 保活服务(Tester Present)
class TesterPresentHandler : public UDSServiceHandler {
public:
UDSResponse handleRequest(const UDSRequest& req) override {
UDSResponse resp;
resp.sid = req.sid;
// 保活服务通常只需要简单确认
// 如果带有子功能0x00表示不需要重置会话超时
bool resetTimeout = true;
if (!req.data.empty() && req.data[0] == 0x00) {
resetTimeout = false;
}
// 肯定响应: SID+0x40
resp.isPositive = true;
// 可选:回显子功能(如果有)
if (!req.data.empty()) {
resp.data.push_back(req.data[0]);
}
std::cout << "Tester Present received, reset timeout: " << resetTimeout << std::endl;
return resp;
}
};

} // namespace uds
4.5 完整的UDS协议栈集成

// uds_stack.cpp - 完整的UDS协议栈集成示例
#include "isotp.hpp"
#include "uds_services.hpp"
#include
#include
#include

class UDSStack {
public:
UDSStack(uint32_t txId, uint32_t rxId)
: m_currentSession(uds::DiagnosticSession::DefaultSession),
m_isotp(uds::ISOTPConfig{txId, rxId, 0, 0, 1000},
[this](const uds::CanFrame& frame) { return this->sendCan(frame); }) {
// 注册UDS服务
registerServices();
// 设置ISO-TP接收回调
m_isotp.setReceiveCallback([this](const std::vector& data) {
this->onISOTPReceived(data);
});
}
// 发送UDS请求(Tester端使用)
bool sendUDSRequest(uint8_t sid, const std::vector& data) {
uds::UDSRequest req{sid, data};
return sendUDSRequest(req);
}
bool sendUDSRequest(const uds::UDSRequest& req) {
// 构造UDS应用层PDU
std::vector pdu;
pdu.push_back(req.sid);
pdu.insert(pdu.end(), req.data.begin(), req.data.end());
// 通过ISO-TP发送
return m_isotp.send(pdu);
}
// 接收CAN帧(由底层CAN驱动调用)
void receiveCanFrame(const uds::CanFrame& frame) {
m_isotp.receiveFrame(frame);
}
// 获取当前诊断会话
uds::DiagnosticSession getCurrentSession() const {
return m_currentSession;
}
private:
bool sendCan(const uds::CanFrame& frame) {
// 实际实现中调用底层CAN驱动发送
std::cout << "Sending CAN frame: ID=0x" << std::hex << frame.id
<< ", DLC=" << (int)frame.dlc << std::dec << std::endl;
return true;
}
void registerServices() {
// 注册0x10服务
m_dispatcher.registerService(
uds::UDSService::DiagnosticSessionControl,
[this](const uds::UDSRequest& req) -> uds::UDSResponse {
uds::DiagnosticSessionControlHandler handler(m_currentSession);
return handler.handleRequest(req);
}
);
// 注册0x22服务
m_dispatcher.registerService(
uds::UDSService::ReadDataByIdentifier,
[this](const uds::UDSRequest& req) -> uds::UDSResponse {
uds::ReadDataByIdentifierHandler handler(
[this](uint16_t did) -> std::vector {
return this->getDataByIdentifier(did);
}
);
return handler.handleRequest(req);
}
);
// 注册0x2E服务
m_dispatcher.registerService(
uds::UDSService::WriteDataByIdentifier,
[this](const uds::UDSRequest& req) -> uds::UDSResponse {
uds::WriteDataByIdentifierHandler handler(
[this](uint16_t did, const std::vector& value) -> bool {
return this->setDataByIdentifier(did, value);
}
);
return handler.handleRequest(req);
}
);
// 注册0x3E服务
m_dispatcher.registerService(
uds::UDSService::TesterPresent,
[](const uds::UDSRequest& req) -> uds::UDSResponse {
uds::TesterPresentHandler handler;
return handler.handleRequest(req);
}
);
}
void onISOTPReceived(const std::vector& data) {
if (data.empty()) return;
// 解析UDS请求
uds::UDSRequest req;
req.sid = data[0];
if (data.size() > 1) {
req.data.assign(data.begin() + 1, data.end());
}
// 分发到对应的服务处理器
uds::UDSResponse resp = m_dispatcher.dispatch(req);
// 构造响应PDU并发送
std::vector responsePdu;
if (resp.isPositive) {
responsePdu.push_back(static_cast(resp.sid + 0x40)); // 肯定响应SID
responsePdu.insert(responsePdu.end(), resp.data.begin(), resp.data.end());
} else {
responsePdu.push_back(0x7F); // 否定响应标识
responsePdu.push_back(resp.sid);
responsePdu.push_back(static_cast(resp.nrc));
}
m_isotp.send(responsePdu);
}
// DID数据提供者(模拟ECU数据)
std::vector getDataByIdentifier(uint16_t did) {
// 模拟DID数据
switch (did) {
case 0xF190: // 车辆识别号(VIN)
return {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'A', 'B', 'C', 'D', 'E', 'F', 'G'};
case 0xF19A: // 软件版本
return {0x01, 0x02, 0x03};
case 0xF19B: // 硬件版本
return {0x01, 0x00};
default:
return {};
}
}
bool setDataByIdentifier(uint16_t did, const std::vector& value) {
// 模拟DID写入
std::cout << "Write DID 0x" << std::hex << did << " with "
<< value.size() << " bytes" << std::dec << std::endl;
return true;
}
private:
uds::DiagnosticSession m_currentSession;
uds::ISOTP m_isotp;
uds::UDSServiceDispatcher m_dispatcher;
};

// 主程序示例
int main() {
// 创建UDS协议栈(Tester端,发送ID=0x700,接收ID=0x708)
UDSStack stack(0x700, 0x708);
// 模拟接收CAN帧(实际由CAN驱动提供)
// 这里演示发送UDS请求
// 1. 发送诊断会话控制请求(切换到扩展会话)
std::cout << "=== Sending Diagnostic Session Control (0x10) ===" << std::endl;
stack.sendUDSRequest(0x10, {0x03}); // 0x03 = ExtendedDiagnosticSession
// 2. 发送读取数据请求(读取VIN)
std::cout << "\n=== Sending Read Data By Identifier (0x22) ===" << std::endl;
stack.sendUDSRequest(0x22, {0xF1, 0x90}); // DID=0xF190
// 3. 发送保活请求
std::cout << "\n=== Sending Tester Present (0x3E) ===" << std::endl;
stack.sendUDSRequest(0x3E, {});
return 0;
}
五、工程实践建议 5.1 协议栈架构设计

在实际嵌入式项目中,UDS协议栈通常遵循AUTOSAR架构设计,从下至上依次为:

  1. CAN驱动层:硬件抽象,负责CAN帧的收发

  2. CAN接口层(CanIf):CAN驱动与上层之间的接口

  3. CAN传输层(CanTp):ISO 15765-2协议实现

  4. PDU路由器(PduR):负责I-PDU的路由分发

  5. 诊断通信管理(DCM):UDS应用层服务实现

5.2 关键技术要点
  1. 流控管理:多帧传输时,接收方通过流控帧控制发送方速率,需正确处理Block Size和STmin

  2. 超时处理:诊断请求有严格的时序要求(P2超时等),需实现完善的超时机制

  3. 会话状态管理:不同诊断会话下可用的服务不同,需实现会话状态机

  4. 安全访问:0x27服务需要实现种子-密钥安全机制

5.3 开源参考实现
  • uds-c:福特工程师开发的开源UDS和OBD-II C语言库

  • uds_standard:完整的C++17 UDS实现,包含ISO-TP完整实现

  • iso14229:面向嵌入式系统的UDS服务器和客户端实现

以上代码提供了一个完整的UDS on CAN诊断协议栈实现框架,涵盖了ISO-TP传输层和UDS应用层服务。在实际产品开发中,需要根据具体的硬件平台和AUTOSAR配置进行适配和优化。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
这就是差距?王曼昱晋级后,日媒一针见血,指出张本美和出局原因

这就是差距?王曼昱晋级后,日媒一针见血,指出张本美和出局原因

林子说事
2026-09-27 15:26:04
0-3惨败新西兰被喊下课!邵佳一,你拿什么跟安东尼奥比?

0-3惨败新西兰被喊下课!邵佳一,你拿什么跟安东尼奥比?

曹老师评球
2026-09-27 21:59:44
陕西男子3次报警,民警拒不派警,致两家四口被杀,法院咋判的?

陕西男子3次报警,民警拒不派警,致两家四口被杀,法院咋判的?

就一点
2026-04-29 17:28:35
原来他俩是亲兄弟,哥哥是全国冠军,弟弟是乒乓天才,都进北京队

原来他俩是亲兄弟,哥哥是全国冠军,弟弟是乒乓天才,都进北京队

深析古今
2026-07-24 16:35:43
国足战韩国首发预测!王钰栋回归,依木兰“上位”,23岁前锋或成奇兵

国足战韩国首发预测!王钰栋回归,依木兰“上位”,23岁前锋或成奇兵

兵哥篮球故事
2026-09-27 16:42:06
31分+14助+6断+9记三分球!22岁探花疯狂增肌,他要抢最佳第六人

31分+14助+6断+9记三分球!22岁探花疯狂增肌,他要抢最佳第六人

微评体育圈
2026-09-28 11:33:33
五百名医生已证实:维生素B12与甲钴胺的真相,最好花点时间看看

五百名医生已证实:维生素B12与甲钴胺的真相,最好花点时间看看

路医生健康科普
2026-08-03 15:25:14
再见!勇士,又被交易了,巴特勒乐疯了……

再见!勇士,又被交易了,巴特勒乐疯了……

体育新角度
2026-09-28 10:51:50
央视直播!林诗栋vs林昀儒,王皓暂停关键,反手火力需拉满

央视直播!林诗栋vs林昀儒,王皓暂停关键,反手火力需拉满

骑马寺的少年
2026-09-27 22:28:48
现阶段的时代红利是什么?网友:麦子熟了5000次,碳水管够头一回

现阶段的时代红利是什么?网友:麦子熟了5000次,碳水管够头一回

夜深爱杂谈
2026-08-15 21:47:35
日男子靠发布反华和吹捧高市早苗视频,两周赚了50万日元,被停止广告收益后,仍要开设新账号

日男子靠发布反华和吹捧高市早苗视频,两周赚了50万日元,被停止广告收益后,仍要开设新账号

环球时报国际
2026-09-28 07:53:02
日媒:严子怡一枪砸穿日本纪录 日本网友:她去参加男子比赛也会赢

日媒:严子怡一枪砸穿日本纪录 日本网友:她去参加男子比赛也会赢

劲爆体坛
2026-09-27 23:15:02
都说油车快淘汰,中秋高速我一辆续航 520km 的电车困在 8 小时长龙里

都说油车快淘汰,中秋高速我一辆续航 520km 的电车困在 8 小时长龙里

小怪吃美食
2026-09-27 18:06:54
一年合同!杨瀚森好友韦斯利签约快船 上季曾妙传助攻小杨精彩暴扣

一年合同!杨瀚森好友韦斯利签约快船 上季曾妙传助攻小杨精彩暴扣

醉卧浮生
2026-09-28 11:42:14
明星赛何润东成全场唯一手柄玩家 一旁陈赫都看呆了

明星赛何润东成全场唯一手柄玩家 一旁陈赫都看呆了

游民星空
2026-09-26 12:13:11
被骂“喝酒不工作”,男子连捅姐夫3刀,事后穿着整齐欲下楼时被抓,曾因犯罪被判拘役!姐夫谅解,法院判了

被骂“喝酒不工作”,男子连捅姐夫3刀,事后穿着整齐欲下楼时被抓,曾因犯罪被判拘役!姐夫谅解,法院判了

南方都市报
2026-09-28 08:16:25
遭网友诅咒“断子绝孙”!罗永浩发文回应:这只是对橡胶娃娃实施最血腥、最残忍的虐待并大喊“服不服”,评论区炸锅

遭网友诅咒“断子绝孙”!罗永浩发文回应:这只是对橡胶娃娃实施最血腥、最残忍的虐待并大喊“服不服”,评论区炸锅

火山詩话
2026-09-28 08:33:04
原来“特别军事行动”,不过是“零号病人”的病态发作

原来“特别军事行动”,不过是“零号病人”的病态发作

秋月独朗
2026-09-26 06:55:59
哈马斯的反渗透不是没防,只是防不过:一套被自己生活方式出卖的天然漏洞!

哈马斯的反渗透不是没防,只是防不过:一套被自己生活方式出卖的天然漏洞!

寰球经纬所
2026-09-27 20:10:57
前央视主持人赵普回忆20年前与刘欢共事:感叹其食量惊人,怀念身体好、精神好的欢哥

前央视主持人赵普回忆20年前与刘欢共事:感叹其食量惊人,怀念身体好、精神好的欢哥

潇湘晨报
2026-09-27 18:29:14
2026-09-28 12:15:00
新能源自动驾驶 incentive-icons
新能源自动驾驶
专注于半导体行业资讯
1015文章数 348关注度
往期回顾 全部

汽车要闻

神龙科技与Momenta达成全球战略合作 2027年起推新车

头条要闻

媒体:俄杜马选举普京获压倒性胜利 俄乌冲突有2大走向

头条要闻

媒体:俄杜马选举普京获压倒性胜利 俄乌冲突有2大走向

体育要闻

114项指控成立,曼城已经完蛋了吗?

娱乐要闻

好汉刘欢:一副好嗓子,一颗赤子心

财经要闻

标价298元核桃油, 实为大豆油冒充

科技要闻

智能体惹了多少祸,OpenAI自己没查清!

态度原创

教育
数码
旅游
时尚
健康

教育要闻

官宣!成都启动学区制治理改革

数码要闻

满电青春集结古都 航嘉满电青年计划9月22日登陆西安电子科技大学

旅游要闻

全球首个具身智能主题乐园落地横琴长隆——以“科技+文旅”深度融合助力新质生产力发展

女人过了三四十岁,一定要准备一件小西装,大方得体又显瘦

鼻子三角区的痘,千万别乱挤!

无障碍浏览 进入关怀版