4. UDS会话层与寻址方式
在UDS诊断节点嵌入式开发中,应用层服务(如0x10会话控制、0x22读数据、0x2E写数据)本身并不直接操作CAN总线。真正把诊断请求和响应“搬”到总线上的,是会话层与传输层。理解物理寻址、功能寻址,以及ISO-TP的单帧、多帧、流控帧机制,是写出稳定诊断协议栈的前提。
一、核心原理:寻址方式决定“谁在听”
UDS over CAN使用29位扩展帧时,通常用地址信息区分诊断对象。两种最基础的寻址方式如下:
- 物理寻址(Physical Addressing):诊断仪针对某一个具体ECU发送请求,只有该ECU会响应。请求ID和响应ID通常成对出现,例如请求0x18DA10F1,响应0x18DAF110。它用于读取故障码、刷写、标定等点对点操作。
- 功能寻址(Functional Addressing):诊断仪向总线上所有ECU广播请求,例如ID 0x18DB33F1。多个ECU可以同时收到并各自决定是否响应。它常用于OBD排放相关读取、统一进入默认会话、统一清除故障码等场景。
功能寻址的响应仍然是物理寻址,即每个ECU用自己的响应ID回复,因此总线上可能出现多个响应帧。开发中必须处理“多响应竞争”和“响应超时”问题,不能假设功能寻址只有一个回复。
二、ISO-TP传输机制:单帧、多帧与流控帧
CAN一帧经典数据最多8字节,而UDS请求或响应经常超过8字节,因此需要ISO 15765-2(ISO-TP)做分段与重组。ISO-TP定义了四种帧类型:
| 帧类型 | PCI高4位 | 作用 | 典型场景 |
|---|---|---|---|
| 单帧 SF | 0x0 | 一帧承载完整诊断数据 | 短请求、短响应,≤7字节 |
| 首帧 FF | 0x1 | 声明总长度并携带第一段数据 | 长响应开始,如读取VIN |
| 连续帧 CF | 0x2 | 按序携带后续数据段 | 长数据分段传输 |
| 流控帧 FC | 0x3 | 接收方控制发送节奏 | 允许继续、等待或溢出 |
单帧的PCI第一个字节为0x0N,其中N表示数据长度(1~7)。例如请求“02 10 03”表示进入扩展会话,长度2,服务0x10,子功能0x03。多帧传输时,发送方先发首帧,接收方回送流控帧,发送方再按流控参数发送连续帧。
三、方法流程:一次多帧响应的完整时序
以诊断仪读取VIN(服务0x22,DID 0xF190)为例,ECU返回的VIN有17字节,加上服务响应头,必然超过7字节,因此走多帧流程:
- 诊断仪发送物理寻址请求:
03 22 F1 90(单帧)。 - ECU回复首帧:
10 14 62 F1 90 ...,其中0x14表示总长度20字节。 - 诊断仪回复流控帧:
30 00 00,表示允许发送、块大小0、最小间隔0。 - ECU连续发送连续帧:
21 ...、22 ...,直到数据发完。 - 诊断仪重组所有段,得到完整响应
62 F1 90 + VIN。
流控帧的三个关键参数是:FS(流状态)、BS(块大小)、STmin(最小间隔)。FS=0表示继续发送,FS=1表示等待,FS=2表示溢出。BS=0表示不再需要流控,可以连续发完;STmin则约束连续帧之间的最小时间间隔,嵌入式发送端必须遵守,否则接收方可能丢帧。
// 典型ISO-TP接收状态机伪代码
switch (pci_type) {
case SINGLE_FRAME:
handle_single_frame(data, len);
break;
case FIRST_FRAME:
total_len = get_total_len(data);
send_flow_control(FS_CTS, BS, STmin);
state = WAIT_CF;
break;
case CONSECUTIVE_FRAME:
if (seq_num == expected_seq) {
append_data(data);
expected_seq++;
}
if (received_len >= total_len) {
deliver_to_uds();
state = IDLE;
}
break;
case FLOW_CONTROL:
apply_fc_params(data);
start_sending_cf();
break;
}
四、案例与场景:功能寻址下的多响应处理
假设整车有发动机ECU、变速箱ECU、网关三个节点。诊断仪用功能寻址发送02 10 03请求进入扩展会话。三个ECU都可能回复06 50 03 00 32 01 F4。此时诊断仪不能只等一个响应就结束,而应设置一个收集窗口,例如100ms内收集所有物理响应,再分别处理。
嵌入式节点侧则要注意:如果本节点不支持该功能,可以选择不响应;如果支持,必须按规范在P2时间内回复。功能寻址请求通常不要求流控,因为请求本身很短;但功能寻址的响应如果很长,仍然可能触发多帧,此时每个ECU各自与诊断仪进行流控交互,总线上会出现多组并行的ISO-TP会话,对诊断仪的缓冲和调度能力要求较高。
五、常见误区与复盘
- 误区一:把功能寻址当物理寻址用。功能寻址广播后,多个ECU同时响应,若诊断仪只处理一个,会误判其他节点无响应。
- 误区二:忽略STmin。连续帧发送过快,接收方来不及处理,表现为随机丢帧或重组失败。嵌入式发送端应在CF之间插入延时。
- 误区三:流控帧BS=0理解错误。BS=0不是“不发送”,而是“不需要再等流控”,发送方可以连续发送剩余所有连续帧。
- 误区四:单帧长度字段算错。单帧PCI中的长度只表示后续数据字节数,不包含PCI本身。写错会导致接收方解析越界。
- 误区五:多帧超时未处理。ISO-TP有N_As、N_Bs、N_Cr等超时参数,嵌入式接收端若不在规定时间内收到下一帧,应终止重组并释放缓冲。
复盘时建议抓取完整CAN日志,按“请求ID → 首帧 → 流控 → 连续帧 → 响应ID”的顺序还原一次诊断交互。重点检查:寻址方式是否正确、PCI类型是否匹配、序列号是否连续、流控参数是否被遵守、总长度是否一致。只有这些环节全部对齐,UDS应用层服务才能稳定运行。