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

车辆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.

相关推荐
热点推荐
奥尼尔评出21世纪NBA最佳阵容一二三阵,一阵无悬念,还遗漏了谁

奥尼尔评出21世纪NBA最佳阵容一二三阵,一阵无悬念,还遗漏了谁

铁甲西奇
2026-09-28 08:31:19
JR:停摆那年签了一支中国球队给我很大笔钱但NBA复赛我却走不了

JR:停摆那年签了一支中国球队给我很大笔钱但NBA复赛我却走不了

狼叔评论
2026-09-27 18:16:22
刘畊宏直播间凉了,“老板娘”的身材戳破了多少人的减肥梦?

刘畊宏直播间凉了,“老板娘”的身材戳破了多少人的减肥梦?

看尽落尘花q
2026-08-23 19:28:46
涉826万存疑交易,“殡葬第一股”前总裁被免职,其管理公司已近30年

涉826万存疑交易,“殡葬第一股”前总裁被免职,其管理公司已近30年

红星资本局
2026-09-27 18:19:12
韩国将自制亚运奖牌!颁给游泳接力预赛选手,韩媒直言组委会违规

韩国将自制亚运奖牌!颁给游泳接力预赛选手,韩媒直言组委会违规

排球黄金眼
2026-09-28 10:35:10
原来贵的东西真有贵的道理!网友:当初嫌贵,买完之后直接真香了

原来贵的东西真有贵的道理!网友:当初嫌贵,买完之后直接真香了

另子维爱读史
2026-08-22 21:08:52
巴祖努:杀害无辜民众是错误的,我决定不参加对以色列比赛

巴祖努:杀害无辜民众是错误的,我决定不参加对以色列比赛

懂球帝
2026-09-27 12:38:39
美国房贷利率离谱飙升到 7.48%!引发了老登和年轻人的死战

美国房贷利率离谱飙升到 7.48%!引发了老登和年轻人的死战

兵卒史
2026-09-27 01:23:39
年纪大了,行房要坚持“3不要”,尤其是最后1个,可能伤身!

年纪大了,行房要坚持“3不要”,尤其是最后1个,可能伤身!

健康科普365
2026-08-10 18:25:07
公路车骑行服,熟女骑行穿搭,利落面料勾勒饱满曲线

公路车骑行服,熟女骑行穿搭,利落面料勾勒饱满曲线

只要高兴就好
2026-09-24 11:58:50
指责泽连斯基违反保密协议,李在明小题大作了:至少两名战俘活了下来

指责泽连斯基违反保密协议,李在明小题大作了:至少两名战俘活了下来

鹰眼Defence
2026-09-27 17:31:27
蔡澜说,刘銮雄追女明星,都是先送3000万,这样90%港姐可以拿下

蔡澜说,刘銮雄追女明星,都是先送3000万,这样90%港姐可以拿下

晓銊就是我
2026-09-07 12:39:23
日媒:即使在雨中中岛瞳也美得令人窒息 日本选手成绩+比美都赢了

日媒:即使在雨中中岛瞳也美得令人窒息 日本选手成绩+比美都赢了

劲爆体坛
2026-09-28 09:11:02
所谓的极端女拳,不过是惯出来的毛病

所谓的极端女拳,不过是惯出来的毛病

云石
2026-09-18 14:59:32
官媒痛斥、丑态尽显、大字不识,难怪国家广电总局拿顶流明星下手

官媒痛斥、丑态尽显、大字不识,难怪国家广电总局拿顶流明星下手

桑启红原
2026-09-28 10:09:48
又曝出轨大瓜,男子自曝妻子出轨班长5个月,班长只给他妻子买过情趣内衣,还曝光俩人大尺度聊天内容

又曝出轨大瓜,男子自曝妻子出轨班长5个月,班长只给他妻子买过情趣内衣,还曝光俩人大尺度聊天内容

汉史趣闻
2026-09-10 18:28:47
上海11号线的高薪幻觉:月薪2万在圈子很普通!

上海11号线的高薪幻觉:月薪2万在圈子很普通!

小怪吃美食
2026-09-28 03:44:29
最牛美女处长,1000天和男上司开房410次,临时工晋升财务副处长

最牛美女处长,1000天和男上司开房410次,临时工晋升财务副处长

就一点
2026-09-05 14:09:14
全体股民做好准备了,从明天周一9.28起,或迎来更大调整行情?

全体股民做好准备了,从明天周一9.28起,或迎来更大调整行情?

风风顺
2026-09-28 01:00:06
亚运会最新奖牌榜:日本157枚,韩国83枚,中国健儿太让国人自豪

亚运会最新奖牌榜:日本157枚,韩国83枚,中国健儿太让国人自豪

锅锅爱历史
2026-09-28 06:23:47
2026-09-28 12:44:49
新能源自动驾驶 incentive-icons
新能源自动驾驶
专注于半导体行业资讯
1015文章数 348关注度
往期回顾 全部

汽车要闻

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

头条要闻

供销社职工以46600元参与拐卖外籍妇女 获刑5年9个月

头条要闻

供销社职工以46600元参与拐卖外籍妇女 获刑5年9个月

体育要闻

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

娱乐要闻

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

财经要闻

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

科技要闻

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

态度原创

游戏
亲子
时尚
健康
军事航空

《堡垒之夜》2026全球赛冠军出炉,获40万美元奖金

亲子要闻

世界上最危险的十大玩具,为何成为了可怕的伤人利器?

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

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

军事要闻

特朗普"罕见"提二战中美同盟 日本慌了

无障碍浏览 进入关怀版