1. 从沙子到车辙:为什么一个搞底层的人要死磕CAN总线
“从沙子到车辙”这个系列名字听起来有点玄乎,其实说的就是一颗芯片从硅砂变成矿石、再经过设计、流片、封装,最后装到车上跑出车辙印的完整链路。前面几篇聊了芯片制造的基础流程和车规级认证的门槛,这一篇专门把镜头对准车载网络里最核心的那根“神经”——CAN总线,以及它的升级版CAN-FD。
如果你正在做车载控制器、BMS、电机控制器、域控制器,或者只是单纯想搞明白为什么车上的线束能那么“聪明”地互相通信,那这篇内容就是写给你的。不管你是刚入行的嵌入式新人,还是从消费电子转汽车电子的老手,CAN都是绕不过去的一道坎。我见过太多人第一次调CAN的时候被波特率、终端电阻、采样点这些参数搞得一头雾水,也见过不少项目因为CAN-FD的配置不当导致整车通信间歇性丢帧。这篇文章就把我这些年踩过的坑、总结出来的经验,一次性讲透。
CAN的全称是Controller Area Network,控制器局域网。它诞生的初衷非常朴素:车上电子控制单元越来越多,如果每个信号都用一根独立的线来传,线束会重到把车压垮。CAN用两根线(CAN_H和CAN_L)把所有节点串成一条总线,靠差分信号传数据,抗干扰能力强,成本低,可靠性高。后来随着ADAS、域控制器、OTA这些功能上来,经典CAN的带宽不够用了,于是有了CAN-FD,FD是Flexible Data-rate的缩写,灵活数据速率。它把数据段的速度提上去,同时把单帧能装的数据从8字节扩展到64字节,这是车载网络演进里非常关键的一步。
2. CAN与CAN-FD的核心机制拆解
2.1 差分信号与显性隐性:CAN总线的物理层逻辑
CAN总线最底层的逻辑其实就两个字:差分。CAN_H和CAN_L两根线,平时都处于大约2.5V的电压水平,这叫隐性状态,逻辑上是1。当总线上有节点要发显性位(逻辑0)的时候,它会把CAN_H拉高到3.5V左右,把CAN_L拉低到1.5V左右,两根线之间产生大约2V的压差。接收端不看单根线对地的绝对电压,只看两根线之间的差值,这就是差分传输的精髓。
为什么差分这么重要?因为汽车环境里电磁干扰极其恶劣,电机、点火线圈、继电器都在疯狂往外辐射噪声。如果用的是单端信号,噪声直接叠加在信号线上,很容易误判。但差分信号里,噪声通常会同时耦合到两根线上,接收端做减法的时候噪声就被抵消掉了。这个原理说起来简单,但实际布线的时候如果CAN_H和CAN_L没有紧密绞在一起,或者长度差异太大,差分的效果就会大打折扣。
显性位和隐性位还有一个关键特性:显性会覆盖隐性。也就是说,如果两个节点同时发送,一个发显性一个发隐性,总线上呈现的是显性。这个特性直接支撑了CAN的仲裁机制,后面会详细讲。
2.2 非破坏性仲裁:CAN为什么不会撞车
CAN总线没有主从概念,任何节点想在总线空闲的时候都可以发。那如果两个节点同时开始发怎么办?这就是CAN最精妙的设计——非破坏性仲裁。
每个节点在发送的同时也在监听总线。它发出去一个位,然后读回来总线上的实际电平。如果自己发的是隐性(1),但读回来是显性(0),说明有别的节点在发显性,自己的优先级不够,于是立刻退出发送,转为接收模式。整个过程没有任何数据被破坏,赢的那个节点继续发,输的那个节点下一轮再试。
仲裁靠的是帧起始部分的标识符(Identifier)。标识符数值越小,优先级越高,因为越早出现显性位就越容易赢得仲裁。这就是为什么在车载网络设计里,安全相关的报文(比如刹车、转向)通常分配更小的ID,确保它们能优先上总线。
我刚开始做CAN的时候,总觉得这套机制有点像会议室里大家同时想发言,谁的声音低沉(显性)谁就先说,声音尖的(隐性)自动闭嘴。虽然不完全准确,但帮助理解足够了。
2.3 CAN-FD的两大杀招:变速率与大数据场
经典CAN的波特率上限是1Mbps,实际车载网络里常用500kbps或者250kbps。数据场最多8字节。这两个限制在域控制器时代就非常尴尬了。一个OTA升级包动辄几兆,用经典CAN传简直是龟速。CAN-FD从两个维度解决了这个问题。
第一个维度是变速率。CAN-FD的帧结构里,仲裁段和CRC段之后的确认段仍然用标准波特率传输,但数据段可以切换到更高的波特率。比如仲裁段用500kbps,数据段用2Mbps甚至5Mbps。这样既保证了仲裁的可靠性,又大幅提升了有效数据的传输速度。这个切换靠的是帧内的BRS位(Bit Rate Switch),发送节点在BRS位之后切换到高速率,接收节点检测到BRS位后也同步切换。
第二个维度是数据场扩展。CAN-FD单帧最多可以携带64字节数据,是经典CAN的8倍。而且它用了新的CRC多项式,针对不同数据长度有不同的CRC计算方式,保证校验强度。数据长度用DLC编码,经典CAN里DLC是0到8,CAN-FD里DLC从9到15分别对应12、16、20、24、32、48、64字节。
这里有个容易踩的坑:CAN-FD的帧格式有两种,一种叫FD Base,一种叫FD Extended。区别在于标识符是11位还是29位,以及是否包含扩展帧格式。配置的时候如果搞混了,通信直接起不来。
3. 从零搭建CAN通信的实操要点
3.1 硬件层:终端电阻、共模电感和布线规范
CAN总线的硬件设计有几个铁律,违反了任何一条都可能导致通信不稳定甚至完全不通。
第一,终端电阻。CAN总线两端必须各接一个120欧姆的终端电阻,并联在CAN_H和CAN_L之间。这两个电阻的作用是匹配总线阻抗,吸收信号反射。如果只接一个或者一个都不接,信号会在总线末端反射回来,和原始信号叠加,导致波形畸变,误码率飙升。我见过一个项目,样车阶段通信正常,量产之后偶发丢帧,查了两个月才发现是某个节点的终端电阻贴片虚焊。
第二,共模电感。在CAN_H和CAN_L进入收发器之前,通常会串一个共模电感,用来抑制共模干扰。选型的时候要注意电感的额定电流和阻抗特性,不能随便拿一个电源用的共模电感来凑数。
第三,布线。CAN_H和CAN_L必须双绞,绞距要均匀,通常建议每米绞合30到50次。双绞的目的是让两根线尽可能处于相同的电磁环境里,这样共模噪声才能被有效抵消。另外,分支线(stub)要尽可能短,一般建议不超过0.3米,速率越高分支越短。如果分支太长,信号在分支末端反射,同样会影响通信质量。
第四,屏蔽。如果环境特别恶劣,比如电机控制器附近,可以考虑用屏蔽双绞线,屏蔽层单点接地。但要注意,屏蔽层如果两端都接地,会形成地环路,反而引入干扰。
3.2 收发器选型:3.3V还是5V,高速还是容错
CAN收发器是连接CAN控制器和总线的桥梁。选型的时候主要看几个参数:供电电压、速率等级、是否支持CAN-FD、是否有唤醒功能。
经典CAN收发器通常是5V供电,比如TJA1050、SN65HVD230这些。但现在的MCU很多是3.3V的,所以3.3V供电的收发器越来越常见,比如TJA1051、SN65HVD233。选型的时候一定要看收发器的VIO引脚,有些收发器逻辑侧电压可以独立设置,方便和不同电压的MCU对接。
CAN-FD收发器要求更高,因为数据段速率可能到5Mbps甚至更高,收发器的传播延迟和对称性必须足够好。常用的CAN-FD收发器有TJA1044、TJA1057、MCP2542FD等。如果项目要求支持CAN-FD,千万别拿经典CAN收发器凑合,速率上不去不说,波形质量也差很多。
还有一个容易忽略的点:收发器的环路延迟。CAN-FD的仲裁段和数据段速率不同,如果收发器延迟太大,在速率切换的时候可能导致采样错误。选型的时候要看数据手册里的Loop Delay参数,通常要求小于200ns。
3.3 控制器配置:波特率、采样点和同步跳转宽度
CAN控制器的配置是实操中最容易出问题的地方。核心参数有三个:波特率、采样点、同步跳转宽度。
波特率的计算基于CAN时钟和位时间分段。一个位时间被分成四个段:同步段、传播段、相位缓冲段1、相位缓冲段2。采样点位于相位缓冲段1和相位缓冲段2之间。采样点的位置用百分比表示,比如75%意味着采样点在位时间的75%处。
采样点的设置非常关键。如果采样点太靠前,信号还没稳定就采样,容易误判;如果太靠后,留给相位缓冲段2的时间太短,无法有效补偿时钟偏差。一般来说,仲裁段采样点建议在75%到80%之间,数据段因为速率高,建议在70%到75%之间。但这也不是绝对的,具体要看总线长度、节点数量和收发器延迟。
同步跳转宽度(SJW)决定了每次重同步可以调整的最大时间量。SJW太小,时钟偏差累积起来无法补偿;SJW太大,又容易受噪声影响。通常SJW设置为1到4个时间份额。
我一般用这个流程来配:先确定CAN时钟频率,比如40MHz;然后确定目标波特率,比如500kbps;计算位时间总时间份额,40MHz除以500kbps等于80个时间份额;然后分配各个段,同步段固定1个,传播段加相位缓冲段1决定采样点,相位缓冲段2留够余量。具体计算后面会给一个实例。
4. CAN-FD配置实战:从参数计算到代码落地
4.1 位时间参数计算:一个500k/2M的完整实例
假设MCU的CAN时钟是40MHz,仲裁段目标波特率500kbps,数据段目标波特率2Mbps,仲裁段采样点80%,数据段采样点75%。
先算仲裁段。位时间总份额 = 40MHz / 500kbps = 80。采样点80%意味着采样点在第64个份额处。同步段固定1个份额。传播段加相位缓冲段1等于63个份额。相位缓冲段2等于80减64等于16个份额。所以仲裁段配置为:Sync=1,Prop+PS1=63,PS2=16,SJW取4。
再算数据段。位时间总份额 = 40MHz / 2Mbps = 20。采样点75%意味着采样点在第15个份额处。同步段1个份额。传播段加相位缓冲段1等于14个份额。相位缓冲段2等于20减15等于5个份额。数据段配置为:Sync=1,Prop+PS1=14,PS2=5,SJW取2。
这些参数最终会写到CAN控制器的寄存器里。不同厂商的MCU寄存器定义不一样,但逻辑是相通的。比如某款常用MCU的CAN-FD模块,仲裁段和数据段各有独立的位时间寄存器,需要分别配置。
注意:数据段的PS2不能太小,否则在速率切换的时候容易出问题。一般建议PS2至少为2个时间份额,如果计算出来小于2,要么降低数据段速率,要么提高CAN时钟。
4.2 过滤器配置:如何精准接收你想要的报文
CAN控制器通常有多个验收过滤器,用来决定哪些报文会被接收到FIFO里,哪些会被丢弃。过滤器配置得好,可以大幅降低CPU的中断负载。
过滤器有两种模式:掩码模式和范围模式。掩码模式是指定一个ID和一个掩码,掩码为1的位必须匹配,掩码为0的位不关心。范围模式是指定一个起始ID和一个结束ID,在这个范围内的报文都接收。
举个例子,假设你只关心ID为0x100到0x10F的标准帧,可以用范围模式,起始ID设为0x100,结束ID设为0x10F。如果只关心ID为0x123的报文,可以用掩码模式,ID设为0x123,掩码设为0x7FF(全部匹配)。
CAN-FD的过滤器还要注意一点:FD帧和经典CAN帧可以混合存在于同一总线上,过滤器需要能区分。有些控制器的过滤器可以设置是否接收FD帧,有些则不行,需要软件层面再判断。
我一般建议把安全相关的报文放在独立的FIFO里,用高优先级中断处理;普通状态报文放在另一个FIFO里,用低优先级中断或者轮询处理。这样即使总线负载很高,安全报文也不会被延误。
4.3 发送与接收的代码框架
下面给一个简化的CAN-FD发送和接收的代码框架,基于常见的嵌入式C语言风格。不同MCU的寄存器操作不一样,但流程是通用的。
// CAN-FD初始化 void canfd_init(void) { // 1. 使能CAN时钟 // 2. 配置引脚复用 // 3. 进入初始化模式 // 4. 配置仲裁段位时间:Sync=1, Prop+PS1=63, PS2=16, SJW=4 // 5. 配置数据段位时间:Sync=1, Prop+PS1=14, PS2=5, SJW=2 // 6. 配置过滤器 // 7. 退出初始化模式,进入正常模式 } // 发送CAN-FD帧 void canfd_send(uint32_t id, uint8_t *data, uint8_t len) { // 1. 等待发送邮箱空闲 // 2. 写入ID、DLC、数据 // 3. 设置BRS位,使能速率切换 // 4. 设置FDF位,标识为FD帧 // 5. 触发发送 } // 接收中断处理 void canfd_rx_isr(void) { // 1. 读取接收FIFO // 2. 解析ID、DLC、数据 // 3. 根据ID分发到不同的处理函数 // 4. 清除中断标志 }实际项目里,发送的时候要注意邮箱的优先级。如果多个邮箱同时待发,控制器会根据邮箱编号或者ID来决定发送顺序。有些控制器支持设置邮箱优先级,有些则固定按编号。如果安全报文和普通报文共用一个发送邮箱,可能会出现安全报文被普通报文阻塞的情况。解决办法是给安全报文分配独立的发送邮箱,并且设置更高的优先级。
接收的时候要注意FIFO溢出。如果总线负载很高,CPU处理不及时,FIFO可能会满。满了之后新来的报文会被丢弃,导致丢帧。解决办法是合理设置FIFO深度,优化中断处理时间,或者用DMA来搬运数据。
5. 常见问题与排查技巧实录
5.1 通信完全不通:从物理层到协议层的排查顺序
CAN通信完全不通是最常见的问题,排查的时候要按顺序来,不要东一榔头西一棒子。
第一步,查电源和地。收发器的供电是否正常,地是否共地。如果两个节点的地电位差太大,差分信号可能超出收发器的共模范围,通信直接失败。
第二步,查终端电阻。用万用表量CAN_H和CAN_L之间的电阻,正常应该是60欧姆左右(两个120欧姆并联)。如果量出来是120欧姆,说明只接了一个终端电阻;如果量出来是无穷大,说明一个都没接。
第三步,查波形。用示波器看CAN_H和CAN_L的波形。正常应该有明显的差分信号,显性位时CAN_H约3.5V,CAN_L约1.5V。如果波形幅度不够,可能是收发器驱动能力不足或者总线负载太重。如果波形上有明显的振铃,可能是终端电阻不匹配或者分支太长。
第四步,查波特率。用CAN分析仪或者示波器测量位时间,确认实际波特率和配置是否一致。如果波特率不匹配,通信肯定不通。
第五步,查过滤器。如果物理层和波特率都没问题,但就是收不到报文,很可能是过滤器配置错了。可以先把过滤器设成接收所有报文,确认能收到之后再逐步缩小范围。
5.2 偶发丢帧:采样点与总线负载的博弈
偶发丢帧比完全不通更难查,因为它时有时无,很难复现。根据我的经验,偶发丢帧通常有以下几个原因。
第一个原因是采样点设置不合理。如果采样点太靠前,信号还没稳定就采样,在温度变化或者电压波动的时候就容易出错。解决办法是用示波器测量实际波形,确认采样点位于信号稳定区域。一般建议仲裁段采样点在75%到80%之间。
第二个原因是总线负载太高。CAN总线的负载率建议不要超过50%,如果超过70%,低优先级报文可能会被长时间阻塞,甚至出现丢帧。可以用CAN分析仪统计总线负载率,如果太高,要么优化报文发送频率,要么升级到CAN-FD提高带宽。
第三个原因是终端电阻不匹配。如果总线两端电阻不是精确的120欧姆,或者中间有节点引入了额外的电容,信号反射会导致波形畸变,在特定数据模式下就会出错。
第四个原因是收发器延迟太大。CAN-FD对收发器延迟很敏感,如果收发器延迟超过数据手册标称值,在高速数据段就可能采样错误。解决办法是换用延迟更小的收发器,或者降低数据段速率。
5.3 CAN-FD速率切换失败:BRS位与收发器延迟的坑
CAN-FD的速率切换是故障高发区。典型症状是仲裁段正常,一到数据段就出错,或者通信间歇性失败。
一个常见原因是收发器的对称性不够。CAN-FD在速率切换的时候,对收发器的上升沿和下降沿对称性要求很高。如果收发器的高电平和低电平传播延迟差异太大,在高速数据段就会导致位宽失真。选型的时候要看收发器数据手册里的对称性参数,通常要求小于10ns。
另一个原因是BRS位的配置。BRS位本身是在仲裁段之后、数据段之前发送的,它自己是用仲裁段速率发的。如果发送节点和接收节点对BRS位的检测不一致,就会导致速率切换不同步。解决办法是确保所有节点的BRS位配置一致,并且收发器支持CAN-FD。
还有一个容易被忽略的点是数据段的CRC。CAN-FD用了新的CRC多项式,如果CRC配置错了,接收节点会认为帧出错,直接丢弃。不同厂商的CAN-FD控制器对CRC的处理方式可能略有不同,配置的时候要仔细看参考手册。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| 通信完全不通 | 终端电阻缺失 | 万用表量CAN_H与CAN_L间电阻 | 补接120欧姆终端电阻 |
| 通信完全不通 | 波特率不匹配 | 示波器测位时间 | 统一所有节点波特率 |
| 通信完全不通 | 过滤器配置错误 | 设为接收所有报文测试 | 修正过滤器ID和掩码 |
| 偶发丢帧 | 采样点不合理 | 示波器看采样点位置 | 调整PS1和PS2比例 |
| 偶发丢帧 | 总线负载过高 | CAN分析仪统计负载率 | 降低发送频率或升级FD |
| 偶发丢帧 | 分支线太长 | 检查布线拓扑 | 缩短分支至0.3米以内 |
| FD数据段出错 | 收发器延迟大 | 查收发器数据手册 | 换用低延迟FD收发器 |
| FD数据段出错 | BRS配置不一致 | 检查所有节点配置 | 统一BRS和速率参数 |
| FD数据段出错 | CRC配置错误 | 对照参考手册检查 | 修正CRC多项式配置 |
6. 一些实操心得与避坑建议
做CAN和CAN-FD这些年,我最大的体会是:物理层的问题永远比协议层多。很多人一遇到通信问题就去翻协议手册,改寄存器配置,但最后发现是终端电阻没接或者线束绞距不对。所以我的建议是,遇到问题先查物理层,用示波器和万用表把电源、地、终端电阻、波形都确认一遍,再去动软件。
另一个心得是关于采样点的。很多MCU厂商提供的配置工具会自动计算采样点,但自动算出来的不一定最优。我一般会手动核算一遍,确保采样点落在信号稳定区域。特别是CAN-FD的数据段,速率高,采样点稍微偏一点就可能出错。
还有一点是关于总线负载的。经典CAN在500kbps下,如果负载率超过50%,低优先级报文的延迟就会明显增加。如果项目里有安全相关的报文,一定要确保它们在总线上的延迟可控。一个实用的技巧是给安全报文分配更小的ID,并且在发送邮箱里设置更高的优先级。
最后说一个CAN-FD的坑:不是所有MCU都支持CAN-FD,即使支持,也不是所有引脚都支持。选型的时候一定要确认MCU的CAN-FD控制器数量和引脚映射。有些MCU只有部分CAN通道支持FD,其他通道还是经典CAN。如果项目要求所有通道都支持FD,选型的时候就要特别注意。
这个系列后面还会继续聊车载以太网和SOME/IP,那是另一个层面的故事了。但CAN和CAN-FD作为车载网络的基础,值得每一个做汽车电子的人花时间吃透。