4.3 板级通信CAN / CAN-FD本文内容摘自本人的开源书《从沙子到车辙 - 一个工程师的理解》 在线阅读/下载from-sand-to-rutsgitclone https://github.com/Lularible/from-sand-to-ruts⭐ 如果对您有帮助欢迎 Star 支持也欢迎通过 GitHub Issues 交流讨论。1983年博世工程师的够了1983年。奔驰W126的线束超过了50公斤。发动机控制、ABS、仪表盘、空调、电动窗——每个新增的电子功能都要拉一对信号线到另一个ECU。线束像藤蔓一样在车身里疯长。博世的工程师算了一笔账照这个趋势到1990年豪华车的线束会吃掉整车成本的15%。他们说够了。我们需要一条总线。不是更快不是更大——是够用。一对双绞线把所有ECU挂上去。带宽1Mbps就够了——当时最复杂的实时信号也不过几百个字节每秒。节点成本必须极低——每个ECU的MCU只有几KB RAM没有处理复杂协议栈的余力。他们的答案是CANController Area Network。1986年首次在SAE大会上发表。今天地球上每辆车出厂时都装着至少一条CAN总线。1983年的博世工程师递出了一根接力棒。40年后你还在用它。你写的每一行CAN驱动代码都在接过这根棒继续跑。显性能覆盖隐性——这是物理层的核心秘密CAN用一对差分线CAN_H和CAN_L。总线两端各接120Ω终端电阻。发送显性位逻辑0收发器驱动CAN_H到约3.5V、CAN_L到约1.5V。差分电压ΔV≈2V。收发器的驱动能力典型值是能向60Ω负载两个120Ω终端并联线缆损耗注入至少1.5V差分电压——相当于至少25mA的驱动电流。发送隐性位逻辑1收发器释放总线。两个120Ω终端电阻并联等效60Ω把CAN_H和CAN_L都拉到约2.5VVCC/2通常VCC5V。差分电压ΔV≈0V。关键是显性能覆盖隐性。如果有两个节点同时发一个发显性(0)另一个发隐性(1)总线上一定是显性。这不是协议规则——是物理定律。差分驱动器主动向60Ω负载注入电流电流在终端电阻上产生电压降——这是欧姆定律。终端电阻的被动上拉无法对抗主动驱动。这个物理特性让CAN实现了CSMA/CR——载波侦听多路访问/冲突解决。与以太网的CSMA/CD不同CAN在冲突时不丢数据、无退避延时。仲裁失败者自动退出发送、转为接收。帧不损坏。这是CAN区别于所有其他总线的核心特性——它让总线利用率在理论上可以接近100%实际工程中通常60%以达到可接受的延时。差分信号的物理直觉为什么用差分信号因为汽车是一个电磁地狱。火花塞放电——击穿电压10-30kV放电电流可达安培级上升时间小于1ns。PWM驱动的电机——di/dt可达1A/ns。大电流在车身金属结构上产生磁场磁通变化耦合到任何附近的导体上。单端信号如SPI的MOSI——一根线对地外部磁场在信号线上感应的噪声电压直接叠加在信号上。如果噪声幅度超过VIL/VIH阈值——bit错误。差分信号CAN_H和CAN_L外部电磁场在两根线上感应出几乎相等的共模噪声。因为两根线绞在一起在空间的每个点上它们到噪声源的距离几乎相等。差分接收器只关心CAN_H - CAN_L的差值——共模噪声被抵消。这个抵消的程度用CMRR共模抑制比衡量CAN收发器的CMRR60dB1000倍即1V的共模噪声在差分输出端只有1mV残差。汽车级的CAN收发器通常70dB。双绞线的绞合频率也经过了精心设计。典型的CAN线束每英寸绞合2-4次。如果绞合太密——线太硬成本高。如果绞合太疏——共模抑制效果差。2-4次/英寸是在抗扰度和机械柔性之间的工程最优。终端电阻为什么是120Ω因为典型的CAN双绞线特征阻抗约120Ω。终端电阻的值必须等于线缆的特征阻抗——否则信号到达线缆末端时产生反射。反射波叠加在原始信号上产生过冲或台阶。CAN仲裁依赖所有节点在同一bit时间内看到一致的总线电平——反射导致的振铃会破坏仲裁的确定性。120Ω不是某种标准规定的——它是线缆的物理属性规定的。共模扼流圈Common Mode Choke。在严重EMI环境中如发动机舱内CAN收发器和总线之间会串一个共模扼流圈。它由两个绕在同一磁芯上的线圈组成——差模信号CAN_H-CAN_L产生的磁通互相抵消扼流圈呈现低阻抗。共模噪声CAN_H和CAN_L同向产生的磁通互相叠加扼流圈呈现高阻抗通常500Ω在1-50MHz范围有效衰减共模噪声。这使得CAN在发动机舱里——离火花塞不到30cm——照样稳定通信。ID仲裁一个字段两个使命CAN帧里有一个11位标准帧或29位扩展帧的ID域。它同时做两件事一、标识帧的内容。协议本身不规定ID的含义——这是OEM自定义的。但行业惯例是每个CAN ID对应一组特定的信号。ID 0x3E8 发动机状态帧包含转速、冷却液温度、节气门位置等ID 0x180 轮速帧四个轮子的速度。二、仲裁优先级。在仲裁阶段所有待发节点同时往外推自己的ID。每一位送出后立刻回读总线的实际电平。如果自己送的是隐性(1)但回读到显性(0)——说明有另一个节点在送优先级更高的IDID更小0比1优先——立即退出转为监听模式。用一个具体例子来说。节点A要发送ID0x3E8 01111101000b。节点B要发送ID0x180 00110000000b。位序节点A节点B总线实际结果10(SOF后第1位)000两位都是0(显性)都继续9100A发1(隐性)B发0(显性) → B的显性覆盖A的隐性 → 总线是0。A回读看到0但自己发的是1——A仲裁失败立即退出。8—11A已退出。B继续发1。…—……B完全不受阻碍地发送整个帧。整个过程在ID域的前几位内完成。没有帧被撞坏——A只是发现自己优先级不够主动让路。B甚至不知道有人和自己竞争。这就是CSMA/CR的优雅冲突被消解而不是被检测后重发。整个过程在ID域内完成不损耗任何帧。确定性极强最高优先级帧的最坏延迟 帧长时间 3个隐性位的帧间间隔Intermission。这是CAN最天才的设计。ID域用同一个字段解决了这是什么数据和谁先说两个问题。没有中央调度器。不需要令牌传递。不需要主站轮询。硬件自己搞定一切。这是信息论级的优雅——一个字段承担了寻址和调度两个正交的语义。穿透追踪一个发动机转速信号你在OEM给的DBCCAN数据库文件里看到这一行BO_ 0x3E8 EMS_1: 8 Engine SG_ EngineSpeed : 16|160 (1,0) [0|8000] rpm 仪表盘翻译CAN ID 0x3E8的帧里起始位16、长度16位、Motorola格式、无符号、因子1.0、偏移0。范围0-8000rpm。仪表盘接收。下面是用DBC描述解析CAN信号的完整C函数// DBC 信号解析: 从 CAN 帧的 8 字节中提取一个有符号或无符号整数// Layout: Intel (little-endian) 或 Motorola (big-endian)// start_bit: DBC 中的起始位号 (0-indexed, 从 byte 0 bit 0 开始)// length: 信号长度 (bits)// is_signed: 1 有符号, 0 无符号// is_motorola: 1 Motorola 格式, 0 Intel 格式uint64_tcan_extract_signal(constuint8_tdata[8],uint8_tstart_bit,uint8_tlength,uint8_tis_signed,uint8_tis_motorola){uint64_traw0;uint8_tbit_posstart_bit;// 逐位提取for(uint8_ti0;ilength;i){uint8_tbyte_idxbit_pos/8;if(byte_idx8)return0;// 越界保护uint8_tbit_in_byte7-(bit_pos%8);// 从 MSB 开始数if(data[byte_idx](1bit_in_byte))raw|(1ULLi);if(is_motorola){// Motorola: 字节内的位从高到低, 字节间地址递增// 但跨越字节边界时 回绕 到上一字节的 LSBif((bit_pos%8)0){// 刚完成一个字节的 MSB, 跳到下一字节的 LSBbit_pos-15;// -8 to next byte, -7 to its LSB}else{bit_pos;}}else{// Intel: LSB first, 简单递增bit_pos;}}// 符号扩展 (有符号信号)if(is_signed(raw(1ULL(length-1)))){uint64_tsign_mask~((1ULLlength)-1);raw|sign_mask;}returnraw;}// 使用示例: 从 CAN 帧解析发动机转速voidcan_rx_callback(uint32_tid,uint8_t*data,uint8_tdlc){if(id0x3E8){// EngineSpeed: start_bit16, length16, Motorola, unsigneduint64_trawcan_extract_signal(data,16,16,0,1);floatrpmraw*1.0f0.0f;// factor1.0, offset0.0update_tacho_needle(rpm);}}这段代码的背后你看不到的地方发生了什么发动机ECU一侧——应用层EMS控制软件把当前转速1847 rpm写入CAN发送缓冲区实际上是一个mailbox的Data字段。CAN控制器硬件把1847编码为0x0737填入data[2]0x07、data[3]0x37。组装帧头SOF(1位显性)→ ID0x3E8(11位)01111101000b→ IDE0(标准帧)→ R0(保留位)→ DLC8(4位1000b)→ 数据8字节→ CRC15→ CRC分隔符→ ACK槽(1位, 发送方发隐性, 接收方拉低表示收到)→ ACK分隔符→ EOF(7位隐性)。接着在位填充Bit Stuffing如果连续5个相同位自动插入一个相反的填充位。接收方自动去除。这是为了确保足够的边沿密度让各节点的PLL时钟恢复能锁定。位时序每一位分成4个时段——Sync段固定1Tq、Prop段补偿总线传播延迟收发器延迟、Phase Seg1和Phase Seg2用于微调采样点位置。1 TqTime Quantum是CAN控制器的时钟周期。采样点在Phase Seg1和Seg2的交界处——通常设在75%-87.5%位宽处。这是延时和抗噪声的权衡采样点越靠后容忍的总线延迟越大长线缆采样点越靠前容忍的信号振动越小高噪声环境。你可以直观地理解这四个段Sync段是裁判鸣哨——所有节点同时开始。Prop段是给信号跑路的时间——信号从总线一头跑到另一头需要时间在这段时间里不能采样。PS1和PS2是采样窗口——在PS1结束时采样总线电平。如果采样点太早信号还没稳定太晚下一个bit已经开始。CAN的采样点通常设在75%-87.5%的位置——经过大量实车验证的最优区间。CAN收发器把TX引脚的单端逻辑0/3.3V转换为差分驱动。显性位(0)→驱动CAN_H到约3.5V、CAN_L到约1.5V。隐性位(1)→释放总线。收发器内部的主要电路是一个波形整形器推挽输出级由CANH和CANL两只大功率MOS管驱动。物理层差分电压在双绞线上传播。信号在双绞线上的传播速度约0.55c-0.65cc光速——因为在FR4 PCB或PVC线缆的介质中电磁波传播速度 c/√εr。典型的绝缘材料εr≈3-4所以速度约0.5c-0.6c。绞合使电气长度略有增加等效速度约0.55c约16.5cm/ns。以此计算5米长的线缆信号单向传播延迟约30ns。仲裁需要双向传播——一方发送bit信号传到另一方另一方采样后可能同时发送——所以最坏往返延迟约60ns。CAN的位时间必须大于这个往返延迟否则仲裁失效。这就是为什么1Mbps CAN的最大总线长度约40米、而5Mbps CAN-FD的数据段只能在短总线5m上实现。一辆5米长的车信号从车头发动机ECU传到车尾ABS ECU5m × 5ns/m ≈ 25ns。这是单向传播延迟。仲裁需要双向——A 发送 bit信号传到 BB 采样B 可能同时发送——所以最坏往返延迟 ≈ 50ns。CAN 的位时间必须大于这个往返延迟否则仲裁失效。这就是为什么 1Mbps CAN 的最大总线长度约 40 米、而 5Mbps CAN-FD 的数据段只能在短总线5m上实现。你的ECU一侧倒序——CAN收发器接收差分电压。内部比较器的阈值通常设在0.5V-0.9V差分ΔV0.9V→显性(0)ΔV0.5V→隐性(1)。转为单端逻辑送RX引脚。收发器同时进行总线故障保护——检测CAN_H和CAN_L对电源/地的短路检测显性位持续时间是否超过限值。CAN控制器硬件逐位接收。边收边做CRC校验。如果CRC正确在ACK槽发送显性位。把完整帧存入硬件RX mailbox置位接收中断标志。同时检查错误计数器TEC和REC——当TEC或REC超过127时进入Error Passive状态超过255时进入Bus Off状态自动断开与总线的连接。这是CAN的故障隔离机制——一个节点不能因为不停地发错误而把整条总线拖垮。中断服务程序NVIC嵌套向量中断控制器将CPU从主循环中拉出跳转到CAN接收ISR。ISR读取mailbox——得到ID0x3E8、DLC8、data[8]{…,0x07,0x37,…}。调用你的can_rx_callback。你的代码can_extract_signal从data[2]和data[3]拼出0x07371847。乘以factor 1.0加offset 0.0。更新仪表盘指针。从EMS软件写下1847到仪表盘指针移动——穿越了CAN控制器的位时序状态机、位填充器、CRC校验器、收发器的差分驱动器、双绞线上0.2c传播的电磁场、另一端收发器的比较器、控制器的硬件mailbox、NVIC中断路由、你的回调函数。不到10毫秒。十层硬件一行C代码。S32K上FlexCAN的配置与收发S32K14x上用的是FlexCAN模块。下面是配置CAN通信、发送一帧、接收一帧的完整寄存器级代码// FlexCAN0 初始化 // S32K144: FlexCAN0 基址 0x40024000#defineCAN0_BASE0x40024000#defineCAN_MCR(*(volatileuint32_t*)(CAN0_BASE0x00))// Module Config#defineCAN_CTRL1(*(volatileuint32_t*)(CAN0_BASE0x04))// Control 1#defineCAN_TIMER(*(volatileuint32_t*)(CAN0_BASE0x08))// Free Running Timer#defineCAN_RXGMASK(*(volatileuint32_t*)(CAN0_BASE0x10))// Rx Global Mask#defineCAN_IFLAG1(*(volatileuint32_t*)(CAN0_BASE0x30))// Interrupt Flags 1#defineCAN_IMASK1(*(volatileuint32_t*)(CAN0_BASE0x28))// Interrupt Mask 1// Mailbox 区域: 每个 MB 4个 32-bit 寄存器 (CS, ID, WORD0, WORD1)// MB0-MB7 基址 CAN0_BASE 0x80#defineMB_CS(n)(*(volatileuint32_t*)(CAN0_BASE0x80(n)*0x100x0))#defineMB_ID(n)(*(volatileuint32_t*)(CAN0_BASE0x80(n)*0x100x4))#defineMB_WORD0(n)(*(volatileuint32_t*)(CAN0_BASE0x80(n)*0x100x8))#defineMB_WORD1(n)(*(volatileuint32_t*)(CAN0_BASE0x80(n)*0x100xC))// CAN_CTRL1中的位时间配置// 设 PE时钟48MHz → 1 Tq 1/48MHz ≈ 20.83ns// 目标 500kbps: 位时间 2μs 96 Tq// 分配: Sync1, PropSeg40(41Tq含Sync), PSEG132, PSEG222, RJW22// 采样点 (14032)/(1403222) ≈ 76.8%voidflexcan0_init_750kbps(void){// 1. 进入冻结模式CAN_MCR|(124);// FRZ 1 (请求冻结)CAN_MCR|(125);// HALT 1 (或MDIS0保持模块时钟)while(!(CAN_MCR(124)));// 等待 FRZACK 1 (已进入冻结)// 2. 使能模块 (退出软复位)CAN_MCR~(125);// 清除 MDIS (使能模块)while(CAN_MCR(125));// 等待 LPMACK0 (退出低功耗)// 3. 配置位时序// CTRL1: 只能在冻结模式下修改// PRESDIV3 → Sclock 48MHz / (31) 12MHz → Tq83.33ns// 目标: 16 Tq/bit → 12MHz/16 750kbps// 但实际设置更仔细:CAN_CTRL1(30)// PRESDIV 3 (Tq83.33ns 48MHz)|(58)// PSEG1 5 (Phase Seg16 Tq)|(412)// PSEG2 4 (Phase Seg25 Tq)|(316)// PROPSEG 3 (Prop Seg4 Tq)|(320)// RJW 3 (同步跳转宽度4 Tq)|(022);// SMP 0 (单次采样 采样点)// 总 Tq/bit 1(Sync) 4(Prop) 6(PS1) 5(PS2) 16// 采样点 (146)/16 68.75%// 位率 12MHz / 16 750kbps// 4. 配置 MB0 为接收 (RX), MB1 为发送 (TX)// MB0 接收所有 ID (全局掩码先清零)CAN_RXGMASK0x00000000;// 全局掩码 0 (不屏蔽任何位)// MB0: 接收 mailbox — CODEEMPTY(0x4), 激活后自动接收MB_CS(0)0x00400000;// CODERx Empty (0x4 24)MB_ID(0)0;// ID0 (将被RXGMASK不过滤, 接收所有帧)// MB1: 发送 mailbox — CODEINACTIVE(0x8)MB_CS(1)0x00880000;// CODETx Inactive (0x8 24)// SRR1(替换远程请求位, 标准帧用)// 5. 清除所有中断标志, 使能 MB0 接收中断CAN_IFLAG10xFFFFFFFF;// 写1清除所有中断标志CAN_IMASK1(10);// 使能 MB0 的接收中断// 6. 退出冻结模式, 进入正常模式CAN_MCR~(124);// 清除 FRZwhile(CAN_MCR(124));// 等待 FRZACK0// 等待模块准备好while(!(CAN_MCR(123)));// 等待 NOTRDY0}你刚刚算出来的PROP_SEGPHASE_SEG1PHASE_SEG2最终变成CAN控制器内部的一个硬件定时器链。每个时间段对应一串触发器——到了预设的TQ数就切换到下一个段。整个CAN网络上所有节点的位时序加起来决定了谁能在下一位抢占总线。// CAN 发送一帧 voidcan_send_frame(uint32_tid,uint8_t*data,uint8_tdlc){// 等待 MB1 空闲 (检查 CODE 字段不是 TX 状态)while(((MB_CS(1)24)0xF)0xC);// CODE0xCTX In Progress// 填充 IDMB_ID(1)(id18)0x1FFC0000;// 标准帧: ID 放在 bit[28:18]// (1 14); // 如果扩展帧// 填充数据: FlexCAN 的 Byte 顺序是 Motorola// MB_WORD0 data[0-3], MB_WORD1 data[4-7]MB_WORD0(1)(data[0]24)|(data[1]16)|(data[2]8)|data[3];MB_WORD1(1)(data[4]24)|(data[5]16)|(data[6]8)|data[7];// 设置 CODETx (0xC) DLC 数据段长度// DLC 放在 CS 的 bit[3:0]MB_CS(1)(0xC24)// CODETX Once|(dlc0xF)// DLC (数据长度)|(0x016);// 不使用 RTR, IDE0 (标准帧)}CAN0-IFLAG1这个寄存器是一个边沿触发的中断标志。当CAN控制器检测到ACK slot期间总线上出现显性位差分电压0.9V时硬件自动把对应的IFLAG bit置1。你读这个bit的时候读到的是一根AHB总线上的电平——它在不到100纳秒前还是CAN收发器比较器输出端的一个电压跳变。// CAN 接收中断处理 // (在 NVIC 中使能 CAN0_ORed 中断)voidCAN0_ORed_IRQHandler(void){// 检查 MB0 的中断标志if(CAN_IFLAG1(10)){// 读取接收到的数据uint8_tdata[8];uint8_tdlcMB_CS(0)0xF;// DLCuint32_tid(MB_ID(0)18)0x7FF;// 标准IDdata[0](MB_WORD0(0)24)0xFF;data[1](MB_WORD0(0)16)0xFF;data[2](MB_WORD0(0)8)0xFF;data[3]MB_WORD0(0)0xFF;data[4](MB_WORD1(0)24)0xFF;data[5](MB_WORD1(0)16)0xFF;data[6](MB_WORD1(0)8)0xFF;data[7]MB_WORD1(0)0xFF;// 处理接收到的帧can_rx_callback(id,data,dlc);// 清除 MB0 中断标志, 重新激活接收CAN_IFLAG1(10);// 写1清除MB_CS(0)0x00400000;// 重新设置为 Rx Empty}}这段FlexCAN驱动代码——每一条MB_CS(1) 0xC8000008——都在驱动CAN控制器内部的状态机从一个mailbox取数据、组装帧、推到位引擎bit engine、驱动收发器、在双绞线上产生差分电压。而你可能只在应用层写了一个can_send_frame(0x3E8, rpm_data, 8)。CAN-FD同一对线八倍速率2012年博世推出了CAN-FD。它保持物理层不变——同一条双绞线、同样的收发器——但做了两个关键改动一、数据段变速。仲裁阶段仍然用原速率如500kbps让所有节点都能参与仲裁。但在仲裁结束后由发送节点单方面将速率切换到更高频率如5Mbps。为什么数据段可以加速因为仲裁阶段需要所有节点同步监测总线上每bit——速率上限 1 / (2 × 总线往返传播延迟)。但数据段只有发送节点在驱动——接收节点只需要采样不需要在每bit通过读回来判断仲裁——所以可以加速。切换机制仲裁阶段结束后在BRSBit Rate Switch位发送隐性——CAN-FD控制器检测到BRS隐性后在BRS位的采样点与CRC分隔符之间切换时钟分频器。接收节点也在同一时刻切换。整个过程在1 bit的时间内完成。对收发器完全透明——收发器看到的只是更快的差分电压翻转。二、数据长度扩展到64字节。经典CAN最多8字节。DLC字段在CAN-FD中被重新编码——DLC8时使用非线性编码9→12, 10→16, 11→20, 12→24, 13→32, 14→48, 15→64。CRC也做了增强17位CRC数据≤16字节或21位CRC数据16字节加上4位stuff count和奇偶校验——错误检测概率从CAN的4.7×10⁻¹¹提升到10⁻²⁷。效果传输64字节从~1.3msCAN 1Mbps压缩到~170μsCAN-FD 5Mbps数据段。对OTA诊断、固件刷写——这是质的提升。数据段速率从1Mbps翻到5Mbps意味着每个bit从1μs压缩到200ns。在这200ns里收发器必须完成驱动差分电压到显性电平1.5V、让信号传播到总线另一端~25ns for 5m cable、接收器的比较器做出判决、为下一位准备好。在5Mbps下信号的眼图开始闭合——不是因为协议有问题而是因为物理层的RC时间常数和收发器的转换速率slew rate赶不上了。这就是为什么CAN-FD只在数据段加速——仲裁段仍然用低速因为仲裁需要所有节点在同一时间看到同一电平。加速段没有仲裁只有两个节点在对话。有限资源的最优解我们来复盘博世工程师在1983年面对的所有约束线束重量必须降到十分之一 → 必须用共享总线。收发器芯片成本必须极低每节点$1→ 双绞线差分驱动最简单可靠。发动机控制和ABS需要确定性延迟10ms最坏情况→ 必须有硬件优先级仲裁不能随机退避。MCU只有几KB RAM、几十MHz → IP协议栈不可能复杂状态机不可能。CAN控制器必须用硬件状态机直接实现到硅片。电磁环境极端恶劣火花塞30kV放电→ 差分信号120Ω终端双绞线物理层天然抗共模干扰。CAN是所有这些约束的交点。它不快1Mbps不灵活无地址寻址、无路由不通用8字节数据、11/29位ID。但在汽车的约束空间里——它是最优的。这不是教科书里比较各种协议的优劣——这是在有限资源下找到唯一解。博世的工程师不是选了CAN——他们是推导出了CAN。40年后的今天CAN仍在每辆新车里活着。每个MCU里都焊着一个CAN控制器。每一条动力总成CAN上都有几十个帧在不同的时间周期内重复广播。它能在跑20年不出通信故障。它的出错率低到需要专门的测试工具CANoe的Error Frame Injection功能才能人为制造。40年前博世工程师画在纸上的协议——今天在你的S32K的FlexCAN Silicon里、在你的PCB的CAN收发器里、在车身的双绞线里——每一比特都在忠实地运行。有限资源 正确设计 一台车跑20年不出通信问题。这不是运气——这是工程的胜利。本篇小结今天我们做了一件事还原博世工程师在1983年面对的所有约束理解CAN为什么不是被选的而是被推导出来的。关键结论CAN是汽车约束空间的唯一交点线束重量必须降到十分之一、收发器成本必须$1、必须确定性延迟——CAN的每一个设计决策都是在回答一个具体的物理和经济约束。ID域的仲裁是协议层的天才设计一个字段同时解决这是什么数据和谁先说——逐位仲裁在物理层就完成了优先级判决无需中央调度器。CAN-FD在保持物理层不变的前提下实现8倍加速数据段变速BRS位触发、数据长度扩展到64字节——对OTA诊断和固件刷写是质的提升。下一节当CAN的1Mbps带宽碰到ADAS域控制器500MB的OTA固件包——差距三个数量级。车载以太网进入汽车不是替代CAN而是与它分工协作。【下集预告】CAN是汽车的主干神经——可靠、确定、低成本。但它只有1Mbps。CAN-FD顶到8Mbps但对ADAS域控制器的500MB OTA固件包——还是远远不够。雷达、摄像头的原始数据流——CAN的带宽差了三个数量级。2015年某德系OEM的E/E架构部门面临一个选择继续用CAN做一切还是引入一个新物种他们选择引入车载以太网——不是双绞线的变种是真正的TCP/IP栈搬进汽车。SOME/IP让服务自己喊I’m Here。DoIP让诊断仪通过IP地址找到ECU一次刷入几百MB固件。AVB/TSN让多摄像头视频流精准同步。但这条路上有一个根本性的问题还没回答CAN是信号总线以太网是数据网络。一辆车需要两个神经系统吗还是说——它们不是竞争关系而是分工协作CAN管活着发动机不熄火、刹车不失灵以太网管聪明自动驾驶感知、云端更新、娱乐。
从沙子到车辙(4.3):板级通信——CAN / CAN-FD
发布时间:2026/6/1 2:06:49
4.3 板级通信CAN / CAN-FD本文内容摘自本人的开源书《从沙子到车辙 - 一个工程师的理解》 在线阅读/下载from-sand-to-rutsgitclone https://github.com/Lularible/from-sand-to-ruts⭐ 如果对您有帮助欢迎 Star 支持也欢迎通过 GitHub Issues 交流讨论。1983年博世工程师的够了1983年。奔驰W126的线束超过了50公斤。发动机控制、ABS、仪表盘、空调、电动窗——每个新增的电子功能都要拉一对信号线到另一个ECU。线束像藤蔓一样在车身里疯长。博世的工程师算了一笔账照这个趋势到1990年豪华车的线束会吃掉整车成本的15%。他们说够了。我们需要一条总线。不是更快不是更大——是够用。一对双绞线把所有ECU挂上去。带宽1Mbps就够了——当时最复杂的实时信号也不过几百个字节每秒。节点成本必须极低——每个ECU的MCU只有几KB RAM没有处理复杂协议栈的余力。他们的答案是CANController Area Network。1986年首次在SAE大会上发表。今天地球上每辆车出厂时都装着至少一条CAN总线。1983年的博世工程师递出了一根接力棒。40年后你还在用它。你写的每一行CAN驱动代码都在接过这根棒继续跑。显性能覆盖隐性——这是物理层的核心秘密CAN用一对差分线CAN_H和CAN_L。总线两端各接120Ω终端电阻。发送显性位逻辑0收发器驱动CAN_H到约3.5V、CAN_L到约1.5V。差分电压ΔV≈2V。收发器的驱动能力典型值是能向60Ω负载两个120Ω终端并联线缆损耗注入至少1.5V差分电压——相当于至少25mA的驱动电流。发送隐性位逻辑1收发器释放总线。两个120Ω终端电阻并联等效60Ω把CAN_H和CAN_L都拉到约2.5VVCC/2通常VCC5V。差分电压ΔV≈0V。关键是显性能覆盖隐性。如果有两个节点同时发一个发显性(0)另一个发隐性(1)总线上一定是显性。这不是协议规则——是物理定律。差分驱动器主动向60Ω负载注入电流电流在终端电阻上产生电压降——这是欧姆定律。终端电阻的被动上拉无法对抗主动驱动。这个物理特性让CAN实现了CSMA/CR——载波侦听多路访问/冲突解决。与以太网的CSMA/CD不同CAN在冲突时不丢数据、无退避延时。仲裁失败者自动退出发送、转为接收。帧不损坏。这是CAN区别于所有其他总线的核心特性——它让总线利用率在理论上可以接近100%实际工程中通常60%以达到可接受的延时。差分信号的物理直觉为什么用差分信号因为汽车是一个电磁地狱。火花塞放电——击穿电压10-30kV放电电流可达安培级上升时间小于1ns。PWM驱动的电机——di/dt可达1A/ns。大电流在车身金属结构上产生磁场磁通变化耦合到任何附近的导体上。单端信号如SPI的MOSI——一根线对地外部磁场在信号线上感应的噪声电压直接叠加在信号上。如果噪声幅度超过VIL/VIH阈值——bit错误。差分信号CAN_H和CAN_L外部电磁场在两根线上感应出几乎相等的共模噪声。因为两根线绞在一起在空间的每个点上它们到噪声源的距离几乎相等。差分接收器只关心CAN_H - CAN_L的差值——共模噪声被抵消。这个抵消的程度用CMRR共模抑制比衡量CAN收发器的CMRR60dB1000倍即1V的共模噪声在差分输出端只有1mV残差。汽车级的CAN收发器通常70dB。双绞线的绞合频率也经过了精心设计。典型的CAN线束每英寸绞合2-4次。如果绞合太密——线太硬成本高。如果绞合太疏——共模抑制效果差。2-4次/英寸是在抗扰度和机械柔性之间的工程最优。终端电阻为什么是120Ω因为典型的CAN双绞线特征阻抗约120Ω。终端电阻的值必须等于线缆的特征阻抗——否则信号到达线缆末端时产生反射。反射波叠加在原始信号上产生过冲或台阶。CAN仲裁依赖所有节点在同一bit时间内看到一致的总线电平——反射导致的振铃会破坏仲裁的确定性。120Ω不是某种标准规定的——它是线缆的物理属性规定的。共模扼流圈Common Mode Choke。在严重EMI环境中如发动机舱内CAN收发器和总线之间会串一个共模扼流圈。它由两个绕在同一磁芯上的线圈组成——差模信号CAN_H-CAN_L产生的磁通互相抵消扼流圈呈现低阻抗。共模噪声CAN_H和CAN_L同向产生的磁通互相叠加扼流圈呈现高阻抗通常500Ω在1-50MHz范围有效衰减共模噪声。这使得CAN在发动机舱里——离火花塞不到30cm——照样稳定通信。ID仲裁一个字段两个使命CAN帧里有一个11位标准帧或29位扩展帧的ID域。它同时做两件事一、标识帧的内容。协议本身不规定ID的含义——这是OEM自定义的。但行业惯例是每个CAN ID对应一组特定的信号。ID 0x3E8 发动机状态帧包含转速、冷却液温度、节气门位置等ID 0x180 轮速帧四个轮子的速度。二、仲裁优先级。在仲裁阶段所有待发节点同时往外推自己的ID。每一位送出后立刻回读总线的实际电平。如果自己送的是隐性(1)但回读到显性(0)——说明有另一个节点在送优先级更高的IDID更小0比1优先——立即退出转为监听模式。用一个具体例子来说。节点A要发送ID0x3E8 01111101000b。节点B要发送ID0x180 00110000000b。位序节点A节点B总线实际结果10(SOF后第1位)000两位都是0(显性)都继续9100A发1(隐性)B发0(显性) → B的显性覆盖A的隐性 → 总线是0。A回读看到0但自己发的是1——A仲裁失败立即退出。8—11A已退出。B继续发1。…—……B完全不受阻碍地发送整个帧。整个过程在ID域的前几位内完成。没有帧被撞坏——A只是发现自己优先级不够主动让路。B甚至不知道有人和自己竞争。这就是CSMA/CR的优雅冲突被消解而不是被检测后重发。整个过程在ID域内完成不损耗任何帧。确定性极强最高优先级帧的最坏延迟 帧长时间 3个隐性位的帧间间隔Intermission。这是CAN最天才的设计。ID域用同一个字段解决了这是什么数据和谁先说两个问题。没有中央调度器。不需要令牌传递。不需要主站轮询。硬件自己搞定一切。这是信息论级的优雅——一个字段承担了寻址和调度两个正交的语义。穿透追踪一个发动机转速信号你在OEM给的DBCCAN数据库文件里看到这一行BO_ 0x3E8 EMS_1: 8 Engine SG_ EngineSpeed : 16|160 (1,0) [0|8000] rpm 仪表盘翻译CAN ID 0x3E8的帧里起始位16、长度16位、Motorola格式、无符号、因子1.0、偏移0。范围0-8000rpm。仪表盘接收。下面是用DBC描述解析CAN信号的完整C函数// DBC 信号解析: 从 CAN 帧的 8 字节中提取一个有符号或无符号整数// Layout: Intel (little-endian) 或 Motorola (big-endian)// start_bit: DBC 中的起始位号 (0-indexed, 从 byte 0 bit 0 开始)// length: 信号长度 (bits)// is_signed: 1 有符号, 0 无符号// is_motorola: 1 Motorola 格式, 0 Intel 格式uint64_tcan_extract_signal(constuint8_tdata[8],uint8_tstart_bit,uint8_tlength,uint8_tis_signed,uint8_tis_motorola){uint64_traw0;uint8_tbit_posstart_bit;// 逐位提取for(uint8_ti0;ilength;i){uint8_tbyte_idxbit_pos/8;if(byte_idx8)return0;// 越界保护uint8_tbit_in_byte7-(bit_pos%8);// 从 MSB 开始数if(data[byte_idx](1bit_in_byte))raw|(1ULLi);if(is_motorola){// Motorola: 字节内的位从高到低, 字节间地址递增// 但跨越字节边界时 回绕 到上一字节的 LSBif((bit_pos%8)0){// 刚完成一个字节的 MSB, 跳到下一字节的 LSBbit_pos-15;// -8 to next byte, -7 to its LSB}else{bit_pos;}}else{// Intel: LSB first, 简单递增bit_pos;}}// 符号扩展 (有符号信号)if(is_signed(raw(1ULL(length-1)))){uint64_tsign_mask~((1ULLlength)-1);raw|sign_mask;}returnraw;}// 使用示例: 从 CAN 帧解析发动机转速voidcan_rx_callback(uint32_tid,uint8_t*data,uint8_tdlc){if(id0x3E8){// EngineSpeed: start_bit16, length16, Motorola, unsigneduint64_trawcan_extract_signal(data,16,16,0,1);floatrpmraw*1.0f0.0f;// factor1.0, offset0.0update_tacho_needle(rpm);}}这段代码的背后你看不到的地方发生了什么发动机ECU一侧——应用层EMS控制软件把当前转速1847 rpm写入CAN发送缓冲区实际上是一个mailbox的Data字段。CAN控制器硬件把1847编码为0x0737填入data[2]0x07、data[3]0x37。组装帧头SOF(1位显性)→ ID0x3E8(11位)01111101000b→ IDE0(标准帧)→ R0(保留位)→ DLC8(4位1000b)→ 数据8字节→ CRC15→ CRC分隔符→ ACK槽(1位, 发送方发隐性, 接收方拉低表示收到)→ ACK分隔符→ EOF(7位隐性)。接着在位填充Bit Stuffing如果连续5个相同位自动插入一个相反的填充位。接收方自动去除。这是为了确保足够的边沿密度让各节点的PLL时钟恢复能锁定。位时序每一位分成4个时段——Sync段固定1Tq、Prop段补偿总线传播延迟收发器延迟、Phase Seg1和Phase Seg2用于微调采样点位置。1 TqTime Quantum是CAN控制器的时钟周期。采样点在Phase Seg1和Seg2的交界处——通常设在75%-87.5%位宽处。这是延时和抗噪声的权衡采样点越靠后容忍的总线延迟越大长线缆采样点越靠前容忍的信号振动越小高噪声环境。你可以直观地理解这四个段Sync段是裁判鸣哨——所有节点同时开始。Prop段是给信号跑路的时间——信号从总线一头跑到另一头需要时间在这段时间里不能采样。PS1和PS2是采样窗口——在PS1结束时采样总线电平。如果采样点太早信号还没稳定太晚下一个bit已经开始。CAN的采样点通常设在75%-87.5%的位置——经过大量实车验证的最优区间。CAN收发器把TX引脚的单端逻辑0/3.3V转换为差分驱动。显性位(0)→驱动CAN_H到约3.5V、CAN_L到约1.5V。隐性位(1)→释放总线。收发器内部的主要电路是一个波形整形器推挽输出级由CANH和CANL两只大功率MOS管驱动。物理层差分电压在双绞线上传播。信号在双绞线上的传播速度约0.55c-0.65cc光速——因为在FR4 PCB或PVC线缆的介质中电磁波传播速度 c/√εr。典型的绝缘材料εr≈3-4所以速度约0.5c-0.6c。绞合使电气长度略有增加等效速度约0.55c约16.5cm/ns。以此计算5米长的线缆信号单向传播延迟约30ns。仲裁需要双向传播——一方发送bit信号传到另一方另一方采样后可能同时发送——所以最坏往返延迟约60ns。CAN的位时间必须大于这个往返延迟否则仲裁失效。这就是为什么1Mbps CAN的最大总线长度约40米、而5Mbps CAN-FD的数据段只能在短总线5m上实现。一辆5米长的车信号从车头发动机ECU传到车尾ABS ECU5m × 5ns/m ≈ 25ns。这是单向传播延迟。仲裁需要双向——A 发送 bit信号传到 BB 采样B 可能同时发送——所以最坏往返延迟 ≈ 50ns。CAN 的位时间必须大于这个往返延迟否则仲裁失效。这就是为什么 1Mbps CAN 的最大总线长度约 40 米、而 5Mbps CAN-FD 的数据段只能在短总线5m上实现。你的ECU一侧倒序——CAN收发器接收差分电压。内部比较器的阈值通常设在0.5V-0.9V差分ΔV0.9V→显性(0)ΔV0.5V→隐性(1)。转为单端逻辑送RX引脚。收发器同时进行总线故障保护——检测CAN_H和CAN_L对电源/地的短路检测显性位持续时间是否超过限值。CAN控制器硬件逐位接收。边收边做CRC校验。如果CRC正确在ACK槽发送显性位。把完整帧存入硬件RX mailbox置位接收中断标志。同时检查错误计数器TEC和REC——当TEC或REC超过127时进入Error Passive状态超过255时进入Bus Off状态自动断开与总线的连接。这是CAN的故障隔离机制——一个节点不能因为不停地发错误而把整条总线拖垮。中断服务程序NVIC嵌套向量中断控制器将CPU从主循环中拉出跳转到CAN接收ISR。ISR读取mailbox——得到ID0x3E8、DLC8、data[8]{…,0x07,0x37,…}。调用你的can_rx_callback。你的代码can_extract_signal从data[2]和data[3]拼出0x07371847。乘以factor 1.0加offset 0.0。更新仪表盘指针。从EMS软件写下1847到仪表盘指针移动——穿越了CAN控制器的位时序状态机、位填充器、CRC校验器、收发器的差分驱动器、双绞线上0.2c传播的电磁场、另一端收发器的比较器、控制器的硬件mailbox、NVIC中断路由、你的回调函数。不到10毫秒。十层硬件一行C代码。S32K上FlexCAN的配置与收发S32K14x上用的是FlexCAN模块。下面是配置CAN通信、发送一帧、接收一帧的完整寄存器级代码// FlexCAN0 初始化 // S32K144: FlexCAN0 基址 0x40024000#defineCAN0_BASE0x40024000#defineCAN_MCR(*(volatileuint32_t*)(CAN0_BASE0x00))// Module Config#defineCAN_CTRL1(*(volatileuint32_t*)(CAN0_BASE0x04))// Control 1#defineCAN_TIMER(*(volatileuint32_t*)(CAN0_BASE0x08))// Free Running Timer#defineCAN_RXGMASK(*(volatileuint32_t*)(CAN0_BASE0x10))// Rx Global Mask#defineCAN_IFLAG1(*(volatileuint32_t*)(CAN0_BASE0x30))// Interrupt Flags 1#defineCAN_IMASK1(*(volatileuint32_t*)(CAN0_BASE0x28))// Interrupt Mask 1// Mailbox 区域: 每个 MB 4个 32-bit 寄存器 (CS, ID, WORD0, WORD1)// MB0-MB7 基址 CAN0_BASE 0x80#defineMB_CS(n)(*(volatileuint32_t*)(CAN0_BASE0x80(n)*0x100x0))#defineMB_ID(n)(*(volatileuint32_t*)(CAN0_BASE0x80(n)*0x100x4))#defineMB_WORD0(n)(*(volatileuint32_t*)(CAN0_BASE0x80(n)*0x100x8))#defineMB_WORD1(n)(*(volatileuint32_t*)(CAN0_BASE0x80(n)*0x100xC))// CAN_CTRL1中的位时间配置// 设 PE时钟48MHz → 1 Tq 1/48MHz ≈ 20.83ns// 目标 500kbps: 位时间 2μs 96 Tq// 分配: Sync1, PropSeg40(41Tq含Sync), PSEG132, PSEG222, RJW22// 采样点 (14032)/(1403222) ≈ 76.8%voidflexcan0_init_750kbps(void){// 1. 进入冻结模式CAN_MCR|(124);// FRZ 1 (请求冻结)CAN_MCR|(125);// HALT 1 (或MDIS0保持模块时钟)while(!(CAN_MCR(124)));// 等待 FRZACK 1 (已进入冻结)// 2. 使能模块 (退出软复位)CAN_MCR~(125);// 清除 MDIS (使能模块)while(CAN_MCR(125));// 等待 LPMACK0 (退出低功耗)// 3. 配置位时序// CTRL1: 只能在冻结模式下修改// PRESDIV3 → Sclock 48MHz / (31) 12MHz → Tq83.33ns// 目标: 16 Tq/bit → 12MHz/16 750kbps// 但实际设置更仔细:CAN_CTRL1(30)// PRESDIV 3 (Tq83.33ns 48MHz)|(58)// PSEG1 5 (Phase Seg16 Tq)|(412)// PSEG2 4 (Phase Seg25 Tq)|(316)// PROPSEG 3 (Prop Seg4 Tq)|(320)// RJW 3 (同步跳转宽度4 Tq)|(022);// SMP 0 (单次采样 采样点)// 总 Tq/bit 1(Sync) 4(Prop) 6(PS1) 5(PS2) 16// 采样点 (146)/16 68.75%// 位率 12MHz / 16 750kbps// 4. 配置 MB0 为接收 (RX), MB1 为发送 (TX)// MB0 接收所有 ID (全局掩码先清零)CAN_RXGMASK0x00000000;// 全局掩码 0 (不屏蔽任何位)// MB0: 接收 mailbox — CODEEMPTY(0x4), 激活后自动接收MB_CS(0)0x00400000;// CODERx Empty (0x4 24)MB_ID(0)0;// ID0 (将被RXGMASK不过滤, 接收所有帧)// MB1: 发送 mailbox — CODEINACTIVE(0x8)MB_CS(1)0x00880000;// CODETx Inactive (0x8 24)// SRR1(替换远程请求位, 标准帧用)// 5. 清除所有中断标志, 使能 MB0 接收中断CAN_IFLAG10xFFFFFFFF;// 写1清除所有中断标志CAN_IMASK1(10);// 使能 MB0 的接收中断// 6. 退出冻结模式, 进入正常模式CAN_MCR~(124);// 清除 FRZwhile(CAN_MCR(124));// 等待 FRZACK0// 等待模块准备好while(!(CAN_MCR(123)));// 等待 NOTRDY0}你刚刚算出来的PROP_SEGPHASE_SEG1PHASE_SEG2最终变成CAN控制器内部的一个硬件定时器链。每个时间段对应一串触发器——到了预设的TQ数就切换到下一个段。整个CAN网络上所有节点的位时序加起来决定了谁能在下一位抢占总线。// CAN 发送一帧 voidcan_send_frame(uint32_tid,uint8_t*data,uint8_tdlc){// 等待 MB1 空闲 (检查 CODE 字段不是 TX 状态)while(((MB_CS(1)24)0xF)0xC);// CODE0xCTX In Progress// 填充 IDMB_ID(1)(id18)0x1FFC0000;// 标准帧: ID 放在 bit[28:18]// (1 14); // 如果扩展帧// 填充数据: FlexCAN 的 Byte 顺序是 Motorola// MB_WORD0 data[0-3], MB_WORD1 data[4-7]MB_WORD0(1)(data[0]24)|(data[1]16)|(data[2]8)|data[3];MB_WORD1(1)(data[4]24)|(data[5]16)|(data[6]8)|data[7];// 设置 CODETx (0xC) DLC 数据段长度// DLC 放在 CS 的 bit[3:0]MB_CS(1)(0xC24)// CODETX Once|(dlc0xF)// DLC (数据长度)|(0x016);// 不使用 RTR, IDE0 (标准帧)}CAN0-IFLAG1这个寄存器是一个边沿触发的中断标志。当CAN控制器检测到ACK slot期间总线上出现显性位差分电压0.9V时硬件自动把对应的IFLAG bit置1。你读这个bit的时候读到的是一根AHB总线上的电平——它在不到100纳秒前还是CAN收发器比较器输出端的一个电压跳变。// CAN 接收中断处理 // (在 NVIC 中使能 CAN0_ORed 中断)voidCAN0_ORed_IRQHandler(void){// 检查 MB0 的中断标志if(CAN_IFLAG1(10)){// 读取接收到的数据uint8_tdata[8];uint8_tdlcMB_CS(0)0xF;// DLCuint32_tid(MB_ID(0)18)0x7FF;// 标准IDdata[0](MB_WORD0(0)24)0xFF;data[1](MB_WORD0(0)16)0xFF;data[2](MB_WORD0(0)8)0xFF;data[3]MB_WORD0(0)0xFF;data[4](MB_WORD1(0)24)0xFF;data[5](MB_WORD1(0)16)0xFF;data[6](MB_WORD1(0)8)0xFF;data[7]MB_WORD1(0)0xFF;// 处理接收到的帧can_rx_callback(id,data,dlc);// 清除 MB0 中断标志, 重新激活接收CAN_IFLAG1(10);// 写1清除MB_CS(0)0x00400000;// 重新设置为 Rx Empty}}这段FlexCAN驱动代码——每一条MB_CS(1) 0xC8000008——都在驱动CAN控制器内部的状态机从一个mailbox取数据、组装帧、推到位引擎bit engine、驱动收发器、在双绞线上产生差分电压。而你可能只在应用层写了一个can_send_frame(0x3E8, rpm_data, 8)。CAN-FD同一对线八倍速率2012年博世推出了CAN-FD。它保持物理层不变——同一条双绞线、同样的收发器——但做了两个关键改动一、数据段变速。仲裁阶段仍然用原速率如500kbps让所有节点都能参与仲裁。但在仲裁结束后由发送节点单方面将速率切换到更高频率如5Mbps。为什么数据段可以加速因为仲裁阶段需要所有节点同步监测总线上每bit——速率上限 1 / (2 × 总线往返传播延迟)。但数据段只有发送节点在驱动——接收节点只需要采样不需要在每bit通过读回来判断仲裁——所以可以加速。切换机制仲裁阶段结束后在BRSBit Rate Switch位发送隐性——CAN-FD控制器检测到BRS隐性后在BRS位的采样点与CRC分隔符之间切换时钟分频器。接收节点也在同一时刻切换。整个过程在1 bit的时间内完成。对收发器完全透明——收发器看到的只是更快的差分电压翻转。二、数据长度扩展到64字节。经典CAN最多8字节。DLC字段在CAN-FD中被重新编码——DLC8时使用非线性编码9→12, 10→16, 11→20, 12→24, 13→32, 14→48, 15→64。CRC也做了增强17位CRC数据≤16字节或21位CRC数据16字节加上4位stuff count和奇偶校验——错误检测概率从CAN的4.7×10⁻¹¹提升到10⁻²⁷。效果传输64字节从~1.3msCAN 1Mbps压缩到~170μsCAN-FD 5Mbps数据段。对OTA诊断、固件刷写——这是质的提升。数据段速率从1Mbps翻到5Mbps意味着每个bit从1μs压缩到200ns。在这200ns里收发器必须完成驱动差分电压到显性电平1.5V、让信号传播到总线另一端~25ns for 5m cable、接收器的比较器做出判决、为下一位准备好。在5Mbps下信号的眼图开始闭合——不是因为协议有问题而是因为物理层的RC时间常数和收发器的转换速率slew rate赶不上了。这就是为什么CAN-FD只在数据段加速——仲裁段仍然用低速因为仲裁需要所有节点在同一时间看到同一电平。加速段没有仲裁只有两个节点在对话。有限资源的最优解我们来复盘博世工程师在1983年面对的所有约束线束重量必须降到十分之一 → 必须用共享总线。收发器芯片成本必须极低每节点$1→ 双绞线差分驱动最简单可靠。发动机控制和ABS需要确定性延迟10ms最坏情况→ 必须有硬件优先级仲裁不能随机退避。MCU只有几KB RAM、几十MHz → IP协议栈不可能复杂状态机不可能。CAN控制器必须用硬件状态机直接实现到硅片。电磁环境极端恶劣火花塞30kV放电→ 差分信号120Ω终端双绞线物理层天然抗共模干扰。CAN是所有这些约束的交点。它不快1Mbps不灵活无地址寻址、无路由不通用8字节数据、11/29位ID。但在汽车的约束空间里——它是最优的。这不是教科书里比较各种协议的优劣——这是在有限资源下找到唯一解。博世的工程师不是选了CAN——他们是推导出了CAN。40年后的今天CAN仍在每辆新车里活着。每个MCU里都焊着一个CAN控制器。每一条动力总成CAN上都有几十个帧在不同的时间周期内重复广播。它能在跑20年不出通信故障。它的出错率低到需要专门的测试工具CANoe的Error Frame Injection功能才能人为制造。40年前博世工程师画在纸上的协议——今天在你的S32K的FlexCAN Silicon里、在你的PCB的CAN收发器里、在车身的双绞线里——每一比特都在忠实地运行。有限资源 正确设计 一台车跑20年不出通信问题。这不是运气——这是工程的胜利。本篇小结今天我们做了一件事还原博世工程师在1983年面对的所有约束理解CAN为什么不是被选的而是被推导出来的。关键结论CAN是汽车约束空间的唯一交点线束重量必须降到十分之一、收发器成本必须$1、必须确定性延迟——CAN的每一个设计决策都是在回答一个具体的物理和经济约束。ID域的仲裁是协议层的天才设计一个字段同时解决这是什么数据和谁先说——逐位仲裁在物理层就完成了优先级判决无需中央调度器。CAN-FD在保持物理层不变的前提下实现8倍加速数据段变速BRS位触发、数据长度扩展到64字节——对OTA诊断和固件刷写是质的提升。下一节当CAN的1Mbps带宽碰到ADAS域控制器500MB的OTA固件包——差距三个数量级。车载以太网进入汽车不是替代CAN而是与它分工协作。【下集预告】CAN是汽车的主干神经——可靠、确定、低成本。但它只有1Mbps。CAN-FD顶到8Mbps但对ADAS域控制器的500MB OTA固件包——还是远远不够。雷达、摄像头的原始数据流——CAN的带宽差了三个数量级。2015年某德系OEM的E/E架构部门面临一个选择继续用CAN做一切还是引入一个新物种他们选择引入车载以太网——不是双绞线的变种是真正的TCP/IP栈搬进汽车。SOME/IP让服务自己喊I’m Here。DoIP让诊断仪通过IP地址找到ECU一次刷入几百MB固件。AVB/TSN让多摄像头视频流精准同步。但这条路上有一个根本性的问题还没回答CAN是信号总线以太网是数据网络。一辆车需要两个神经系统吗还是说——它们不是竞争关系而是分工协作CAN管活着发动机不熄火、刹车不失灵以太网管聪明自动驾驶感知、云端更新、娱乐。