news 2026/10/5 9:59:34

CANoe实战:ISO15765多帧传输原理与VIN读取报文解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANoe实战:ISO15765多帧传输原理与VIN读取报文解析

干过几年车载总线测试的人都知道,只要跟UDS诊断打交道,CANoe基本是绕不开的家伙。ISO15765这几个字听起来挺唬人,但它本质上解决一个问题:CAN总线一帧只能塞8个字节,诊断数据动不动就是几十个字节,怎么传?ISO15765规定了一套“拆包-打包”的规则,业内常叫它ISO-TP。很多人第一次在Trace里看到一堆以 10、21、30 开头的帧,直接懵掉——明明我发的是个读VIN的请求,怎么总线上回了一大串?

这篇文章就专门把这层窗户纸捅破。我会用CANoe作为分析工具,从ISO15765多帧传输的帧结构讲起,再到如何配置CANoe的传输层,最后用一次读取VIN(17字节数据)的真实报文,逐字节拆给你看。无论你是刚接触ECU测试的应届生,还是被诊断报文折磨过的老同志,照着走一遍,下次再看到带多帧传输的Trace,心里会踏实很多。

1. 多帧传输到底在解决什么问题

1.1 为什么会有ISO15765

CAN总线最早是给控制信号设计的,像转速、油门、刹车这类量,一帧8字节绰绰有余。但后来车厂开始做诊断和刷写,要把“读取故障码”“写入配置”“更新固件”这种动辄几十上百字节的数据塞进CAN,8字节显然不够用。

总不能为了一次诊断就前前后后发十几帧原始数据吧?接收方怎么知道哪些帧属于同一个消息?哪一帧在前、哪一帧在后?中间断了怎么办?于是ISO 15765-2成了事实标准,它定义了一种传输协议,也就是我们常说的ISO-TP。它把上层的数据拆成多帧,加上标识信息,按顺序发出去,接收方再把这些帧拼成完整数据。CANoe里很多诊断报文显示成一条“Diagnostic”消息,底层其实是ISO-TP在背后自动拆装。

1.2 四种帧类型的“角色分工”

ISO-TP用四种帧类型完成整个多帧流程:

  • 单帧(SF):数据长度不超过7字节(经典CAN),直接用一帧传完。
  • 首帧(FF):数据长度超过7字节时,第一帧先发出去,里面带着总长度和首段数据。
  • 流控帧(FC):接收方收到首帧后,返回一个“允许发送”的指令,里面携带BS、STmin等参数。
  • 连续帧(CF):发送方按流控帧的指示,一帧一帧把剩余数据发完。

这四种帧靠第一个字节的PCI(协议控制信息)区分。PCI高四位是帧类型标识,例如0x0代表单帧,0x1代表首帧,0x2代表连续帧,0x3代表流控帧。在CANoe的Trace窗口里,你往往会看到“SF/FF/FC/CF”的缩写,要能一眼认出来。

1.3 物理寻址与功能寻址

ISO15765的寻址通常分两种:

  • 物理寻址:点对点,比如测试仪发给某个ECU,请求ID一般是0x7E0,响应ID是0x7E8。这种最常用。
  • 功能寻址:广播,一个请求发给多个ECU,常用ID是0x7DF。功能寻址一般只用于请求,不用于响应。

CANoe里配置传输层的时候,要分清这两种寻址对应的CAN ID。很多人配置错误导致收不到响应,多半是物理寻址ID没配对。

2. 实战前必须搞定的CANoe环境配置

2.1 我推荐的CANoe配置方式

先说一下环境。我用的是CANoe 16/17系列,接口有VN1640和虚拟CAN通道。如果你只是想学多帧报文分析,不一定非要真实硬件,CANoe自带的虚拟CAN总线也能跑通整个流程。安装的时候务必确认装了“Diagnostics”相关组件,部分功能需要授权,没授权的话诊断窗口会用不了。

新建工程时选择CAN总线,配置两条虚拟通道:Channel1和Channel2。用CANoe的“Simulation Setup”把通道连接起来,再加上一个DBC文件定义好节点和报文。没有DBC也可以先裸发CAN帧,但解析不出ISO-TP消息,所以建议老老实实建一个基础DBC。

DBC我用CANdb++(Vector自带的数据库编辑器)创建。典型配置是定义两个节点:Tester(测试仪)和ECU,然后定义物理请求报文ID=0x7E0,物理响应报文ID=0x7E8。ISO-TP本身不强制这些ID具体是什么值,但UDS诊断领域约定俗成,大部分OEM都沿用这套逻辑。

2.2 配置传输层的关键步骤

DBC建好后,在CANoe的“Diagnostics/ISO TP”区域,右键添加一个ISO TP传输对象。这里要设置:

  • 发送ID和接收ID:请求ID 0x7E0,响应ID 0x7E8。
  • 寻址类型:物理寻址。
  • 协议类型:ISO 15765-2(CAN)。
  • 处理模式:可以把“对完整帧进行重组”打开,这样在Diagnostic窗口里看到的是一条完整诊断响应,而不是一堆拆分后的裸CAN帧。

这一步最容易踩的坑是“诊断描述文件(CDD)”。CANoe诊断控制台加载CDD后,可以直接用服务名发诊断请求,非常方便。但如果手头没有CDD,也可以手动发送CAN帧,靠CANoe的TP层去拆包。两种方式我都会在实战里覆盖到。

2.3 Trace窗口和过滤技巧

ISO-TP报文在Trace里非常刷屏,尤其是发送几十个字节的多帧响应,一秒钟可能几十条CF帧。我的习惯是先把Trace窗口的默认列配置好:查看CAN ID、数据场、帧类型、绝对时间、相对时间这几列,再打开设置里的“右键过滤”功能。

点击Trace窗口工具栏上的“Display Filter”图标,可以只显示请求ID和响应ID,或者只显示ISO-TP相关报文。不要在一堆其他网络管理、应用报文里找诊断帧,效率太低。还可以在“Write Window”里用CAPL脚本打印关键信息,比如打印收到的完整多帧数据,分析速度更快。

3. 手把手实战:用VIN读取把多帧报文拆明白

3.1 一个真实的读取VIN全过程

我以一个ECU返回VIN码的场景为例。UDS服务是22(读数据),DID是F190(VIN)。请求数据是22 F1 90,只有3个字节,CAN单帧完全能装下,所以请求帧只发一个单帧:

方向帧类型CAN IDData
请求SF0x7E002 22 F1 90

02是单帧PCI,代表单帧且数据长度为2字节,后边跟22 F1 90。这个请求没有任何悬念,一帧就完事了。

重点在响应。假设ECU返回的VIN是ABC123XYZ45678901,ASCII码一共17个字节。UDS响应数据为62 F1 90(2字节DID + 1字节服务响应)再加上17字节VIN,总共20字节。20字节超过了7字节,所以必须走ISO-TP多帧。

3.2 首帧FF怎么解析

ECU发送的第一帧如下:

方向帧类型CAN IDData
响应FF0x7E810 14 62 F1 90 41 42 43

首帧PCI的格式是:第一个字节高四位为1,表示首帧;低四位是完整数据长度的bit11~bit8;第二个字节是完整数据长度的bit7~bit0。所以10 14拼接得到长度0x014,也就是20字节,正好是将来重组后的完整响应长度。

首帧数据区只有6字节,放的是完整响应最前面的6个字节:

  • 62:响应服务ID,与请求的22服务对应。
  • F1 90:DID字段。
  • 41、42、43:就是VIN里最前面的三个字符A B C。

所以这条首帧的完整含义就是:我要传20字节数据,前6字节我已经发给你了,下面是后面14字节,等我收到流控帧再安排节奏。

3.3 流控帧FC怎么解析

ECU发完首帧后,正常不会立刻继续发连续帧。它必须先等测试仪(Tester)回一个流控帧,告诉它“你按这个节奏来发”。

测试仪收到首帧后,回一个流控帧:

方向帧类型CAN IDData
请求FC0x7E030 00 00

流控帧PCI的第一个字节高四位是3,代表流控帧。低四位叫FS(Flow Status):

  • 0:Continue,继续发。
  • 1:Wait,先等着。
  • 2:Overflow,接收缓冲区溢出,丢弃本次传输。

示例里的FS=0,表示可以继续。第二个字节是BS(Block Size),表示允许发送方连续发送的连续帧数量。BS=0是个特殊值,表示“不限帧数,一口气发完”。第三个字节是STmin(Separation Time min),表示两个连续帧之间的最小时间间隔。STmin=0x00代表不强制额外延时,但很多工具还是会留一点间隔,避免CAN发送缓冲溢出。

3.4 连续帧CF的序号和数据拼接

收到FC后,ECU开始连发剩余的连续帧。完整响应的20字节中,首帧已经带了6个,还剩14个字节,刚好分成两个连续帧每个7字节:

第一帧CF:

方向帧类型CAN IDData
响应CF0x7E821 31 32 33 58 59 5A 34

PCI第一个字节高四位是2,表示连续帧;低四位是序列号SN。第一个连续帧的SN通常是1(部分协议栈也有从0开始的,后续会聊到)。数据区放的是后续7个字节:31 32 33 58 59 5A 34,也就是ASCII字符1 2 3 X Y Z 4。

第二帧CF:

方向帧类型CAN IDData
响应CF0x7E822 35 36 37 38 39 30 31

SN=2,数据区是剩余7个字节:35 36 37 38 39 30 31,也就是5 6 7 8 9 0 1。

接收方拿到FF+CF1+CF2后,按顺序把数据区拼接起来:

  • FF数据6字节:62 F1 90 41 42 43
  • CF1数据7字节:31 32 33 58 59 5A 34
  • CF2数据7字节:35 36 37 38 39 30 31

拼起来就是62 F1 90 41 42 43 31 32 33 58 59 5A 34 35 36 37 38 39 30 31,转成ASCII就是ABC123XYZ45678901。到这里,一次完整的多帧响应就解析完了。

3.5 传输参数的计算逻辑

有人会问,BS和STmin怎么选?简单说:

  • BS决定“发几帧停一下”。BS=N,表示发送方每发N个连续帧后,必须等接收方重新发一个FC才继续。
  • STmin决定“帧与帧之间的最小时间”。如果ECU接收处理速度慢,STmin就要给大一点;如果太快,又会造成总线负载升高。

STmin的具体含义还要注意单位。0x00到0x7F表示0到127ms;0x80到0xF0时,单位变成100us,比如0xFA其实不是标准值。常见的10是1ms,32是5ms,64是10ms。实际项目中,STmin会根据ECU底层驱动能力来确定。CANoe的诊断传输层配置里可以直接选,配置完它会自动生成对应的FC参数。

4. 多帧传输常见的坑:现象与排查

做过几轮诊断测试后,你会发现多帧传输的问题翻来覆去就那么几个。我整理了一份在CANoe环境下的避坑经验,按出现频率排序。

4.1 看不见首帧或连续帧,Trace里全是裸CAN帧

很多时候明明发了20字节的响应,Trace窗口里却看不到带有10、21、30的报文,反而看到一串完整的原始数据被拆成8字节一条的CAN帧。这是CANoe把ISO-TP层“帮助”了,它默认重组了完整消息,并且在Diagnostics窗口和Trace里只展示重组后的结果。

解决方法是到Trace窗口的“Display”里打开ISO-TP的详细模式,或者直接在报文列表里看“CAN TP”列。如果你希望看到传输层的裸帧,可以右键“ISO TP”选项,选择“show transport protocol frames”。不要误以为ECU没发多帧,其实数据早就到了。

4.2 发完FF后一直没有FC响应

典型的故障是ECU发完首帧后,测试仪不回复流控帧。排查顺序如下:

  • 检查故障注入:是不是CAPL脚本或者DBC里把流控帧屏蔽了。
  • 检查地址ID:确认请求ID和响应ID配对是否正确。
  • 检查诊断协议配置:CANoe的ISO TP传输层如果发送ID和接收ID设置反了,收到的FF会被当成无效帧丢弃。
  • 检查超时时间:CANoe默认的N_As/N_Bs超时是1秒,如果测试仪没来得及解析,也会出现超时。

这个坑最隐蔽的地方在于,很多新手用CDD和诊断窗口发送请求时,CANoe会自动帮你回FC,但如果你直接用“CAN IG”面板手动发送帧,CANoe不会自动回流控帧,此时ECU发完FF就一直等,最终超时报N_Bs超时。所以我建议,纯学习阶段就用诊断窗口或CAPL来走完整流程,别用手动CAN发送去模拟测试仪。

4.3 CF序号为什么不是从0开始

我在拿到首帧之后,见过很多人在Trace里盯着第一个连续帧的SN看。有人收到21开头的帧,就会问:为什么不是20?

ISO15765标准里,SN是一个4位循环计数器,从0到15循环。但在标准实现中,首帧之后第一个连续帧的SN通常从1开始,后续依次递增。部分工具或协议栈会从0开始,这个在判断时会有歧义。我的经验是不要纠结起始值,重点检查连续帧的SN是否连续。如果中间出现跳号,比如前面是SN=3,后面直接SN=5,那基本可以判定丢帧,需要做重传处理。

CANoe在重组时如果发现SN不连续,会在Trace里显示错误或者直接超时。你可以在诊断窗口的“Trace Filter”里勾选“Protocol errors”,快速定位这类问题。

4.4 BlockSize与STmin搭配出问题

有时候首帧发完,流控帧也回得很快,但连续帧发了几帧就停了。这时候排查BS和STmin的取值。BS=3代表每发3个CF,就要等接收方再发一次FC。如果接收方迟迟不发第二个FC,传输就卡住。BS=0虽然不限次数,但工程上不建议在复杂的CAN网络中无脑设0,因为一旦总线拥堵,要么出错误帧,要么接收方缓冲区溢出。

STmin设置太小时,比如0x00,连续帧之间几乎没有间隔。如果ECU底层写Flash或者做校验,可能来不及处理,导致后续CF被丢弃。一般项目里STmin至少设到0x0A(10ms)。在CANoe里可以通过“CAN Statistics”窗口看到总线负载和错误帧情况,如果错误帧多,先怀疑STmin。

4.5 常见问题速查表

现象可能原因解决方向
Trace只显示重组后的诊断消息CANoe默认合并TP层开启“show transport protocol frames”
发完FF后无FC手动发送帧导致无流控响应改用诊断窗口或CAPL
报文接收超时N_BsFC未返回或超时参数过短检查ID、协议配置和超时时间
连续帧SN跳变丢帧或总线错误检查STmin和BS,查看错误帧
响应长度与实际不符FF中的长度计数错对照FF的12位长度字段重新计算
功能寻址收不到响应功能寻址不能用于响应请求用0x7DF,响应必须物理寻址0x7E8

5. 进阶玩法与个人心得

5.1 用CAPL脚本自动发送并校验多帧

手动发一次UDS请求没有问题,但做压力测试和自动化回归时,就要用CAPL来跑。我常用的套路是:

  • 用diagSetP2Parameter设置超时。
  • 用diagSendRequest发送CDD里定义好的诊断请求。
  • 通过on diagResponse回调接收完整响应。
  • 在回调里用diagGetParameter解析响应中的VIN字节并比对预期值。

用CAPL的好处是,你不需要关心底层拆包拼包逻辑,CANoe的协议栈已经把FF/CF/FC全部处理好了。它会给你一个完整响应的字节数组,直接用MemCmp做结果校验。批量刷写、反复读写DID的自动化脚本,基本都是这个思路。

5.2 与Python联合测试的扩展

有些团队习惯用Python控制CANoe做集成测试。可以用CANoe的COM接口启动工程、发送诊断请求、读取响应。比如调用CANoe.Application对象,再通过Diagnostic对象触发请求,这样就能在Python测试框架里跑诊断自动化。

不过这种方案对CANoe版本依赖较强,COM接口偶尔会因为版本不匹配出幺蛾子。如果是学习阶段,先用CAPL把逻辑调通,再考虑Python封装。

5.3 一个调试小技巧

最后分享一个我压箱底的小技巧。排查多帧传输问题时,我习惯在Trace窗口添加两列:Ack和Error。当某个CF帧出现CRC或ACK错误时,这两列会标红,立刻能定位到物理层干扰。很多时候你以为ISO-TP配置不对,其实是总线上丢帧。

另外,CANoe的“Graphics Window”配合ISO-TP层,可以把BS和STmin的时序可视化。调了几次参数后,你会对“STmin=1ms和5ms的实际总线效果”有直观感觉。这些参数的变化用肉眼很难看出来,但图形曲线骗不了人。

根据我个人经验,ISO15765多帧传输真正难的不是协议本身,而是你第一次面对一堆看似杂乱帧时的心态。记住每类帧的角色:FF是报幕员,FC是交通指挥,CF是跑腿的,SF是一句话能说完的事。用CANoe多看几次真实报文,多拆几轮字节,这个坎很快就能迈过去。

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

一文读懂Linux内核PM Core:设备挂起与恢复的完整机制

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 9:59:10

cJSON内存泄漏全解析:free与cJSON_Delete的区别及排查实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 9:55:05

基于机器学习的学生体测成绩预测与分析系统开题报告(计算机毕业设计)

一、选题背景与研究意义 (一)选题背景 随着国民健康战略与教育数字化深度融合,大学生体质健康测试已成为高校人才培养的核心考核指标,体测成绩直接关联学生评奖评优、毕业资格与综合素质评价。当前国内大学生群体普遍存在运动习…

作者头像 李华
网站建设 2026/10/5 9:54:53

岭回归解决多重共线性:Python实现与调参避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 9:51:43

中科蓝讯Downloader从开关机配置到EQ调音全流程实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华