资讯动态

ISO 15765-2(ISO-TP)协议详解:从帧格式到流控参数

发布时间:2026/10/16 10:34:16 来源:尧图企业网站定制
先把结论放前面经典 CAN 的数据场只有 8 字节而一条完整的 UDS 诊断响应动辄几百字节做 Bootloader 刷写时更是按 4KB 数据块往下推。如果直接在应用层硬塞一次“读数据”得拆成几百个独立请求应答没法对齐时序没法保证诊断协议直接废掉。ISO 15765-2也就是常说的 ISO-TP就是来解决这个问题的——它在 CAN 应用层之下、数据链路层之上垫了一层“分段传输”机制把长报文切成多帧发送接收端重组后再交给上层的 UDS。这篇就把这层协议从帧格式、时序参数到传输流程完整拆一遍。1. 为什么 UDS 必须有一层 TP8 字节 CAN 和 4KB 诊断数据之间的鸿沟1.1 CAN 帧容量与诊断数据规模的天然矛盾CAN 2.0A 标准帧的数据场只有 8 字节CAN 2.0B 扩展帧虽然 ID 从 11 位扩到了 29 位但数据场依然只有 8 字节。CAN FD 把数据场扩到了 64 字节但那是后话传统 CAN 网络、绝大多数量产 ECU 的诊断通道至今仍跑在 8 字节 CAN 上。UDSISO 14229定义的服务里0x22 读数据可以一次请求多个 DID比如把 VIN、ECU 硬件版本、软件版本、生产日期一次全读回来响应长度很容易突破 100 字节。0x2E 写数据、0x36 传输数据块就更夸张OEM 的刷写规范里数据块尺寸普遍是 4096 字节4KB配合 0x34 请求下载时带上的 blockLength 参数一起用。这就出现了一道硬鸿沟上层一次响应可能有 4KB底层一帧只能塞 8 字节差着 512 倍。如果让应用层自己拆包那每个 CAN 帧都得附加序号、总长度、校验之类的元信息应用层代码会被这种“传输细节”淹没DID 和数据块语义反而成了陪衬。诊断协议栈的架构分层本质上就是要把“传输长数据”这件事从应用逻辑里剥离出去交给专门的协议层处理。1.2 没有 TP 层会怎样一个“裸传”的反面推演假设不用 ISO-TP直接拿 CAN 帧装 UDS 报文会出现三个实际问题。第一个是长度歧义接收方收到一帧数据怎么知道这是完整的报文还是一段碎片8 字节正好够一个完整的单帧响应但对超过 8 字节的收完第一帧之后得一直等后续帧怎么判断“等完了”没有总长度字段只能靠超时猜一旦中间丢一帧整个会话就挂死。第二个是重组无序如果多帧并发比如同时存在多条诊断请求的响应收方怎么区分哪些帧属于同一个报文没有多帧标识和服务绑定关系帧之间串了就全乱套。第三个是流控缺失发送方一口气把 100 帧全发出去接收方的 CAN 控制器缓冲区和应用层接收队列根本扛不住丢帧是必然的。丢一帧就得整条报文重传效率惨不忍睹。ISO 15765-2 的设计目标恰好逐个消除这三个问题用 PCIProtocol Control Information字段携带帧类型和总长度解决长度歧义用首帧/连续帧的序列号解决帧归属和排序用流控帧做发送节奏控制接收方说“能收多少、多久收一次”发送方就按这个节奏来。单看这些机制不复杂但组合起来就形成了一套完整的、经过量产验证的分段传输协议。2. ISO 15765-2 帧格式拆解SF、FF、CF、FC 四个角色2.1 协议控制信息 PCI 与四种帧类型的身份判定ISO-TP 把传输过程里的每个 CAN 帧都叫“协议帧”通过首字节的高半个字节4 bit来区分身份。这 4 bit 就是 PCI 类型标识取值和含义非常固定PCI 类型值帧类型英文缩写作用一句话0x0单帧SFSingle Frame数据不超过 7 字节时一帧搞定0x1首帧FFFirst Frame长报文的起始帧宣告总长度0x2连续帧CFConsecutive Frame后续数据帧带 4 bit 序号0x3流控帧FCFlow Control接收方发给发送方控制发送节奏需要强调一点首帧的 PCI 高半字节等于 0x1并不是说“第一个字节是 0x10~0x1F”而是整个首字节的低 4 bit 和第二个字节组合成 12 bit 的总长度。也就是说FF 的 PCI 占用两个字节而 SF、CF、FC 的 PCI 只占一个字节。理解了这个字节占用差异后面算“每帧实际可用数据长度”就不会晕。2.2 单帧 SF小报文的一站式交付单帧格式是 1 字节 PCI 最多 7 字节数据经典 CAN字节布局[ 0x0N ][ D1 ][ D2 ] ... [ D7 ]其中0x0N的 N 是低 4 位表示数据长度1~7。如果一条 UDS 请求只有 2 字节比如 0x10 0x01 会话切换发送方直接构造一个单帧首字节 0x02后面跟 0x10 0x01发出去就完事。接收方看到 PCI 类型为 0就知道不用等后续帧这一帧就是完整报文。单帧最多 7 字节是因为首字节用来放长度了数据区只剩 7 字节。如果用扩展寻址extended addressing长度字节后面还要插一个目标地址字节可用数据进一步缩到 6 字节。这意味着地址格式直接决定有效载荷换寻址方式等于重新算所有帧长这是个在工程上经常被忽略的细节。2.3 首帧 FF长报文的“宣言书”首帧格式是 2 字节 PCI 最多 6 字节数据字节布局[ 0x1L ][ 总长度低8位 ][ D1 ] ... [ D6 ]第一个字节的高 4 位固定 0x1低 4 位是 12 位总长度的高 4 位第二个字节是总长度的低 8 位两个字节拼起来是 12 位无符号整数范围 0~4095这就是标题里说“4KB”的来历注意一个容易误解的点首帧本身会携带最多 6 字节的实际数据这 6 字节是紧跟在前 2 字节 PCI 之后的不需要再额外说明“首帧有没有数据”——只要报文总长大于 7首帧必然带数据而且固定带 6 字节经典 CAN 标准寻址下。12 位长度上限 4095不是协议偷懒而是设计时基于分段帧序号和流控能力折中的结果。对 UDS 刷写来说一个 block 4KB 刚好卡在边界内再大的数据就得分多个 block通过多次 0x36 请求传输这个设计本身也契合刷写协议按块确认的机制。2.4 连续帧 CF数据主体与 4 bit 序列号回绕首帧之后的数据全部由连续帧承载。每个连续帧字节布局[ 0x2N ][ D1 ] ... [ D7 ]N 是 4 bit 序列号从 1 开始递增到 0xF 后下一个回到 0x0然后继续 0x1、0x2……这个回绕逻辑是 TP 层最容易写错的地方。序列号只覆盖连续帧首帧本身不算序号。所以发送流程是FF不带序号→ CF 序号 1 → CF 序号 2 → …… → CF 序号 15 → CF 序号 0 → CF 序号 1。接收方靠这个连续递增回绕后依然连续的序列号判断有没有丢帧。如果收到 CF 的序号不是“上一个序号 1”模 16 意义下说明中间有帧丢了整个报文作废只能等发送方超时重传。举例FF 之后收到 CF 序号 3而上一个 CF 序号是 1那序号 2 就是丢了。此时接收方不能只补收一个 2因为 CAN 本身不保证帧顺序恢复只能整条报文丢弃。后面讲排查时你会看到序列号错乱是 CAN 总线负载高时最常见的 TP 层异常之一。2.5 流控帧 FC接收方给发送方的“节流阀”流控帧是唯一一个反向的帧——由接收方发给发送方。格式字节布局[ 0x3F ][ BS ][ STmin ]高 4 位固定 0x3低 4 位是流控状态 FSFlow StatusBS块大小Block Size允许发送方连续发送的 CF 帧数量STmin最小帧间隔时间Separation Time Minimum单位毫秒两个连续帧之间的最小间隔FS 有三个取值FS 值含义发送方行为0x0ContinueToSendCTS按 BS 和 STmin 继续发 CF0x1WaitWT暂停发送等待下一个 FC0x2Overflow / abort中止本次传输报文作废实际量产中 0x1 用得很少0x2 在接收缓冲区不足时会出现。掌握 BSD 和 STmin 的配合逻辑对调优传输速率很重要后面单独开一节细讲。3. 一次完整传输复盘从 6 字节请求到 4KB 响应的全链路3.1 标准寻址模式下的 4KB 响应分帧计算先把数字算清楚。经典 CAN、标准寻址、数据场 8 字节的条件下单帧能装 7 字节有效数据首帧能装 6 字节有效数据每个连续帧能装 7 字节有效数据假设一个 4096 字节的响应用首帧发前 6 字节剩余 4090 字节4090 ÷ 7 584.285向上取整得 585 个连续帧总帧数 1FF 585CF 586 帧如果 STmin 是 10ms不考虑流控帧时间光发送就要 585 × 10ms 5.85 秒。这个数字对刷写来说太慢了所以量产刷写时 STmin 通常会压到 1ms 甚至 0ms配上较宽的 BS 来提速。后面会讲参数怎么选。这里有个值得注意的“4KB 情结”12 位 FF 长度上限 4095而 4KB 就是 4096刚好超了 1 字节。所以严格按 4KB 定义 block 的规范实际首帧长度字段必须写 4096这在 12 位长度里表示不了。实操中要么 block 定义成 4095 字节要么用 CAN FD 把单帧容量提上去。这也是为什么很多 OEM 的刷写 blockLength 写的是 4095 而不是 4096。3.2 请求阶段单帧完成 0x34 请求下载拿刷写过程中的“请求下载”举例。0x34 服务带数据和长度信息请求通常这样组织[ 0x34 ][ 数据格式 ][ 地址和长度信息 ][ 0xFF ][ blockLength 高字节 ][ blockLength 低字节 ]对经典 CAN 标准寻址来说整条请求如果不超过 7 字节就用一个单帧发出。比如02 10 01是 0x10 01 会话切换单帧长度 203 22 F1 90是 0x22 F1 90 读软件版本单帧长度 3请求基本都落在单帧里响应才是真正需要分段的大头。3.3 响应阶段FF FC CF 的握手时序完整的长响应交互流程如下诊断仪Tester ECUServer | | |-------- 单帧请求 (0x22 F1 90) -------| | | |------- 首帧 FF总长 N带 6 字节-| | | |-------- 流控帧 FCBS8, STmin2ms)-| | | |------- CF 序号 17 字节数据-------| |------- CF 序号 27 字节数据-------| |------- CF 序号 37 字节数据-------| |------- ... | | | |-------- 流控帧 FCBS8 ... -------| 如果 BS 限定了块大小每 8 帧一个 FC |------- CF 序号 9 ... ----------------|这个握手过程里ECU 作为响应发送方诊断仪作为接收方。诊断仪收到首帧后必须根据自身接收缓冲区能力回复一个流控帧。流控帧里 BS8表示“你最多连发 8 帧然后停下等我下一个流控帧”。STmin2ms表示“帧与帧之间至少隔 2ms”。为什么这么设缓冲区较小的接收端用较小的 BS 保护自己但代价是多往返几次 FC在低波特率总线上不算什么在高速总线上就是额外开销。3.4 传输完成后的 N_PCI 状态管理与超时计算ISO-TP 没有“结束帧”这种概念接收方通过累计收到的数据长度达到 FF 宣告的总长度就认为报文传输完成。也就是说接收方必须自己数“我已经收到多少字节了”传输完成后清理状态机回到空闲态等待下一条。这个状态管理具体到实现上有几个关键节点发送方发送完最后一个 CF 后进入等待状态等待可能的 FC如果还有后续块接收方判断字节数达到 totalLen 后把组好的数据交给上层 UDS清空重组缓冲区如果中途出错FC 收到 Overflow、CF 序号错乱等接收方停止重组并丢弃发送方靠超时才发现失败超时参数有标准可循。ISO 15765-2 定义了几个关键时间参数生产中直接决定诊断稳定性时间参数描述典型值N_As发送方发送 CAN 帧的最长时间CAN 控制器内部处理一般 1msN_Ar接收方发送流控帧的最长时间1000ms ECU 响应慢时可能更长N_Bs发送方等待流控帧的最长时间1000msN_Cr发送方等待连续帧发送的时间1000ms这四个参数里工程上最容易出问题的就是 N_Bs发送方发出首帧后一直等流控帧如果接收方的应用层卡在别的事情上流控帧迟迟不来发送方等满 1000ms 就判定超时直接把整条报文 abort 掉。4. 工程落地中的关键决策BS、STmin、寻址方式与 CAN FD 的交叉影响4.1 Block Size 与 STmin 怎么搭配一个带计算的实际调优案例BS 和 STmin 是 TP 层传输性能的两个旋钮。BS 决定每批连续发几帧STmin 决定帧间最小间隔。两者不是独立的——BS0 表示“不限制”发送方可以把整条报文一次性发完但 BS0 遇上接收端缓冲区小大概率丢帧。反过来BS 设成 8STmin 设成 10ms总共 585 帧的响应光等间隔就要 5.8 秒用户体验直接劣化。一个合理的起步配置是BS0STmin1ms。在这种配置下接收方靠 CAN 控制器 FIFO 的深度扛短时间的帧洪峰1ms 间隔让应用层有充足时间把帧从 FIFO 搬到重组缓冲区。实测下来500kbps 总线上每帧 8 字节加帧头开销大约 0.26ms1ms 间隔不会造成总线拥塞吞吐能达到每帧 1ms4KB 响应大约 586ms已经可以接受。如果接收方的 DMA 和 FIFO 深度不够比如只有 8 帧深度的控制器建议改用 BS8~16STmin0.5ms 的配置。每 8~16 帧做一次流控确认既能把 FIFO 泄漏风险控制住又不会像 BS0 那样完全放开。记住一个经验公式FIFO 深度 × 每帧传输时间 ≥ BS ×每帧传输时间 应用层处理时间。如果左边小于右边必丢帧。4.2 扩展寻址 vs 标准寻址6 字节和 7 字节的抉择ISO-TP 支持两种 CAN 寻址方式。标准寻址下CAN ID 本身就包含目标逻辑地址数据场从第一个字节就是 PCI扩展寻址下数据场第一个字节放目标地址PCI 从第二个字节开始。这意味着标准寻址SF 可用数据 7 字节FF 可用数据 6 字节CF 可用数据 7 字节扩展寻址SF 可用数据 6 字节FF 可用数据 5 字节CF 可用数据 6 字节大部分 OEM 的量产诊断走的是标准寻址因为 29 位 CAN ID 有足够的位空间容纳源地址和目标地址。但一些历史遗留项目、售后诊断仪上用的是扩展寻址这时每条 TP 消息的有效载荷就要重新测算。最坑的是扩展寻址下 12 位长度上限依然 4095但同样 4095 字节需要更多 CF 帧4095 - 5 40904090 ÷ 6 681.67682 个 CF 帧比标准寻址多了 97 帧。如果总线上还有其他报文这 97 帧会抢占不少总线时间。4.3 CAN FD 对 TP 层的影响64 字节时代还需要分帧吗CAN FD 把数据场扩到 64 字节很多人第一反应是“TP 层可以删了吧”。实际情况没那么简单。CAN FD 的数据场是大了但 ISO 15765-2 的 TP 机制依然有效——UDS 响应动不动就几百上千字节64 字节也不够装。CAN FD 下的 TP 只是把 SF/FF/CF 的可用数据从 7 字节变成了 63 字节帧数少了但分帧、流控、重组的逻辑全都在。真正值得注意的是同一个 TP 连接不能一会儿用 CAN 2.0 帧、一会儿用 CAN FD 帧。CAN FD 报文的帧格式和仲裁机制与经典 CAN 不兼容接收方必须明确知道当前诊断会话用的是哪种帧格式混用会导致整个 TP 状态机全部错乱。实操建议Bootloader 刷写场景如果 ECU 和工具都支持 CAN FD优先走 CAN FD吞吐提升立竿见影。4KB 数据在 CAN FD 下只需要 1 个 FF 65 个 CF每帧 63 字节传输时间从 586ms 缩到 66ms同样 1ms 间隔体验完全不是一个量级。4.4 首帧长度字段 4095 上限的工程应对前面说过 12 位长度的限制这在设计诊断规范时是有实际影响的。假设 OEM 想把 block 定义成 4KBISO 15765-2 的 FF 直接表达不了。量产项目里常见的做法有几种一是把 blockLength 卡在 4095 字节。0x34 请求下载里的 blockLength 参数直接写 4095后续 0x36 每次最多传 4095 字节。虽然“4KB”看起来少了 1 字节但实际上刷写流程按 block 循环多次最后一次只剩零星数据损失可以忽略。二是对超过 4095 的数据拆成多条 TP 消息每条 TP 消息携带部分数据然后由上层 UDS 协议0x36 传输数据拼接。这个做法在 ISO-TP 层面不能解决必须靠 UDS 层面的 block 机制配合。三是在规范层面直接定义“本车型诊断刷写数据块上限为 4095 字节”。这是最省事也最稳妥的方案所有工具链基于这个上限开发不会踩 12 位长度的边界。5. 常见问题与排查技巧实录5.1 超时问题速查N_Bs、N_Cr、N_Ar 分别怎么查TP 层出问题八成能归到超时上。对着现象看参数现象可能原因排查方向发送方发出 FF 后一直等不到 FC直到超时ECU 应用层忙没来得及发 FC查 ECU 诊断任务优先级排查标定/刷写期间是否占满 CPU发送方在等 N_Cr 超时接收方发了 FC 后没有按时收后续 CF查总线负载率负载超过 70% 时 CF 延迟是常态接收方发完 FC 后没等到完整数据中间 CF 丢失接收方 seq 校验失败后丢弃抓总线数据检查是否 CF 序号跳动发送方发了 CF 序号 0xF 后卡住序号回绕处理逻辑写错查代码里是否对“15 1 → 0”做了特判很多新手栽在这一个特别容易踩的坑N_As 超时。N_As 是发送方从启动发送到 CAN 控制器真正发出去的时限。总线负载高时仲裁竞争可能导致一帧等了很久才上总线。ISO 15765-2 给的 N_As 上限是 1000ms但如果整条 TP 消息里某一帧在总线上排队超过 1 秒加上其他超时参数整个诊断会话可能连锁超时。解决思路是提高诊断报文的 CAN 优先级减小 CAN ID而不是只调 TP 层参数。5.2 序列号错乱定位法如何从总线日志中断案抓总线日志是排查 TP 层问题的基本功。拿到一段日志先按 CAN ID 过滤出诊断相关的帧然后按时间排序。一个健康的 4KB 响应流程在 Canvas 工具里看应该是FF → FC → CF 1 → CF 2 → …… 连续不断。如果中间出现 CF 序号跳变比如直接从 CF 5 跳到 CF 8基本可以判定 CF 6、7 没发出去或者发出去被总线错误吞了。这时候分两步查第一步看总线错误帧和错误计数器。CAN 控制器遇到位错误、填充错误会自动重发重发次数超限就报 Bus Off。日志里如果伴随大量 Error Frame那问题在物理层或波特率配置不在 TP 层。第二步看发送方的发送队列。很多嵌入式实现里TP 层的发送函数只是把帧交给 CAN 驱动如果驱动队列满帧会被丢弃而不是阻塞重试。这种丢帧在日志上表现为“协议帧缺失”但总线本身没有错误。处理办法要么加大驱动队列深度要么在 TP 层加“未发送完成不丢弃”的保证逻辑。5.3 STmin 取值引发的“慢到怀疑人生”案例实际项目中遇到过这样的事某供应商的 ECUTP 层 STmin 写死 20msN_Cr 超时 1000ms。读一个 500 字节的 DID光发送阶段就要 70 多个 CF × 20ms 1.4 秒整个诊断响应加上其他开销接近 2 秒。测试人员反馈“读个版本号卡到不行”。排查时先怀疑网络问题后来看总线日志发现 STmin20ms而诊断仪的 FC 请求明明是 STmin2ms。问题出在 ECU 的 TP 层实现它没按诊断仪的 FC 参数执行而是用了自己的固定配置。ISO 15765-2 的规定是接收方通过 FC 的 STmin 告诉发送方“你发帧的最小间隔”但发送方也可以选择“我慢一点发”——ECU 的 TP 层只把 STmin 当成“最小值”自己额外做了限速。这类问题的本质是参数协议没有对齐。量产项目里建议测试部门把“DTDiagnostic Tester请求 STmin、ECU 响应实际 STmin”纳入一致性测试用例很多 OEM 的诊断规范里已经写了这条。5.4 “状态机卡死收到强制型 FC 后不恢复”的复位技巧还有一种少见但非常致命的情况发送方发出 FF 后接收方回了一个 FS2Overflow/Abort的流控帧。按协议发送方收到 FS2 必须中止当前报文的传输回到空闲状态。但如果实现里没有正确处理 FS2 的语义把后续收到的 CF 当成了新报文的首帧来处理状态机就会错乱整个诊断通道后续所有帧都解析不了。解决办法有两种。一种是软件防御TP 层状态机上电初始化为空闲态任何非法状态组合都强制回到空闲态并在诊断仪侧加一个“发送 0x7F 诊断会话控制”尝试重置 ECU 诊断会话。另一种更彻底在 TP 层加看门狗计数器如果连续 N 帧非法比如收到的 PCI 类型和当前状态不匹配直接复位整个 TP 层状态机并上报诊断栈错误。这个坑在项目上出现过某次刷写中断后ECU 的 TP 层状态机停在“等待 FF”状态后续无论诊断仪发什么ECU 都不响应。最后只能下电重启因为 TP 层没有做非法帧恢复机制。从那以后我把“状态机容错设计”写进了协议栈代码评审清单。6. 一个来自一线的配置模板与几个实用建议6.1 可直接抄的 TP 层参数配置模板根据自己的项目实践直接给一套经过验证的起步配置配置项取值适用场景CAN 波特率500 kbps常规诊断寻址方式标准寻址29 bit ID量产车型主流方案BS接收方0ECU 接收缓冲区深且 DMA 可靠时STmin接收方1 ms兼顾稳定性和吞吐BS发送方8~16发送时尊重接收方 FC 参数STmin发送方按接收方 FC 执行不自己额外限速N_Bs 超时1000 ms标准值除非 ECU 响应很慢再放大N_Cr 超时1000 ms标准值总线负载高时建议 2000 msCAN ID 优先级诊断报文 ID 低于所有应用报文确保诊断帧仲裁优先这套配置跑在 500kbps 总线上实测 4KB 响应时间约 600ms满足绝大多数刷写和读数据的性能要求。如果总线负载常年高于 60%建议上 CAN FD 并重新测算。6.2 工具链选择的个人体会软件工具链方面开发阶段用 CANoe 的 TP 层仿真模块可以快速验证协议逻辑但自己写 TP 层协议栈时最好配合总线日志逐帧比对。量产测试阶段建议用专门的诊断测试仪做一致性验证重点覆盖长时间重复读大数据 DID、刷写过程中高频中断恢复、不同 STmin 参数排列组合。示波器和逻辑分析仪在排查物理层问题时很有用但排查 TP 层逻辑问题时重点抓“帧时间戳”——两台设备的时间基准要一致否则无法精确计算 N_Bs 和 N_Cr。经验做法在诊断仪侧记录每个帧的硬件时间戳抓完日志后直接按时间戳计算帧间隔比看工具自带的“相对时间”靠谱得多。6.3 这套机制还能怎么扩展使用ISO-TP 不只用于 UDS 诊断。在雨刮、车窗、座椅控制器等车身域节点之间传配置参数、OTA 场景里刷写 Bootloader、Pre-compile 阶段的标定数据下载都可能需要 TP 层承载大块数据。甚至在做网关路由诊断报文时TP 层的数据流控逻辑也适用于转发过程只是网关要额外处理跨网段的地址映射和速率适配。我的实际体会是ISO 15765-2 的 TP 层是整个 UDS 诊断协议栈里最“底层”但也最容易出“偶发”问题的一层。帧格式是死的但状态机的实现细节、参数选择的平衡、异常情况的容错才是决定一个诊断协议栈稳不稳定的关键。如果刚开始接触这个协议先把四种帧的角色、12 位长度、BS/STmin 的配合逻辑吃透再上手写代码会少走很多弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑