ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

CanTp协议深度解析:AUTOSAR诊断与刷写的数据传输基石

CanTp协议深度解析:AUTOSAR诊断与刷写的数据传输基石 1. 为什么需要CanTp8字节CAN帧塞不下诊断数据做车载软件开发的朋友应该都有这种体会CAN总线上一个标准数据帧撑死也就8字节可日常开发里我们动不动就要传几十上百字节的数据。诊断仪读故障码、ECU刷写固件、标定参数下载哪个不是大块头如果直接让应用层把数据往CAN控制器里塞那纯属妄想——硬件层面就把你卡死了。这就得请出AUTOSAR协议栈里的传输层模块CanTpCAN Transport Protocol。它干的事情说白了就是拆装快递发送端把超过8字节的大报文拆成一帧一帧的小CAN帧发出去接收端再把收到的碎片重新拼装成完整报文交给上层。整个过程对上层应用完全透明你只管把一块数据扔给PDURProtocol Data Unit Router协议数据单元路由器至于它怎么拆、怎么发、怎么保证不丢不乱全部由CanTp在底层搞定。按照ISO 15765-2标准CanTp支持的报文长度理论上可以达到4095字节。当然实际工程里没人真往上限堆但就算传个几百字节的UDSUnified Diagnostic Services统一诊断服务响应没有CanTp也寸步难行。举一个最常见的场景诊断仪发一个读取VIN码的请求ECU要返回17字节的车架号。17个字节标准CAN帧最多8个普通扩展帧也才8个数据字节怎么办没有CanTp就得跑两三次请求-响应协议复杂不说还容易出错。有了CanTp上层直接返回17字节模块自动拆成3帧发出去诊断仪那边再自动拼出来应用层无感。所以说CanTp是整个AUTOSAR通信栈里承上启下的关键一环。它上面连着PDUR和COMCommunication模块下面钩着CanIfCAN Interface模块本身不直接操作CAN控制器寄存器而是通过CanIf的API把数据发到CAN总线。搞懂了CanTp你才算真正弄明白整车诊断和刷写链路的前半程。2. 帧类型与时间参数传输层协议的核心骨架CanTp定义了四种帧类型分别是单帧Single FrameSF、首帧First FrameFF、连续帧Consecutive FrameCF和流控帧Flow ControlFC。它们之间怎么配合下面拆开细说。2.1 单帧SF8字节以内的救命稻草如果上层报文总长度不超过7个字节标准寻址下CanTp就发单帧。单帧的PCI字节Protocol Control Information协议控制信息结构是高4位帧类型单帧为0000低4位数据长度如果数据长度大于7需要扩展但那是CAN FD时代的话题经典CAN里7字节封顶举个例子一个4字节的报文PCI就是0x04后面跟4个数据字节。这里有个新手常犯的错直接把报文长度当作PCI的值发出去结果明明7字节的数据写了0x07但PCI低4位最大表示7如果数据正好7字节没问题可实际代码里很多人会把长度减1再填进去导致接收端解析出的长度少1字节最后拼出来的数据多一个尾巴。千万记住SF的长度域填的是实际数据长度不是长度减1。2.2 首帧FF大块头发送的开场白当数据长度超过7字节发首帧。首帧的PCI结构是高4位0001表示首帧低4位第二字节组成12位长度值最大4095字节首帧数据区只有6个字节可用因为第二字节被长度占用了这6个字节就是整个长报文的前6字节。接收方收到首帧后就知道来了一个大报文总共多长然后回复流控帧告诉发送方你可以发连续帧了一次发多少块间隔多长。2.3 连续帧CF后续数据的搬运工首帧之后剩余数据全部用连续帧发送。连续帧的PCI高4位是0010低4位是序号Sequence NumberSN。序号从1开始注意不是0最大15到15后回绕到0继续。这里有个非常容易踩的坑很多人在代码里判断连续帧序号时直接用if (sn expected_sn)这种逻辑但忘了序号回绕的问题。15之后回0如果数据很大序号是会循环使用的必须在代码里考虑回绕场景。我见过一个项目刷写大数据时偶发数据错乱查了三天最后发现就是序号回绕的边角没处理好。2.4 流控帧FC发送节奏的闸门流控帧由接收方发出作用是控制发送方节奏。它的PCI高4位是0011后面跟着三个参数FSFlow Status0表示继续发送1表示等待Wait2表示溢出/中止OverflowBSBlock Size允许连续发送的连续帧数量发满BS个后必须等待下一个流控帧STminSeparation Time minimum相邻连续帧之间的最小时间间隔这里有个实操细节FS1的等待流控帧Wait FC一般只在接收方忙于处理数据时临时使用。它的语义是暂停但别放弃我还在发送方收到后会继续等待下一个流控帧。但如果等待超时N_Br超时发送方就得报错中止传输。2.5 时间参数决定传输可靠性的隐形规则CanTp协议里定义了N_As、N_Ar、N_Bs、N_Br、N_Cs、N_Cr六类时间参数。我不打算把标准的表格原样抄一遍只讲几个直接影响调试的参数N_As发送方发送SF/FF/CF的超时发送方从开始发送到CAN控制器确认发送成功的最大时间。这个参数跟底层驱动和总线负载相关配置太短容易误报太长则诊断响应变慢。N_Bs发送方等待FC的超时发送方发出FF后必须在N_Bs时间内收到FC否则中止。实测中如果接收方处理慢N_Bs设太短会频繁报流控帧超时。N_Cs发送方发送CF后等待下一个发送请求的超时这一项在标准里定义为发送CF后的最大时间主要用于检测发送方卡死。N_Ar接收方等待FF/CF的超时接收方收到FF后必须继续收到CF第二次接收超时就报错。这里有个细节第一个N_Ar超时是可以容忍的发送方可能还没准备好连续两次超时才算真正失败。很多初学者不知道这个容错一次的规则。N_Cr接收方等待连续帧的超时跟N_Ar类似但作用于后续CF的接收。收到CF时刷新定时器。N_Br接收方发送FC到收到下一个FF/CF的超时对发送方而言发完FC后必须在N_Br内收到响应超时则传输中止。这些时间参数全部通过ECUCECU Configuration配置在经典的配置工具里对应的容器是CanTpGeneral和CanTpChannel。配置原则就一条给底层总线负载留出余量别把超时设得太极限。我自己一般会把N_As设为100ms标准默认值是1000ms但实测100ms足够N_Bs设为1000msN_Cs设为1000msN_Ar设为1000msN_Cr设为1000msN_Br设为1000ms。这个组合在多数项目里跑得很稳。2.6 流控参数BS和STmin发送节奏的核心调节旋钮BSBlock Size表示接收方允许发送方连续发多少个CF才需要等待下一个FC。这个参数很实用如果收方处理缓冲区只有64字节而报文总共200字节你可以在第一个FC里把BS设为88个CF×7字节56字节收满56字节后再发下一个FC继续收。BS0表示不限制发送方可以一口气发完所有CF。STmin的取值有张标准表我贴个常用值STmin值含义0x00无间隔限制尽快发送0x01-0x7F每帧间隔1ms-127ms值即毫秒数0xF0-0xF9间隔100us-900us0xF0100us每加1加100us0xFA-0xFE保留不使用工程里我几乎不用0x00哪怕内部模块之间通信也不用因为0x00会让发送方以最大速率发包容易把总线带宽打满影响其他报文。UDS诊断常用的STmin配置是0x0A10ms或0x1420ms刷写场景有时会用0x011ms配合BS0来提速。讲真刷写速度跟STmin关系非常大STmin设小了刷写快但总线负载飙高可能丢帧。3. 地址模式与帧格式差异普通寻址和扩展寻址的取舍做CanTp配置时最容易被忽略的是寻址模式。ISO 15765-2定义了两种网络层寻址普通寻址Normal Addressing和扩展寻址Extended Addressing。它们直接影响PCI布局和数据可用字节数。3.1 普通寻址简洁高效但帧的数据空间少了普通寻址下CAN ID本身就携带了源地址和目标地址信息。SF帧格式为PCI1字节数据最多7字节。FF帧格式为PCI2字节数据最多6字节。CF帧格式为PCI1字节数据最多7字节。这种模式的优点是解析简单PCI后面直接就是数据代码逻辑清晰性能和内存开销都小。缺点嘛就是CAN ID被寻址功能占用后可用的ID空间受限而且如果同一总线上多个ECU需要同时传输大报文ID分配会比较紧张。3.2 扩展寻址灵活但每帧少一个字节扩展寻址在PCI之前加了一个N_AINetwork Address Information字节相当于每帧都多带一个子地址。这样多个上层协议实例可以共享同一个CAN ID靠N_AI区分。扩展寻址下SF帧格式变为N_AI1字节PCI1字节数据最多6字节。FF帧格式变为N_AI1字节PCI2字节数据最多5字节。CF帧格式变为N_AI1字节PCI1字节数据最多6字节。代价就是每帧少1字节数据空间一次传输要多拆几帧速度略慢。工程上什么时候用扩展寻址典型场景是同一个ECU里多个诊断通道共用一条物理CAN总线比如同时支持UDS和J1939诊断或者做网关路由时需要在同一个CAN ID上叠加多个传输实例。这种情况下扩展寻址几乎是唯一解。3.3 混合寻址Mixed Addressing还有一种混合模式SF和CF用普通寻址FF用带地址的格式但实际项目里用得很少AUTOSAR也并未把它作为默认推荐配置。初学阶段不用管它碰到再说。从配置工具的角度寻址模式是在CanTpChannel容器里通过CanTpAddressingFormat属性设置的可选值支持普通寻址、扩展寻址等多种组合。注意如果用的是普通寻址你在CanTpRxNSdu里配置的CanTpRxNSduNarCoEN_AI那类参数就完全没用了别白配。4. 基于达芬奇配置器的CanTp配置全流程与实战避坑AUTOSAR配置工具五花八门但国内用得最多的还是Vector的DaVinci Configurator Pro俗称达芬奇。我基于工程经验把CanTp配置的关键步骤走一遍同时标注几个特别容易翻车的点位。4.1 第一步创建CanTp模块并关联底层在达芬奇里新建一个ECU配置后先从模块列表里添加CanTp。添加后系统会自动生成一个CanTp根容器里面有CanTpGeneral和CanTpChannel两类主要子容器。CanTpGeneral里必须配的项包括CanTpMainFunctionPeriod主函数周期经典CAN一般配10ms如果总线负载高或报文大可以配5ms。这个值直接影响N_As和连续帧发送的实时性。配太大会导致发送延迟累积配小了CPU开销上去。CanTpRxPadding接收数据padding模式一般配置为CanTpPadding或CanTpRxNoPadding。CanTpTxPadding类似发送padding模式。CanTpCtrlFramePayloadSize控制帧FF/FC的有效载荷尺寸。标准CAN就是8因为数据区8字节CAN FD可以配到64。CanTpChannel里的关键项更多。每个通道对应一个物理CAN总线通道下面要配置发送和接收的NSDUNetwork Layer SDU。4.2 第二步配置收发NSDU这一步是最容易出错的地方。以接收方向为例你要配置CanTpRxNSdu其中核心参数包括CanTpRxNSduId上层通常是PDUR用来索引这个接收通道的ID。CanTpRxNSduNarCoE如果是扩展寻址这里填N_AI值。CanTpRxNSduCanId监听的CAN ID。这里要特别注意配置的是带地址的CAN ID如果底层CanIf对CAN ID做过偏移或掩码配置对不上就会一直收不到数据。CanTpRxNSduCdrexPdu这个不多说跟着生成代码走。CanTpRxNSduHrs是否需要硬件接收硬件接收是CanIf层面的优化CanTp只管逻辑。发送方向的CanTpTxNSdu类似但要额外配置CanTpTxNSduBorrowing是否允许借用接收路径的buffer这一项在资源紧张时很有用但会让代码逻辑变复杂没把握的项目别开。4.3 第三步配置Buffer与最大报文长度CanTpChannel下面还有CanTpBuffer相关的配置主要是CanTpRxBufferSize和CanTpTxBufferSize。这两个值直接决定模块能处理的最大报文长度。这里有个坑很多人以为把Buffer配成4095就是最大能力但Buffer大小还受CanTpRxNsdu里CanTpRxNsduLength的限制。两者必须同时满足否则实际传输超过较小值就会报长度错误。我在一个项目里就吃过亏Buffer配了512Length配了256结果发300字节的诊断响应直接被吞了后面查配置才发现俩不一致。实际工程里我的习惯是Buffer按通道最大可能报文长度配比如刷写场景配1024或2048诊断场景配256就够。Buffer开得太大浪费RAM太小又不够用所以一定要基于实际报文长度去算。4.4 第四步时序和流控参数配置回到CanTpChannel里的时序参数前面提到的N_As、N_Bs、N_Cs、N_Ar、N_Cr、N_Br以及BS和STmin都在这个容器里配。我用一个工程常用配置做示例参数诊断标准刷写场景推荐说明N_As100ms50ms发送确认超时刷写可以激进一些N_Bs1000ms1000ms等待FC超时别设太短N_Cs1000ms1000ms发送CF超时N_Ar1000ms1000ms接收FF/CF超时N_Cr1000ms1000ms连续帧接收超时N_Br1000ms1000ms发FC后等待响应超时BS00无块限制刷写提速STmin10ms1ms刷写时极速传输但总线负载飙升4.5 生成代码后的三次手动检查代码生成不是终点。我每次生成完代码会做三件不那么自动的事第一检查CanTp_MainFunction是否在调度表里被周期调用。有时候生成的OsTask配置和调度表不匹配导致主函数根本没跑现象就是诊断超时。别问我怎么知道的。第二检查CanIf和CanTp之间的接口是否配对。CanTp_TxConfirmation、CanTp_RxIndication这些接口的注册必须在CanIf的配置里存在否则底层收发事件传不上来。第三检查CanTp_Init的初始化顺序。AUTOSAR模块初始化有先后依赖CanTp必须在CanIf之后、PDUR之后初始化但必须在COM等上层模块之前。有些项目用EcuM的初始化序列控制没排对顺序会出现模块初始化成功但数据不通的诡异问题。5. CanTp与上下层模块的交互从PDUR到CanIf的数据链路很多教程讲CanTp都是孤立地讲但我总觉得不把它放到整个通信栈里看你根本理解不了为什么有些配置非得这么配。这一节专门讲数据链路。5.1 发送方向的完整路径上层比如UDS服务或COM模块要发一个30字节的诊断响应数据会这样走第一步上层模块调用PDUR的API比如PduR_CanTpTransmit或PduR_ComTransmit。PDUR根据配置的路由表找到对应的CanTp通道和NSDU ID然后调用CanTp_Transmit。第二步CanTp_Transmit收到数据后做三件事检查长度是否超限、把数据复制到自己的发送Buffer、把发送状态设为需要发送。如果是小于等于7字节的小报文直接构造SF帧调用底层API发送如果超过7字节构造FF帧发出去然后进入等待FC状态。这里值得注意CanTp的CanTp_Transmit其实只是个登记动作真正发送动作是在CanTp_MainFunction里触发的。第三步CanIf发送。CanTp通过CanIf_Transmit把数据交给CanIfCanIf再调用Can驱动把帧送上总线。发送完成后Can驱动回调CanIf调用CanTp_TxConfirmation通知CanTp这帧发完了。第四步CanTp收到TxConfirmation后对SF来说一次传输就完成了可以向上报发送完成对FF来说则进入下一阶段——发送连续的CF每发完一帧接着发下一帧直到全部发完。5.2 接收方向的完整路径接收方向反过来。CAN驱动收到帧后上报CanIfCanIf根据CAN ID找到对应的接收通道调用CanTp_RxIndication。CanTp收到一帧后先检查PCI判断帧类型如果是SF且长度没超限直接校验后复制到接收Buffer然后调用PduR_CanTpRxIndication通知上层完整报文已到。如果是FF记录总长度、保存前6字节数据构造FC帧发回去配置了BS和STmin都会体现在这帧里然后进入等待CF状态。注意这个FC不是立即发的也是在CanTp_MainFunction里被调度发出的。如果是CF检查序号数据追加到接收Buffer继续等待下一帧。直到收到的数据长度达到FF宣告的总长度整包数据齐了调用上层接收回调然后重置状态机。5.3 交互中容易踩的三个坑坑一PDUR路由没配好。CanTp跟上层通信全靠PDUR路由表如果路由目标没配对数据会莫名丢在PDUR层。排查方法很笨但有效打开PDUR配置逐个检查PduRRoutingTable里源和目标NSDU的映射关系。坑二CanIf的HOHHardware Object Handle配置冲突。如果同一个CAN ID被多个HOH同时监听接收时可能出现数据被错误分发的情况。CanTp配置的CAN ID应该只在CanTp的接收通道里出现一次别让COM或者其他模块占用同一个ID。坑三主函数周期与帧间隔不匹配。CanTp_MainFunction的调度周期决定了连续帧发送的最大速率。假设STmin配成1ms但主函数周期是10ms那实际帧间隔至少10msSTmin再小也没用。很多开发人员把STmin调得很小却发现速度上不去就是这个原因。5.4 CanTp与网络管理、J1939的关系热词里出现了J1939和网络管理。简单说两句AUTOSAR网络管理CanNm跟CanTp不是一回事网络管理报文走Com模块跟CanTp没有直接关系但在网关场景网络管理报文和诊断报文共用总线带宽STmin设太小会挤压网络管理报文的发送时间导致网络管理状态机抖动别忽视这个间接影响。J1939的传输协议TP虽然也是拆包-重组的思路但它是独立于AUTOSAR CanTp的另一套协议基于SAE J1939-21帧格式、寻址方式都不一样。AUTOSAR里如果你想跑J1939 TP得配J1939Tp模块而不是CanTp。它们俩在同一个总线上的共存一般没问题因为CAN ID分配不同但记得在CanIf层把ID隔离好。6. 实际调试中那些让你挠头的CanTp问题讲了这么多理论和配置最后上一盘实战经验。我把这些年调试中碰到的高频问题整理成一张对照表附上排查思路希望能帮大家少走弯路。现象大概率原因排查方法诊断仪发出请求后无任何响应PDUR路由未配置或CanTp没初始化检查PduR和CanTp配置看主函数是否被调用SF帧能收到大报文FF/CF收不全接收Buffer长度不足或序号比较逻辑有误检查Buffer配置和序号判断代码偶发超时但总线负载并不高STmin设置过小导致发送拥塞调大STmin或者检查主函数周期收到FF后一直等不到CFN_Bs超时发送方没收到FC抓取FC帧确认FC发出时机和STmin连续帧序号错乱序号回绕处理不当检查代码里是否用mod运算处理回绕数据能发出去但上层收不到CanTp_TxConfirmation没回调上来检查CanIf的TxConfirmation注册刷写时偶发数据校验失败BS/STmin配置导致帧重叠或丢帧降低STmin到更大值增大Buffer6.1 案例复盘一次刷写失败三天的排查去年底一个项目客户那边刷写固件时偶发失败概率大概5%左右。现场复现不太稳定抓CAN报文看数据传输中断在某个CF字节上——接收方一直没收到某帧CF。一开始怀疑CAN控制器底层丢帧把波特率、总线负载查了个遍没问题。后来回看配置发现一个问题CanTp的STmin配的是0x00无间隔限制主函数周期10ms。按理说主函数周期10ms限制了最多每10ms发一帧CF不会真的无间隔但问题就出在CanTp主函数里连续发送处理当上一个CF发送完成后主函数在同一个周期内连续构造并发送了多个CF中间没有任何延迟因为代码逻辑在初次发送时不会检查STmin只有等待状态才检查。结果就是偶发情况下一个调度周期里连发了几帧CF在整条链路的接收侧处理不过来丢帧了。修复办法把STmin改成0x055ms同时在发送CF的代码里强制检查距上一帧发送时间是否满足STmin不满足就推迟到下一周期。改完复测三天刷写零失败。6.2 案例复盘扩展寻址下N_AI不匹配导致的静默丢包另一个项目两个ECU通过扩展寻址通信配置时N_AI一个填0x01一个填0x02抓CAN报文看帧全在总线上但接收方就是不解包。后来发现发送方用的是普通寻址建了报文接收方却按扩展寻址解析两边PCI错位数据全乱。这类问题用调试工具看报文时特别迷惑——CAN帧内容明明一样怎么解析不出来实际上就是双方配置不一致。所以做联调时第一步永远是对配置表别急于抓数据。6.3 调试工具的选择建议CanTp调试有个尴尬点逻辑分析仪或CANoe虽然能看到帧但帧内容里PCI、SN这些字段是否匹配还是得靠解析脚本或手动计算。我的日常做法是双轨并行用CANoe或PCAN抓总线报文重点看帧类型是否按预期交替出现FF→FC→CF→CF…。在代码里开临时日志把CanTp的收发状态机切变打印出来对照CAN报文一起看。两个人配合效率最高一个盯总线报文一个盯代码日志交叉验证。单人调试容易陷入报文看起来正常但代码就是不收的困境。6.4 关于Padding的一个冷门但实用的点CanTp标准里有个Padding的概念发送CF时最后一帧的数据如果不满7字节普通寻址后面的空闲字节要不要填充有些ECU会填0xFF有些填0x00有些随机。接收方如果严格要求数据长度必须精确匹配Padding的行为会影响CRC校验之类的结果。因此在刷写场景里固件数据的最后一段如果长度不是7的倍数最后一帧的Payload尾部怎么处理发送方和接收方必须达成一致。最好的做法是不要把Padding当作随意填处理统一填0x00同时上层应用在组包时自己处理好有效长度不要依赖底层Padding。7. 配置之外的进阶话题CanTp on CAN FD与快速唤醒场景最后聊几个现代项目里绕不开的进阶话题给有基础的朋友一点扩展思路。7.1 CanTp和CAN FD的故事CAN FDFlexible Data-rate可变速率CAN单帧最大64字节数据这对CanTp是个大利好同样的报文拆的帧数少了传输时间大幅缩短。AUTOSAR从4.2版本开始完整支持CanTp on CAN FD。配置上变化主要有三点CanTpCtrlFramePayloadSize可以配64。SF帧单帧最大支持63字节PCI占用1字节如果长度小于等于63直接一个SF搞定。FF帧的12位长度域还是只能表示4095字节但配合64字节帧效率已经很高刷写速度能提升好几倍。注意CAN FD本身有BRSBit Rate Switch和ESIError State Indicator等额外位CanTp不直接处理这些那是CanIf和Can驱动的事。但配置时最好确认底层Can驱动是否工作在FD模式否则CanTp以FD格式发的帧底层按经典CAN发出去会直接出错。7.2 快速唤醒与XCP on CanTp越来越多的项目用XCPUniversal Calibration Protocol通用标定协议做标定XCP on CAN就是建立在CanTp之上的。这时候对时序的要求会比UDS更苛刻因为标定讲究实时性STmin和数据吞吐量直接挂钩。我建议标定场景直接把BS配0STmin配0x01~0x05同时把主函数周期降到5ms这样单帧吞吐率才能跑上去。7.3 多路通道与资源复用一个ECU如果同时有多个CAN通道每个通道一个CanTpChannel实例。有些项目为了省内存会把接收Buffer做成池子Buffer Pool多个NSDU共享。这个优化能做但开发复杂度直线上升建议先把单通道跑稳再考虑。我自己对CanTp的总体感受是它不像网络管理或状态管理那样有大量的状态切换代码结构相对清晰但正因为看起来简单很多人反而忽略了配置项之间暗戳戳的依赖关系。很多时候诊断报文的超时问题追根溯源都落在CanTp的某个配置参数或初始化顺序上。配置这件事真不是靠背参数表能解决的项目里多抓几次报文、多对照几轮代码和配置手感自然就出来了。
返回列表