ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CANoe中ISO 15765多帧传输全解析:从首帧到连续帧,逐字节拆解UDS长响应

CANoe中ISO 15765多帧传输全解析:从首帧到连续帧,逐字节拆解UDS长响应 如果你第一次在CANoe的Trace窗口里看到0x7E8这条ID连续蹦出好几帧第一帧是10 14 62 F1 90 4C 53 56第二帧是21 41 41 34 31 4A 30 41第三帧是22 31 32 33 34 35 36 37中间还夹了一条30 00 00 00 00 00 00 00大概率会一头雾水这到底是几条报文为什么要分这么多帧那串21、22开头的又是什么其实这几帧就是ISO 15765定义的传输层Transport ProtocolTP在干活。它解决的是一个非常朴素的矛盾CAN单帧数据场最多8个字节而UDS诊断服务一次要传的数据经常超过8个字节。比如读VIN码0x22服务的响应有20个字节一条CAN帧根本装不下那就得拆。怎么拆、怎么告诉对方“我拆了”、对方怎么回“我知道了你继续发”全是ISO 15765-2定的规矩。这篇文章就是带你把这条规矩彻底看明白。我会用CANoe里实际抓到的报文从帧结构、逐字节拆解、Trace配置到常见的坑手把手捋一遍。适合刚接触UDS诊断、被多帧搞得晕头转向的测试工程师、嵌入式软件工程师也适合那些已经会用CANoe的Diagnostic Console点按钮、但还想知道底层到底发生了什么的人。1. 为什么多帧传输总让人“懵圈”1.1 名字很直观行为却不像想象中那样ISO 15765-2里有四种帧Single Frame单帧、First Frame首帧、Flow Control流控帧、Consecutive Frame连续帧。名字本身都不难懂但一旦落到Trace里很多人就乱了原因在于它们出现的顺序不是按字母排的。一次正常的“长响应”交互顺序是这样的接收方先收到一个首帧FF它相当于一个“开场白”我要发的完整数据一共N个字节这里先给你前6个字节。发送方确切说是请求方也就是诊断仪侧收到FF后回一个流控帧FC相当于说收到你可以继续发或者按我要求的节奏发。接收方随后收到一串连续帧CF每一帧带7个字节数据还有一个从1开始递增的序列号。我在带新人时常用一个快递的类比FF是快递员打电话说“这个包裹一共20公斤我先搬了6公斤到门口”FC是你说“行你继续搬”CF就是后面一趟一趟搬上来的包裹而序列号就是每趟的编号防止中途丢件。1.2 CAN帧和UDS消息不是一回事这是懵圈的根源很多人犯迷糊是因为脑子里把“一条CAN帧”和“一条诊断消息”画了等号。实际上两者之间隔着传输层UDS服务比如0x22读数据、0x2E写数据、0x19读故障码是应用层的东西ISO 15765-2是传输层CAN控制器只负责把一条一条独立的数据帧发到总线上。这三层在CANoe里对应的观察窗口完全不同层级协议在CANoe里怎么看应用层UDS服务Diagnostic Console里看到的“Response”传输层ISO 15765-2 PCITrace里的10/21/30开头字节数据链路层CAN帧Trace里的ID、DLC、时间戳如果你只看Diagnostic ConsoleCANoe会帮你在后台把多帧重组好你看到的是一条完整的UDS响应底层拆了几帧、中间有没有流控全都看不见。而一旦你打开Trace看原始报文传输层的“拆装现场”就暴露出来了没有提前建立这套分层概念的人自然就懵了。所以我的建议是学ISO 15765多帧千万别只用Diagnostic Console一定要开着Trace去对应着看。只有看到“高层的完整响应”和“底层的多帧拆解”是同一件事你才算真正理解了它。2. 四类帧的字节结构把N_PCI背下来后面就顺了2.1 单帧SF和首帧FF长度字段最容易搞混ISO 15765-2里每个CAN数据场的第一个字节或多个字节叫N_PCIProtocol Control Information专门用来标记帧类型和数据长度。经典CAN8字节数据场、普通寻址下四种帧的布局如下帧类型第一个字节第二字节数据载荷SF单帧0x0nn数据长度最大7无最多7字节UDS数据FF首帧0x1m 0xllm和ll组成12位长度见左最多6字节UDS数据CF连续帧0x2ss序列号见左最多7字节UDS数据FC流控帧0x3ff流控状态BSSTmin注意看SF和FF的用法差异这是初学者最容易栽跟头的地方。SF的第一个字节高四位固定是0低四位是这帧里携带的UDS数据长度。比如诊断请求22 F1 90是3个字节那么CAN帧数据就是03 22 F1 9003就是SF的N_PCI表示后面3个字节是有效数据。FF则复杂一些因为长度可能超过154位装不下了。FF用两个字节表示长度第一字节的高四位固定是1低四位是12位数据长度的最高4位第二字节是整个长度的低8位。所以10 14表示的是这是一个FF数据总长度是0x014也就是20个字节。这里有个极其容易搞混的点FF_DL 0x14 20指的是这条UDS完整消息一共20个字节而首帧本身只携带了6个字节。20和8、20和6这些数字在脑子里对不上人就乱了。记住一句话FF_DL是“总账”不是“当前帧账”。2.2 连续帧CF和流控帧FC序列号与红绿灯CF的N_PCI是一个字节高四位固定是2低四位是序列号SN。协议规定首帧之后的第一条CFSN必须是1之后每帧加1到15之后回绕到0。为什么不是0开头因为FF在逻辑上已经隐含了一个“第0帧”的位置CF从1开始接续。FC的N_PCI也是关键。第一个字节高四位固定是3低四位是流控状态FSFS值含义说明0 (0x30)CTS——继续发送接收方准备好了发吧1 (0x31)WAIT——等待接收方忙于其他事稍后再给我FC2 (0x32)OVFLW——溢出/中止接收方缓冲不够这次传输作废FC的第二个字节是BSBlock Size表示允许连续发多少条CF后才需要再等下一个FC。BS0是一个特殊值表示“不限制block大小剩下的CF你一口气发完”。第三个字节是STminSeparation Time minimum表示两条CF之间最小间隔时间0x00-0x7F表示0到127毫秒0xF1-0xF9表示100到900微秒。把这三字节连起来看30 00 00的意思就是继续发不限制块大小也不要求间隔。2.3 寻址方式不同能装的数据量也不同上面说的7字节、6字节、7字节都有一个前提用的是普通寻址Normal Addressing也就是CAN ID本身已经区分了请求和响应不需要在数据场里再加目标地址字节。但还有一种常见方式叫扩展寻址Extended Addressing数据场第一个字节固定放目标/源地址字节比如物理寻址时放ECU地址这个字节不算在UDS消息里所以留给PCI和数据的位置就少了一个帧类型普通寻址可用数据扩展寻址可用数据SF7字节6字节FF6字节5字节CF7字节6字节很多OEM的诊断规范会明确规定用哪种寻址方式。如果配置错了比如接收方按扩展寻址解析发送方却按普通寻址发那么所有字段都会错位一位看起来就是“每个字节都对不上号”。一旦在工程里遇到这种情况先别急着怀疑协议栈把CANoe Trace里的第一字节拆出来看确认是不是地址字节把PCI顶到后面去了。还有一种29位CAN ID的情况ID里包含了源地址和目标地址数据场里通常不加地址字节装载能力和普通寻址一样。具体用哪种以协议规范为准。3. CANoe实战拆解一次完整的读VIN多帧交互3.1 动手前的环境和配置为了把原理落到实际我在CANoe里搭了一个最简单的诊断测试环境一个Vector硬件接口接驳到ECU所在的总线上或者直接用CANoe的虚拟CAN口做纯软件仿真总线波特率500 kbit/s物理寻址请求ID是0x7E0响应ID是0x7E8。操作上分几步打开CANoe配置一个CAN通道波特率选500 kbit/s采样点按OEM规范设置。在“Simulation Setup”里添加一个网络节点有现成的诊断描述CDD/ODX就直接加载没有的话用IGInteractive Generator或CAPL节点手动发请求。打开Trace窗口先添加“Protocol”列和“DLC”列这样后面解码TP帧时能直接看到帧类型。如果你只是想看数据内容不想让CANoe自动做TP高亮直接用Raw模式看也可以因为这篇文章就是要逐字节自己拆。我用一个CAPL节点发了一条22 F1 90的UDS请求。22是ReadDataByIdentifier服务IDF1 90是DID这里对应的是VIN码。3.2 请求阶段为什么3个字节就敢用单帧请求只有3个字节加上1个字节的SF N_PCI一个CAN帧完全装得下所以Trace里看到的就是一条单帧序号时间方向IDDLCData100:01.234Tx0x7E0803 22 F1 90 00 00 00 00这里03是SF的PCI表示这条帧携带的UDS数据是3个字节22 F1 90。后面的00都是填充字节不是有效数据。有些ECU喜欢填AA或CC填充值不是协议规定死的所以解析时一定要以PCI里的长度为准不要看到DLC8就全读进去。3.3 响应阶段FF/FC/CF逐字节分析ECU收到请求后返回的VIN有20个字节这是一个典型的多帧响应。CANoe Trace里抓到的完整过程如下序号时间方向IDDLCData帧类型200:01.256Rx0x7E8810 14 62 F1 90 4C 53 56FF300:01.258Tx0x7E0830 00 00 00 00 00 00 00FC400:01.260Rx0x7E8821 41 41 34 31 4A 30 41CF 1500:01.262Rx0x7E8822 31 32 33 34 35 36 37CF 2第二帧10 14高四位1低四位0第二字节14拼起来是12位长度0x014 20。后面6个字节是UDS响应数据的前6字节62 F1 90 4C 53 56。注意这帧的DLC是8但有效数据只有6个字节因为它要留2字节给PCI。有些ECU会在这里做填充有的不填DLC照常拉满8解析时必须跳过前2字节。第三帧30 00 0030表示CTS继续发送。00表示BS0不限制连续帧数量。00表示STmin0不强制帧间隔。这帧本身只有3个字节是协议有效的后面的00全是填充。第四帧21开头是第一条CFSN1后面7字节是第7到第13个UDS数据字节41 41 34 31 4A 30 41。第五帧22开头是第二条CFSN2后面7字节是最后7个数据字节31 32 33 34 35 36 37。把三部分拼起来首帧的6字节62 F1 90 4C 53 56加上CF1的7字节41 41 34 31 4A 30 41再加上CF2的7字节31 32 33 34 35 36 37得到完整的响应62 F1 90 4C 53 56 41 41 34 31 4A 30 41 31 32 33 34 35 36 37这是20个字节和FF_DL里声明的20完全一致。拆开看62是0x22服务的正响应SIDF1 90是DID后面17个字节就是ASCII码的VIN字符串“LSVAA41J0A1234567”。校验一下长度17个字符正好。3.4 时间戳里藏着的协议参数把上面几张表的时间列连起来看你会发现FF和CF之间的时间差只有2毫秒上下这是因为FC里STmin0。如果ECU在FC里写了1016ms那么你会在Trace里看到CF帧之间至少间隔16ms如果写了3250ms那间隔会更明显。这种时间上的观察对排查“为什么我的分帧传输老是超时”很有帮助。我曾经遇到过一个项目ECU在FC里要求STmin0xF1100微秒但是诊断仪端的协议栈因为内部任务调度实际发CF间隔只有50微秒结果ECU那边缓冲区溢出直接回了一条32把传输掐断。这种问题光看数据内容根本找不到必须盯时间戳。另外协议里还有几个时间参数也值得了解N_Bs是发完FF后等待FC的超时时间N_Cr是两条CF之间允许的最大间隔。CANoe的TP统计信息里能看到这些超时是否发生过后面排查超时NRC时会很有用。4. 让CANoe帮你把TP层“翻译”成人话4.1 Trace窗口的协议解码配置在CANoe默认情况下如果你在Trace里看到的是原始数据很可能是因为工程没有加载诊断描述或者通道没有启用ISO TP解码。多数场景下只需要做两件事确认通道的CAN设置里加载了包含ISO 15765传输层属性的数据库.dbc或CDD/ODX或者添加了诊断通道。Trace窗口的列头右键把“Protocol”列显示出来。如果配置正确这一列会显示ISO-TP或者Diag相关的协议名FF、CF、FC也会被识别。实际测试中我会同时打开三个窗口Trace看原始帧、Diagnostic Console看UDS层、CAN Statistics看总线整体负载。尤其是在做压力测试时如果总线上有大量其他报文CF帧可能会被高优先级报文延迟导致N_Cr超时这时CAN Statistics窗口里的总线负载率就是第一手线索。4.2 用Diagnostic Console和HexView配合验证Diagnostic Console的价值在于它把TP层的拆装完全封装了你选中“ReadDataByIdentifier”填上DID它会自动帮你发请求、收响应然后把重组的完整响应直接显示出来。这对日常测试非常高效但也很容易让人忽略底层逻辑。所以我建议的做法是先用Diagnostic Console确认“功能上是通的”再用Trace去验证“底层是怎么通的”。比如Console里看到响应是62 F1 90加VIN你再去Trace里数一数应该是1个FF加2个CFCF序列号是1和2。两边对得上说明协议栈工作正常对不上才是你需要深入源码或者CAPL脚本的时候。HexView这个工具主要是用来查看和编辑二进制文件的在诊断领域经常用来做Flash数据、标定数据的比对。它不直接参与TP解析但如果你在做Bootloader刷写测试常常需要把待刷写的.s19或.hex文件用HexView打开对照诊断仪实际发送的分帧内容确认数据分块、校验和逻辑是否符合预期。4.3 用CAPL写一个多帧自动拼装小工具有的测试场景需要在CANoe里自动验证TP组包是否正确比如做自动化回归测试。这时候写一个几十行的CAPL脚本比人眼盯着Trace靠谱得多。下面是一个简化版的示例监听0x7E8上的响应把FF和CF自动拼成完整数据并在完成后输出variables { byte rxData[5000]; int rxLen 0; int totalLen 0; } on message 0x7E8 { int i; int pciType; pciType this.byte(0) 4; if (pciType 1) // First Frame { totalLen ((this.byte(0) 0x0F) 8) | this.byte(1); rxLen 0; for (i 2; i 8; i) rxData[rxLen] this.byte(i); } else if (pciType 2) // Consecutive Frame { for (i 1; i 8; i) rxData[rxLen] this.byte(i); if (rxLen totalLen) { write(完整响应, 长度%d, totalLen); for (i 0; i totalLen; i) write( %02X, rxData[i]); rxLen 0; totalLen 0; } } }这段脚本只处理了普通寻址、经典CAN的情况没有判断FC也没有处理扩展寻址和地址字节但在多数读VIN、读数据块的测试场景里足够验证组包逻辑了。实际工程中建议把输出拼成一个字符串写到Write窗口或者面板文本控件里这样清洗得多。5. 多帧传输的翻车现场这些坑我基本都踩过5.1 CF序号走到F之后绕回0代码里最容易漏前面说过CF的SN是4位从1走到15之后下一帧会变0。很多人写接收组包逻辑时会潜意识认为SN应该一直是1、2、3……顺序递增一旦看到2F后面跟着20第一反应是“协议错了吧”。举个例子一条UDS响应如果总长度是130字节FF先带6字节剩下124字节每帧CF带7字节一共需要18条CF。SN会是这样一段序列1、2、3……14、15、0、1、2、3。也就是说第16帧的SN是0而不是16。如果你的判断代码写的是“收到SN0就认为非法”那这条长响应就永远组不完整。正确的做法是按模16来比较哪怕SN回绕了也能判断连续性。CANoe的TP层已经处理了这个问题但如果是自写协议栈或者写CAPL辅助脚本十有八九会在这里翻车。5.2 FC里的BS含义很多人一开始理解反了我第一次带项目时看到FC的BS1以为是“完整传输完成”后来才发现完全不是这回事。BS1的意思是发送方最多只能发1条CF然后必须停下等接收方再发一个FC才能继续发下一条。这相当于每传一帧都要握一次手效率很低但接收方缓冲区很小或处理速度慢时这是必要的保护。理解BS后再看刷写场景就清楚为什么有些Bootloader的刷写速度上不去了——要么BS1导致频繁握手要么FC里STmin设得很大。通常刷写阶段ECU会要求一个较小的BS和STmin比如BS1、STmin1ms以保证Flash写入不溢出而普通的诊断读数据ECU一般给BS0、STmin0尽量跑满带宽。如果发送方无视BS约束一口气把几十条CF全发出去接收方的接收缓冲很快就会爆随后一条32OVFLW直接让这次的传输中止。在Trace里看到这种情况先检查FC的第二个字节再检查发送方有没有按约定的块大小执行。5.3 STmin被忽略低速端根本来不及处理STmin看似只是一个毫秒数但它直接决定接收端“能不能喘过气”。高速诊断场景下STmin0很正常总线速率500k时理论上两条背靠背的CF之间只有几十微秒间隔接收端的CPU如果只是简单搬运可能处理不过来。在测试中我发现很多ECU在FC里写的STmin并不是0比如0x1016ms、0x3250ms这些数值通常和ECU的接收任务周期有关。如果诊断仪侧为了追求速度强行忽略STmin后续大概率会出现CF丢失或接收超时最终表现为诊断无响应或响应异常。排查思路也简单在Trace里量两条CF的时间差如果ECU要求STmin50ms但实际发CF的间隔只有1ms那问题一定在发送方。反之如果发送方已经按50ms发了ECU还是超时那就要怀疑ECU内部的软件问题了。5.4 环境相关的坑虚拟CAN口、采样点、软件版本关注CANoe的人应该都搜过“虚拟CAN口”“采样点”“运行后自动退出”这类问题。这几个确实都和实际测试环境强相关。虚拟CAN口CANoe Virtual Bus非常适合做纯软件的协议学习没有真实电路不会有错误帧采样点设置也不会影响数据接收你可以放心的在里面模拟ECU和诊断仪交互。但虚拟CAN口有个副作用——过于理想。真实总线上有报文仲裁、位定时偏差、线束干扰有些TP问题在虚拟环境下根本复现不出来。所以我一般建议学协议用虚拟CAN验证可靠性一定要上真实硬件和真实总线。采样点的问题常见于高波特率总线。比如500 kbit/s下如果采样点设置得太后比如90%线缆稍长一点就可能采样到边沿附近导致数据错误进而产生错误帧。很多人在CANoe里看到Error Frame就怀疑ECU坏了其实先看一下通道配置里的采样点是否在80%左右往往能省下半天排查时间。至于CANoe 17 SP3运行后自动退出的情况多数和授权服务、驱动版本或者.NET组件有关。真遇到的话别急着怀疑工程文件先试管理员权限运行、检查Vector工具链是否完整再去事件查看器里看崩溃模块基本都能定位。6. 一点自己的排查心法先看PCI再谈诊断6.1 拿到一条长报文我从来不会先读数据不管是自己做测试还是帮同事看问题我拿到一帧带10或21开头的报文第一件事永远是看PCI不是看后面的业务数据。因为业务数据再花哨如果PCI不对后面全是废的。具体我会按这个顺序撸一遍看第一字节高四位是1FF、2CF还是3FC。FF的话立刻算一下12位总长度心里大概有数后面会来几条CF。CF的话看低四位SN确认序列号连续特别是要留意F之后有没有正确回到0。FC的话重点看BS和STmin判断发送方后面应该用什么节奏发。最后才拼数据再去UDS层做语义解析。这个顺序看起来机械但效率极高。因为PCI就是传输层的“骨架”只要骨架是对的数据就算解析错了也只是业务层的问题骨架错了一切免谈。6.2 测试报告里真正有价值的永远是原始Trace最后分享一个习惯每次出一个诊断相关的测试结论我都会把完整的原始Trace一起附上而不是只贴Diagnostic Console的截图。因为Console截图是“加工过的结果”丢了传输层的所有过程信息而原始Trace里包含了时间戳、ID、DLC、每个字节甚至总线错误帧这些才是跨部门沟通时最有说服力的东西。对正在学CANoe和ISO 15765的朋友我的建议很朴素不要满足于会点几个按钮、会看Console显示“Response”。花一个下午把读VIN、读DID这些场景在Trace里逐帧扒一遍自己动手算一次FF_DL、CF数量、序列号回绕这个协议你就真的掌握了。以后再看到10 14开头的帧不会再懵而是会心一笑哦这是个20字节的长响应啊。
返回列表