news 2026/4/3 8:09:48

快速理解UDS诊断中的SID与DID含义

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
快速理解UDS诊断中的SID与DID含义

一文吃透UDS诊断中的SID与DID:从协议本质到实战开发

你有没有遇到过这样的场景?

在CANoe里抓了一堆报文,看到22 F1 87就懵了:“这到底是读什么?”
刷写ECU时提示“Negative Response: 0x31”,翻手册半天才反应过来是DID不支持;
写自动化脚本读取发动机温度,结果返回的数据像乱码——原来是字节序搞反了……

这些问题的背后,其实都绕不开两个最基础、却又最容易被“想当然”的概念:SIDDID

它们不是高深的加密算法,也不是复杂的通信状态机,但却是每一个汽车电子工程师在接触UDS(Unified Diagnostic Services)时必须跨过的第一道门槛。理解得越深,你在诊断开发、测试和调试中走得就越稳。

今天我们就抛开教科书式的罗列,用“人话+实战视角”彻底讲清楚:

SID到底是什么?DID又凭什么成为数据访问的钥匙?它们是怎么配合工作的?


为什么我们需要UDS?背景比定义更重要

现代一辆中高端车里有上百个ECU——发动机、刹车、空调、仪表、ADAS……这些“大脑”怎么维护?

总不能每个模块都拆开来测吧?于是行业达成共识:给每个ECU装一个“维修窗口”,通过标准协议对外提供服务。这个协议就是UDS(ISO 14229-1)

它运行在CAN总线上,就像一套通用的“维修暗语”。只要你说对了口令,ECU就会乖乖配合。

而所有“口令”的核心结构,就是:

[做什么] + [操作谁]

对应到UDS里:
- “做什么” →SID
- “操作谁” →DID

别小看这两个字段,整个UDS生态都是围绕它们构建起来的。


SID:你的诊断请求是个“动词”

它的本质是一个服务编号

想象你在餐厅点菜:
- 你说“来份宫保鸡丁”——这是个具体动作。
- 菜单上会给每道菜编个号,比如“A03”。

在UDS世界里,SID就是那个“菜单编号”,只不过它是十六进制的单字节值(0x00 ~ 0xFF),代表你要执行哪类诊断操作。

比如:
| SID (Hex) | 中文含义 | 英文原名 |
|----------|--------|---------|
|0x10| 切换诊断会话 | Diagnostic Session Control |
|0x22| 按标识符读数据 | Read Data by Identifier |
|0x2E| 按标识符写数据 | Write Data by Identifier |
|0x3E| 心跳保持 | Tester Present |
|0x14| 清除故障码 | Clear Diagnostic Information |

你可以把SID理解为“动词”:你想?那就用0x22;想?就用0x2E;想重启?可能是0x11子功能配合使用。


请求/响应机制:如何确认ECU听懂了?

当诊断仪发送一条命令时,格式通常是:

[SID] [Sub-function] [Parameters...]

ECU收到后,如果处理成功,会返回一个“正响应”:

[SID + 0x40] [Sub-function] [Data...]

注意这个+0x40的设计非常巧妙:

  • 原始请求是0x22(读数据)
  • 正面响应变成0x62(即 0x22 + 0x40)

这样接收方一看就知道:“哦,这是刚才我发的那个读请求的回信。”

如果出错了呢?那就走否定路径:

7F [Original SID] [NRC]

例如:

7F 22 12 → 表示 SID=0x22 不支持或子功能无效

其中 NRC(Negative Response Code)告诉你错在哪,常见的有:
-0x11: 服务不支持
-0x12: 子功能不支持
-0x31: 请求超出范围(比如DID不存在)

这套反馈机制保证了通信的可追溯性和容错能力。


实战代码:ECU端如何分发SID?

在嵌入式开发中,我们通常会在主循环或CAN中断里监听到来的诊断帧,并根据第一个字节做路由:

void uds_handle_request(uint8_t *req, uint8_t len) { if (len == 0) return; uint8_t sid = req[0]; switch (sid) { case 0x10: handle_session_control(req + 1, len - 1); break; case 0x22: handle_read_by_id(req + 1, len - 1); break; case 0x2E: handle_write_by_id(req + 1, len - 1); break; case 0x3E: handle_tester_present(req + 1, len - 1); break; default: send_nrc(sid, 0x11); // Service not supported break; } }

这段代码虽然简单,却是UDS协议栈的核心骨架。你会发现几乎所有量产项目的诊断模块,底层逻辑都长这样。

⚠️ 小贴士:实际项目中建议加一层安全检查,比如判断当前是否处于允许该服务的会话模式下,否则即使SID合法也应拒绝。


DID:你要操作的“数据身份证”

它不是数据本身,而是数据的“名字”

很多人初学时容易混淆:以为DID里存的是VIN或者版本号。

错!DID只是一个编号,用来告诉ECU:“我要拿你内存里的某样东西,请按这个名字去找。”

就像你去图书馆借书,你说“我要《深入理解计算机系统》”,管理员不会当场背给你听,而是根据书名去架上找出来再交给你。

DID就是这本书的“书名+索书号”。

典型例子:
| DID (Hex) | 含义 |
|----------|------|
|0xF187| 车辆识别码 VIN |
|0xF190| ECU硬件零件号 |
|0xF101| 引导程序版本 |
|0xF189| 软件版本号 |
|0xF400| 当前里程数(厂商自定义) |

这些DID的具体映射关系,一般记录在ODX文件或内部设计文档中,由OEM统一管理。


工作流程:一次典型的DID读取发生了什么?

以读取VIN为例,完整链路如下:

  1. 诊断仪发出请求帧:
    22 F1 87

  2. 网关转发至目标ECU(如BCM)

  3. ECU解析:
    - SID = 0x22 → 要读数据
    - DID = 0xF187 → 查表看看有没有这个条目

  4. 找到匹配项,调用其读取函数:
    c uint8_t read_vin(uint8_t *out_buf) { memcpy(out_buf, g_stored_vin, 17); return 17; // 返回长度 }

  5. 组装响应并发送:
    62 F1 87 56 49 4E 31 32 33 34 35
    其中56 49 4E...是ASCII编码的 “VIN12345”

  6. 诊断仪收到后解析:前三个字节校验无误,后面提取字符串即可显示

整个过程通常在几十毫秒内完成,实时性完全满足需求。


高阶特性:DID不只是“读变量”

你以为DID只能读静态参数?远不止!

✅ 支持批量读取

一个请求可以带多个DID:

22 F187 F190 F101

ECU依次查找并返回:

62 F187 ... 62 F190 ... 62 F101 ...

当然受限于CAN帧长度(最多8字节数据),一般一次最多读2~3个短DID。

✅ 可绑定动态计算值

有些DID并不直接对应内存地址,而是需要实时计算:
- 发动机平均油耗(基于历史数据积分)
- 整车健康评分(多传感器融合)
- CAN负载率统计

这时DID更像是一个“API接口”,触发一段逻辑运算后再返回结果。

✅ 权限控制与安全隔离

敏感DID(如安全密钥、里程修改)往往设置了访问门槛:
- 必须先进入扩展会话(SID 0x10)
- 再通过安全解锁(SID 0x27)
- 才能访问特定DID

这种分层防护机制有效防止非法篡改。


实战代码:DID查表机制怎么做才高效?

资源有限的ECU不适合用哈希表,常见做法是维护一张静态描述符表

typedef struct { uint16_t did; uint8_t min_len; // 最小允许长度 uint8_t (*read_fn)(uint8_t*); uint8_t (*write_fn)(const uint8_t*, uint8_t); } DidEntry; // 实现函数声明 uint8_t read_vin(uint8_t *out); uint8_t read_ecu_part_no(uint8_t *out); uint8_t write_odometer(const uint8_t *data, uint8_t len); // DID注册表(按升序排列便于优化查找) const DidEntry did_map[] = { {0xF101, 1, read_boot_version, NULL}, {0xF187, 17, read_vin, NULL}, {0xF190, 1, read_ecu_part_no, NULL}, {0xF400, 4, read_odometer, write_odometer}, // 可读写 }; #define DID_COUNT (sizeof(did_map)/sizeof(DidEntry))

处理函数遍历查找(线性搜索足够快,除非DID极多):

void handle_read_by_id(uint8_t *data, uint8_t len) { while (len >= 2) { uint16_t did = (data[0] << 8) | data[1]; const DidEntry *entry = NULL; for (int i = 0; i < DID_COUNT; i++) { if (did_map[i].did == did) { entry = &did_map[i]; break; } } if (!entry || !entry->read_fn) { send_nrc(0x22, 0x31); // Request out of range return; } uint8_t bytes_written = entry->read_fn(&tx_buf[3]); tx_buf[0] = 0x62; tx_buf[1] = data[0]; tx_buf[2] = data[1]; can_send(0x7E8, tx_buf, 3 + bytes_written); data += 2; len -= 2; } }

这套机制清晰、易维护、适合量产环境,很多AUTOSAR项目也采用类似思路。


实际工程中的那些“坑”,你知道吗?

理论懂了,落地照样踩雷。以下是我们在项目中总结的真实经验:

❌ 问题1:明明写了DID,却收不到响应?

可能原因:
- 当前处于默认会话,某些DID被禁用
- CAN波特率不对(特别是进入Bootloader后切换了速率)
- 报文ID错误(物理寻址 vs 功能寻址混淆)

✅ 解法:先发个10 03进入扩展会话,再尝试读取。


❌ 问题2:读出来的数据像是乱码?

常见于以下情况:
- 字节序错误:x86是小端,某些MCU是大端
- 编码方式误解:以为是ASCII,其实是BCD或IEEE float

举例:温度值返回0x42 C8 00 00,你以为是整数?
其实是 IEEE 754 单精度浮点数 → 对应100.0°C

✅ 解法:务必查阅ODX或DFI文档确认数据类型和格式!


❌ 问题3:同一个DID,在不同车型上含义不一样?

是的!尤其是厂商自定义区域(如0xFxxx中的部分段落)可能未标准化。

比如0xF1AA在A车上是电池电压,在B车上却是胎压校准标志。

✅ 解法:建立企业级DID分配规范,避免跨平台冲突。


✅ 最佳实践建议

项目推荐做法
DID管理建立全局DID登记表,禁止随意分配
安全控制敏感DID必须配合安全访问(SID 0x27)
性能优化高频DID缓存最新值,减少重复采集
日志审计记录关键DID的访问时间与来源
可扩展性预留10%~20% DID范围用于后期升级

结语:SID与DID,是起点,更是基石

回到开头的问题:

“22 F187” 到底意味着什么?

现在你应该能脱口而出:
-0x22Read Data by Identifier—— 我要读数据
-0xF187VIN—— 我要读的是车辆识别码

这就是UDS最基础的语言单元。

掌握它,你才能进一步去做:
- 自动化诊断脚本编写(Python + CANalyzer)
- OTA升级前的状态采集
- 故障码自动清除工具开发
- 远程诊断云平台对接

未来的智能汽车不仅是“轮子上的手机”,更是“网络化的分布式系统”。而UDS,就是连接这一切的“神经系统”。

SID与DID虽小,却承载着整个车载诊断世界的秩序与逻辑。

如果你正在入门汽车电子,不妨从读懂第一条22 F187开始。
也许下一个优化诊断效率的人,就是你。

欢迎在评论区分享你第一次成功读出VIN时的心情 😄

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/1 6:16:10

OpenCode效果展示:AI编程助手生成的惊艳代码案例

OpenCode效果展示&#xff1a;AI编程助手生成的惊艳代码案例 1. 引言&#xff1a;为什么我们需要终端原生的AI编程助手&#xff1f; 在现代软件开发中&#xff0c;开发者对效率的要求越来越高。传统的IDE插件式AI辅助工具虽然功能丰富&#xff0c;但往往依赖云端服务、存在隐…

作者头像 李华
网站建设 2026/4/3 7:49:21

轻量级人脸分析系统:日志监控方案

轻量级人脸分析系统&#xff1a;日志监控方案 1. 引言 1.1 AI 读脸术 - 年龄与性别识别 在智能安防、用户画像构建和人机交互等场景中&#xff0c;人脸属性分析正成为一项关键的前置技术能力。其中&#xff0c;年龄与性别识别作为基础的人脸语义理解任务&#xff0c;因其低复…

作者头像 李华
网站建设 2026/3/31 19:58:19

Glyph OCR全流程可视化,调试分析更方便

Glyph OCR全流程可视化&#xff0c;调试分析更方便 1. 引言&#xff1a;从“看图识字”到“理解字形”的范式跃迁 在传统OCR技术长期依赖像素级特征提取与序列建模的背景下&#xff0c;智谱AI推出的Glyph-视觉推理模型提出了一种全新的思路——将文字识别问题转化为“字形理解…

作者头像 李华
网站建设 2026/3/25 6:01:48

如何快速掌握文本转CAD工具:Zoo UI完整使用指南

如何快速掌握文本转CAD工具&#xff1a;Zoo UI完整使用指南 【免费下载链接】text-to-cad-ui A lightweight UI for interfacing with the Zoo text-to-cad API, built with SvelteKit. 项目地址: https://gitcode.com/gh_mirrors/te/text-to-cad-ui 想要通过简单的文字…

作者头像 李华
网站建设 2026/3/27 21:25:56

AI智能证件照制作工坊如何嵌入OA系统?内网集成实战案例

AI智能证件照制作工坊如何嵌入OA系统&#xff1f;内网集成实战案例 1. 引言&#xff1a;业务场景与集成需求 在企业日常办公中&#xff0c;员工入职、档案管理、门禁系统配置等环节均需标准证件照。传统方式依赖外部拍摄或人工PS处理&#xff0c;流程繁琐且存在隐私泄露风险。…

作者头像 李华
网站建设 2026/4/1 16:53:26

如何评估卡通化效果?unet主观评分标准建立

如何评估卡通化效果&#xff1f;UNet主观评分标准建立 1. 功能概述与技术背景 人像卡通化技术近年来在社交娱乐、数字内容创作等领域广泛应用。基于UNet架构的图像风格迁移模型&#xff0c;如ModelScope平台提供的DCT-Net&#xff0c;在保持人物身份特征的同时实现高质量的卡…

作者头像 李华