去年有个朋友调试一辆工程车的CAN总线,仪表上偶尔丢转速信号,折腾了两天没结果。我过去看了下,他抓的波形里CRC错误反复出现,但总线上终端电阻、波特率都没问题。后来一查,是他自定义的报文DLC填了8,实际只发3个字节,控制器在算CRC时把后面的填充数据也纳进去了,两边对不上,接收节点自然就报CRC错误。这类问题,说穿了就是对一帧CAN报文从SOF到EOF每个位的作用没吃透。
CAN总线的数据帧结构,是所有CAN开发者的基本功。不管你是做车载电子、工业控制,还是用MCU做节点通信,报文能不能稳定收发、总线上出了故障能不能快速定位,最后都会回到“这一位是干吗的、那一场是怎么校验的”这些细节上。这篇文章准备把数据帧从头到尾拆一遍,把SOF、仲裁场、控制场、数据场、CRC场、ACK场、EOF和间歇场一次讲清楚,顺便聊聊我在实际调试中踩过的坑。想系统性理解CAN通信、或者正在排查总线疑难杂症的,这篇应该对你胃口。
1. 为什么说搞懂帧结构是调通CAN的前提
1.1 滤波、仲裁、错误识别,全都要回到帧结构上来
很多初学者觉得CAN通信无非是调个波特率、发个报文,出了问题就看波形,波形对了就能通。但实际项目里,滤波配置、ID优先级设计、错误状态判断、接收方式选择,这些看似独立的环节,本质上都依赖对帧结构的理解。
举个例子,报文滤波。你去看CAN控制器的验收滤波器配置,标准帧要填ID和掩码,扩展帧要填29位ID,很多人照着参考代码抄一遍就完事,但一旦报文收发异常,根本不知道从哪下手。如果你清楚标准帧里ID之后还有RTR、IDE、DLC这些位,你就明白滤波时为什么要区分标准帧和扩展帧,也明白为什么某些控制器在接收远程帧时,滤波器要额外处理RTR位。
再说仲裁。CAN总线的优先级仲裁发生在仲裁场,ID越小优先级越高。多节点同时发送时,谁先赢得总线,比的就是这一串ID位在总线上的逐位“与非”。如果你不理解仲裁场从高位到低位的比较过程,就解释不了为什么同一总线上一旦某个节点疯狂发低ID报文,其他节点会一直拿不到总线使用权。
错误识别更是离不开帧结构。CAN的错误帧、过载帧虽然不属于数据帧,但它们的判断依据恰恰是数据帧里的EOF、ACK槽、CRC序列这些字段。哪个位不对,控制器就抛对应类型的错误,错误计数累加到一定程度就进Bus Off。不了解这些,你连看寄存器都不知道看什么。
1.2 先从“电平视角”看一帧数据
CAN总线物理层是差分信号,显性电平对应逻辑0,隐性电平对应逻辑1。一帧数据帧,其实就是总线上一段连续的电平变化序列:
- 帧起始(SOF):1个显性位
- 仲裁场:ID位 + RTR位
- 控制场:IDE + r0 + DLC
- 数据场:0到8个字节
- CRC场:15位CRC序列 + 1位CRC界定符
- ACK场:1位ACK槽 + 1位ACK界定符
- 帧结束(EOF):7个隐性位
- 间歇场(ITM):至少3个隐性位
这一长串位,在示波器上看起来就是连续的显隐交替波形。很多调试工具能直接按字段解析出来,但如果你只在工具上看结果,不看底层电平,遇到波形异常时会很被动。我自己的习惯是:逻辑分析仪抓原始电平,再用工具做协议解析,两边对照着看。尤其是ACK场,发送方和接收方在同一个位上做主从切换,很容易出问题。
2. 一张浓缩表:标准帧和扩展帧的字段构成
2.1 各字段的名称、长度和位置
CAN数据帧有两种格式:标准帧(CAN 2.0A,11位ID)和扩展帧(CAN 2.0B,29位ID)。两种格式在总线上的位布局差别不小,我把关键字段整理成一张表,方便对照。
| 字段 | 标准帧 | 扩展帧 | 说明 |
|---|---|---|---|
| SOF | 1位显性 | 1位显性 | 帧起始 |
| 基础ID | 11位 | 11位 | 标准ID号 |
| SRR | 无 | 1位隐性 | 替代远程请求位 |
| IDE | 1位显性 | 1位隐性 | 标识符扩展位 |
| 扩展ID | 无 | 18位 | 扩展帧独有 |
| RTR | 1位 | 1位 | 远程发送请求位 |
| r0/r1 | 1位保留(r0) | 2位保留(r1+r0) | 控制器固定输出显性 |
| DLC | 4位 | 4位 | 数据长度代码 |
| 数据场 | 0-64位 | 0-64位 | 实际载荷 |
| CRC序列 | 15位 | 15位 | 校验序列 |
| CRC界定符 | 1位隐性 | 1位隐性 | 分隔符 |
| ACK槽 | 1位 | 1位 | 接收节点确认位 |
| ACK界定符 | 1位隐性 | 1位隐性 | 分隔符 |
| EOF | 7位隐性 | 7位隐性 | 帧结束 |
| ITM | ≥3位隐性 | ≥3位隐性 | 间歇场 |
有这个表在手上,看协议栈代码和寄存器手册会轻松很多。很多位字段在寄存器里不一定直接暴露,比如SRR、IDE、RTR,但你在配置发送邮箱时会看到它们被组合成“帧格式”选项。理解这些位的真实排布,才知道选标准帧和扩展帧时,谁更占总线带宽。
2.2 标准帧与扩展帧的兼容性陷阱
标准帧和扩展帧能在同一条总线上共存,但有个前提:所有节点必须都支持2.0B规范,或者至少在收到自己不认识的帧格式时能正确处理。这里有个经典坑,控制场里的IDE位,标准帧是显性(0),扩展帧是隐性(1)。如果总线上有个老节点不支持扩展帧,它看到IDE隐性位时,会因为识别不出后续的18位扩展ID而报格式错误,甚至出声明错误帧,把整条总线搅乱。
我实际遇到过两套系统对接的问题。A设备用STM32的bxCAN,发的是扩展帧,B设备是多年前的老方案,只做标准帧收发,结果两端始终不通,B节点疯狂发错误帧。后来在中间加了协议转换,问题才解决。所以做系统设计时,帧格式选型要提前确认所有节点的能力,不能默认大家都支持扩展帧。
另外,扩展帧里有个SRR位,替代远程请求位,在扩展帧里它总是隐性。它的作用是为了保证:如果总线上同时出现一个标准数据帧和扩展数据帧,标准帧能赢得仲裁。因为标准帧在RTR位置上是显性(数据帧RTR为0),扩展帧在SRR位置上是隐性(1),显性位压过隐性位。这个设计很巧妙,但它也告诉我们,标准帧在ID相同的情况下,优先级天然高于扩展帧。
3. 逐位拆解:SOF、仲裁场、控制场到数据场
3.1 SOF:一帧数据开始的“起跑线”
SOF全称Start of Frame,帧起始,只有一个显性位。CAN总线空闲时是隐性状态,所以SOF的显性边沿,是所有节点同步的起点。
为什么用显性位而不是隐性位?因为在总线空闲时,任何一个节点拉低总线(显性)都能被所有节点立刻检测到。如果SOF用隐性位,总线空闲也是隐性,节点就无法区分“一帧开始”和“总线空闲”,同步也就无从谈起。这个边沿信号,对控制器内部的位时序同步非常关键,接收节点靠这条下降沿来重新同步自己的采样点位置。
实际调试中,如果你用示波器抓波形,找一帧数据的起点,就直接找总线从隐性变成显性的第一个位置。如果总线上有好几帧连续数据,每一帧的起点都是这个动作。有些示波器触发方式可以直接设置成“显性脉冲宽度”触发,就是基于SOF的特征。
3.2 仲裁场:ID越小越优先,RTR怎么用
仲裁场紧跟在SOF后面,包含ID号和RTR位。标准帧的仲裁场是12位(11位ID + 1位RTR),扩展帧的仲裁场比较复杂,先是11位基础ID,然后是SRR、IDE、18位扩展ID,再是RTR位。
ID号不只是“报文的编号”,它同时决定报文在总线上的优先级。CAN协议规定:总线空闲后,如果多个节点同时发送,从SOF后的第一个位开始逐位比较,显性(0)优先于隐性(1),所以ID数值越小的报文,仲裁胜出的概率越高。
实操里有个容易忽略的点:ID的位顺序是高位在前,先比较ID的MSB。你设计多节点系统时,要结合自己系统的实时性要求来分配ID。比如我做过一个设备,报警报文的ID设得很小(0x010),普通数据报文ID设得比较大(0x300),这样一旦有报警,它几乎立刻就能抢到总线,不管其他节点在不在发数据。
RTR位:远程发送请求位。数据帧的RTR是显性(0),远程帧的RTR是隐性(1)。远程帧本身不带数据,它的作用是请求对端节点发送一个ID相同的数据帧。日常开发里远程帧用得不算多,但有些老协议栈会用它做主动查询。需要注意:如果总线上同时竞争的是一个标准数据帧和一个ID相同的标准远程帧,数据帧先赢,因为RTR位它发的是显性。
3.3 控制场:IDE、r0、DLC,每个bit都在出力
控制场有6位,包含IDE位、保留位(r0/r1)和4位DLC(数据长度代码)。
IDE位用来区分这是标准帧还是扩展帧。标准帧的IDE是显性(0),扩展帧是隐性(1)。为什么控制场里要放这个位?因为标准帧在ID只有11位,扩展帧在ID有29位,接收节点必须第一时间知道后续还有没有扩展ID要解析,IDE就是这个开关。前面说过,这个位也决定了标准帧和扩展帧同时竞争时的仲裁结果。
保留位r0(标准帧)、r1和r0(扩展帧)在设计规范里要求发送时输出显性(0),接收时忽略。有些较老或非标准的控制器在保留位上行为不一致,会在总线上制造格式错误。我在一个项目里就见过某国产CAN收发器在保留位上输出隐性,导致标准帧被部分接收节点判为格式错误。遇到这种批量丢帧的诡异问题,可以重点检查保留位。
DLC是4位,取值范围0到8,对应数据场0到8个字节。4位理论上能表示0到15,但CAN协议只定义到8,9到15这个区间属于非法长度。如果你在配置发送邮箱时填了大于8的值,控制器通常会限制为8,或者直接报配置错误。我之前遇到过一个案例,一个节点发出来的DLC是9,接收节点的控制器直接把整帧判定为格式错误,结果其他正常报文也跟着受影响。后来查出来是协议栈里DLC位域赋值溢出,写到了相邻字段。
4. CRC场与ACK场:数据完整性是怎么被保证的
4.1 15位CRC的校验范围和硬件处理
CRC场由两部分组成:15位CRC序列和1位CRC界定符。CRC序列的作用是校验数据在传输过程中有没有出错,它覆盖的范围是SOF、仲裁场、控制场、数据场,到CRC序列之前为止。CRC界定符固定为隐性位,用来把CRC序列和后面的ACK场隔开。
CAN用的CRC多项式是固定的15位多项式(x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1)。这套算法由控制器硬件自动完成,发送时自动生成CRC序列,接收时自动重新计算再比对,不需要软件参与。但这不意味着开发者完全不用关心。我调试时遇到CRC错误,第一反应不是去手动算CRC,而是先看:
- 波特率是否偏差太大,采样点位置不对会导致误码
- 总线电平是否异常,终端电阻阻值是否合适
- 报文格式是否配置错误,比如DLC和实际数据长度不一致
- 总线上是否有节点发送了非法位(比如保留位、界定符错误)
CRC错误频繁出现时,不要急着怀疑收发器芯片,先查物理层。电磁干扰、线缆过长、分支过多都会让位信号变形,控制器采样到的电平跟发送端不一致,CRC自然就对不上。
4.2 ACK槽:总线上有没有人理你
ACK场有两部分:ACK槽和ACK界定符,中间没有间隔。ACK槽的作用是让发送节点确认“有没有其他节点正确收到这帧数据”。
机制是这样的:发送节点在ACK槽位置主动释放总线,输出隐性电平,然后在这一位结束时去采样总线。正常情况下,至少有一个接收节点正确解析了这帧数据后,会在ACK槽位置把总线拉低,输出显性电平。发送节点采样到显性,就知道有人确认了。如果发送节点采样到隐性,说明总线上没有节点确认这帧,或者所有接收节点都觉得这帧有问题。控制器会把这个情况上报为ACK错误。
ACK槽的调试价值非常大。你用示波器抓波形时,如果发现ACK槽后面那个位一直是隐性,说明总线上存在物理层或配置问题:可能是收发器没接好、终端电阻不匹配、波特率不一致,也可能接收节点的验收滤波把所有报文都屏蔽了。我做过一个最小系统验证,两个节点都配置好了,结果就是不通,最后发现是一个节点忘记把收发器第8脚(静音模式)拉低,收不到数据,ACK自然不给。
ACK界定符固定为隐性位,作用是把ACK槽和后面的EOF分隔开。这里有个细节:ACK槽可以被任意节点拉低,但ACK界定符不能,任何节点在ACK界定符上输出显性,都会导致格式错误。
4.3 EOF与ITM:7个隐性位为什么是“安全区”
EOF全称End of Frame,帧结束,一共7个隐性位。这7个隐性位是协议里规定的固定格式。接收节点如果在这7位期间采样到显性电平,会把这判为格式错误(Frame Error),并触发错误帧。
为什么EOF必须是7个隐性位?因为前面CRC场、ACK场里有很多显性可能的区域,为了确保一帧结束之后总线能恢复到稳定的隐性状态,协议留了足够长的“缓冲”。同时,这个隐性位序列也是接收控制器判断“一帧结束”的基础:检测到连续一定数目的隐性位之后,就认为帧结束了。
EOF结束之后,总线不能立刻进入下一帧,而是要经过一个间歇场(ITM,至少3个隐性位)。间歇场是帧与帧之间的最低间隔要求。为什么需要间歇场?因为EOF和ITM加起来一共至少10个隐性位,CAN控制器需要这部分时间完成内部状态机切换、错误计数更新、DMA搬运等工作。如果你把总线利用率压到极限,每帧后面只跟3个隐性位就发下一帧,一些处理能力弱的控制器可能会来不及处理,出现丢帧。
5. EOF与ITM:一帧结束的边界和隐藏规则
5.1 7个隐性位为什么不能少
这个主题我单独拎出来再强调一下,因为EOF和位填充规则结合后,有个很容易被忽略的坑。CAN协议规定:在SOF到CRC序列结束之间,如果连续出现5个相同电平,就必须插入一个反向的填充位。但EOF的7个隐性位里不执行位填充规则。也就是说,EOF可以让7个隐性位连续出现,不需要额外插入显性位。
这个设计是为了让接收节点能检测出“位填充错误”。如果在EOF之前的有效字段里,你抓到了连续6个隐性位,那大概率是发送端位填充逻辑出错了。而在EOF期间,连续多少个隐性位都合法。
调试时还有一个细节:EOF最后一个隐性位,是接收节点状态机里的一个关键参考点。很多CAN控制器的“报文结束中断”就是在这附近触发的。你如果做高吞吐数据传输,想精确计算总线占用率,就要明白一帧的真正结束点是EOF末尾,后面还可能跟着ITM。
5.2 帧结束后的间歇场,接收中断和DMA切换的关键区域
帧结束后的ITM,虽然不是严格意义上的“帧内字段”,但它在接收策略中非常重要。软件判断一帧报文是否收完,通常有两种方式:一种是控制器在EOF结束、报文存入接收邮箱后置位中断标志;另一种是靠DMA的“空闲检测”功能,在总线空闲一段时间后触发DMA传输完成。
讨论“CAN总线一般用中断接收还是DMA接收”时,很多人只关注CPU负载,忽略了帧结构和接收机制的关系。其实不管哪种方式,底层都依赖EOF和ITM的定义:控制器必须识别出“一帧结束”,才能把缓冲区里的数据交给CPU或DMA搬运。
如果使用中断接收,控制器会在完整收到一帧(EOF结束)后产生接收中断,软件在中断里读走数据。这种方式延迟低,但一帧一个中断,在高速率、高负载下CPU占用率会比较高。如果使用DMA接收,通常是配合控制器的FIFO接口,数据按字节或字搬进内存,然后借助总线空闲检测触发一次DMA传输完成中断,把整批数据一次性取走。
我个人的看法是:一般只做少量节点、中低速率通信时,用中断接收足够稳定,配置也简单;要做高吞吐、多路数据采集时,DMA接收更合适,但你要保证链路层的“帧结束判定”是可靠的,否则字节流边界会错位,整批数据解析就会全乱。,没有DMA连续底层的基础。
6. 基于帧结构的接收策略:中断还是DMA
6.1 判断“一帧结束”是接收策略的第一要务
关于中断接收和DMA接收的取舍,前面已经提了几句,这里展开讲。
先明确一个前提:CAN总线是半双工、多主、基于报文的通信,接收节点必须知道什么时候一帧完整地到达了。传统串口的接收通常是按字节中断,然后配合超时判断一帧结束;CAN不一样,控制器硬件会自动识别SOF和EOF,在完整报文进入接收缓冲区后才会通知CPU或DMA。因此,接收策略的设计重点不是“怎么判断帧结束”,而是“如何高效地把缓冲区里的帧取走”。
中断接收的典型流程:接收FIFO非空中断触发 -> 软件读取帧信息(ID、DLC、数据) -> 清标志继续接收。这个流程简单可靠,只要中断响应够快,不丢帧就行。工程师最常犯的错误是在中断里做太多事,比如调用耗时很长的打印函数,或者执行复杂协议处理,结果下一帧的接收中断被延迟,FIFO溢出丢帧。我在调试时看到过有人把Modbus处理逻辑直接塞进CAN接收中断里,总线负载一高就丢帧,后来把协议处理挪到主循环,问题立刻消失。
DMA接收的典型流程:控制器把报文按接收FIFO映射到内存地址,DMA自动搬运,等到总线上出现空闲(ITM条件满足)时触发DMA传输完成中断,或者由控制器在收到一帧后产生DMA请求。这种方式的好处是CPU干预少,适合一帧接一帧连续接收的场景;缺点是配置复杂,一旦DMA地址边界、缓存大小和FIFO结构不匹配,排查起来比较头疼。
6.2 两种接收方式的取舍与实操建议
我自己的选型标准很简单:
- 节点数量少(个位数)、总线速率不高(250kbps以下)、报文周期不长,优先用中断接收,代码好维护,出问题好定位。
- 节点数量多、总线速率高(500kbps以上)、需要连续收发大量数据的场景,用DMA接收,但一定要先验证FIFO深度和DMA请求机制。
使用DMA接收时有几个实测经验值得记一下:
- 注意DMA传输长度配置。有些控制器接收FIFO是按字对齐的,一个报文占4个字,DMA搬运长度要按字计算,不是按字节。
- 在一帧数据到达的瞬间,DMA请求和中断标志可能同时出现,要注意处理顺序,避免重复读取。
- 如果启用了接收超时中断,配合DMA使用时要小心超时阈值:太短容易被总线噪声触发,太长会导致接收延迟过大。
无论选哪种方式,最终都要回到帧结构上来。你只要明白了EOF、ITM、ACK这些字段如何影响控制器状态机,就能理解为什么有些控制器要等EOF之后才置接收完成标志,也就能理解为什么DMA空闲检测不是万灵药。
7. 数据帧结构相关的常见故障与排查心得
7.1 ACK错误、CRC错误、格式错误的排除顺序
这一节把我在现场遇到最多的问题和排查顺序整理出来,供大家参考。
ACK错误频繁出现时,先看总线物理连接和终端电阻,再看波特率、采样点,最后看接收节点是不是真的在正常工作。我遇到过一次很奇怪的现象:两个节点互相发报文,示波器看波形非常干净,但发送节点一直报ACK错误。查了半天,发现接收节点的CAN控制器被配置成只听模式(Listen Only Mode),只收不发,ACK槽当然不会被拉低。
CRC错误代表接收节点收到了信号,但数据内容被破坏。这种情况下,优先怀疑电磁干扰、线缆质量、位时序采样点,其次怀疑报文配置不匹配。CRC错误是“内容对不上”,ACK错误是“没人响应”,这两个概念别混淆。
格式错误则表示接收节点在固定位上读到了非法电平,比如CRC界定符、ACK界定符或EOF上出现显性位。遇到格式错误,优先查发送节点的协议栈配置,看保留位、界定符是否被误改动;其次看总线负载是否过高,帧间隔ITM不足也会导致帧边界识别异常。
7.2 错误帧和EOF怎么区分
很多初学者在抓波形时,分不清错误帧和正常EOF。错误帧的结构很特殊:由6个显性位加上8个隐性位组成。当任意节点检测到总线错误时,它会立刻发送6个连续显性位,破坏当前帧,所有节点会因此放弃当前帧并开始错误处理。
区分错误帧和正常EOF的关键在于位置和电平形态。正常EOF出现在ACK界定符之后,是7个连续隐性位;错误帧的6个显性位出现在任意位置,而且后面往往跟着8个隐性位的错误界定符。在示波器上,错误帧看起来就是一段突然拉低的显性电平,紧接着一段隐性电平,跟正常帧尾的纯隐性完全不同。
如果你在总线上频繁看到错误帧,说明系统已经处在不稳定状态。错误计数会因此上升,达到255后节点进入Bus Off,彻底退出总线。排查时可以用CAN分析仪的“错误帧统计”功能,看错误帧类型分布,能更快定位是物理层问题还是协议层问题。
7.3 自己画一张高清结构图最靠谱
很多教程文章里都有CAN帧结构图,但真正要理解它,我建议你自己画一张。不用多精美,用Excel都行,把位按顺序一格一格画出来,标注每个字段的长度和固定电平,画完之后你对帧结构的记忆会非常深刻。
我自己的做法是:先画一张标准帧,再画一张扩展帧,把SOF、仲裁场、控制场、数据场、CRC场、ACK场、EOF、ITM依次排开,每个字段下方标注“发送方输出”、“接收方响应”这类信息。这张图在实际调试时用处很大——你可以把示波器抓到的波形和这张图逐字段比对,快速定位哪个位出了问题。
这里分享一个小技巧:把DLC的十六进制值和字节数对应关系打印出来贴在工位旁边。DLC四位二进制对应0-8个字节,实际很多控制器寄存器里是直接写十六进制,有人写9、10、11这类值,总线上一旦发出来,轻则接收异常,重则整条总线被格式错误刷屏。这种低级错误排查起来费时费力,一张对照表能省很多事。
8. 从帧结构到实际调参:波特率、采样点与容错
8.1 位时间与采样点的位置
搞懂帧结构之后,再去看波特率和采样点配置,你会有种“原来如此”的感觉。CAN总线的每一位并非无限窄,而是由若干个时间量子(Time Quantum,TQ)组成的。一个位周期里,控制器要决定在哪个时间点采样总线电平,这个位置就是采样点。
采样点位置直接决定通信可靠性。常见配置里,采样点通常设在75%到87.5%之间。我习惯设在80%左右,原因很简单:CAN信号在传输过程中会有上升沿/下降沿的延迟和振铃,采样点太靠前,还没等信号稳定就采样,容易误判;采样点太靠后,位末端又被下一位的边沿影响,同样危险。取中间偏后的位置,抗干扰能力最好。
位时间的另一个参数是同步跳转宽度(SJW),它决定了控制器最多能在一个位周期内调整多少TQ来同步。对于一般工业现场,SJW设1到2个TQ足够;总线很长、节点数很多时,可以适当加大。
8.2 位填充对同步的影响
位填充规则在帧结构里经常被忽略,但它对同步真的很重要。连续5个相同电平后插入一个反向位,这保证了总线上不会出现长时间没有电平跳变的情况。CAN控制器靠边沿来保持位同步,如果某个持续高电平太长,节点之间时钟偏差累积起来就可能采样错位。
所以,位填充不是单纯的传输开销,它是整个同步机制的一部分。你在计算总线负载率、估算一帧耗时的时候,要把位填充考虑进去。理论上一帧最短多少位,最坏情况插入多少个填充位,都要算进去。我之前在设计一个高负载系统时,给总线负载率留了20%的余量,就是因为位填充会让实际帧长比理论值长不少。
8.3 一个务实的建议:先抓正常波形,再抓异常波形
最后给大家一个非常实用的排查建议:系统正常运行的时候,一定要抓几帧标准波形存下来。帧结构、电平幅值、位时间长度、ACK槽的位置,这些正常状态下的特征,都是你日后排查故障时的参照物。
之前有个项目,设备在客户现场隔一段时间就通信中断。我远程看了半天日志没看出问题,后来让现场的同事抓了一段波形发过来,跟我在实验室抓的正常波形一对比,发现CRC界定符后面多了个毛刺,波形拉长了一点。最后定位是线束插头氧化导致接触电阻不稳定,总线容性负载发生了变化,位信号变形。如果没有正常波形做对照,这个问题很可能要花几天才能查出来。
做CAN开发这几年,我越来越觉得,帧结构不是一门需要死记硬背的“理论”,它就是一套现成的排查工具。你看得懂SOF,就找得到一帧数据的起点;你看得懂ACK槽,就知道有没有节点在回应;你看得懂EOF和ITM,就理解了接收中断和DMA切换的底层逻辑。把这些字段刻在脑子里,遇到任何CAN总线问题,先按帧结构逐位过一遍,很多疑难杂症都能迎刃而解。