news 2026/6/1 2:06:47

从沙子到车辙(4.3):板级通信——CAN / CAN-FD

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从沙子到车辙(4.3):板级通信——CAN / CAN-FD

4.3 板级通信:CAN / CAN-FD

📚本文内容摘自本人的开源书《从沙子到车辙 - 一个工程师的理解》

🔗 在线阅读/下载:from-sand-to-ruts

gitclone 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,没有处理复杂协议栈的余力。

他们的答案是CAN(Controller 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.5V(VCC/2,通常VCC=5V)。差分电压Δ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收发器的CMRR>60dB(1000倍),即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)——说明有另一个节点在送优先级更高的ID(ID更小,0比1优先)——立即退出,转为监听模式。

用一个具体例子来说。

节点A要发送ID=0x3E8(= 01111101000b)。节点B要发送ID=0x180(= 00110000000b)。

位序节点A节点B总线实际结果
10(SOF后第1位)000两位都是0(显性),都继续
9100A发1(隐性),B发0(显性) → B的显性覆盖A的隐性 → 总线是0。A回读看到0,但自己发的是1——A仲裁失败,立即退出。
811A已退出。B继续发1。
B完全不受阻碍地发送整个帧。

整个过程在ID域的前几位内完成。没有帧被"撞坏"——A只是发现自己优先级不够,主动让路。B甚至不知道有人和自己竞争。这就是CSMA/CR的优雅:冲突被消解,而不是被检测后重发。

整个过程在ID域内完成,不损耗任何帧。确定性极强:最高优先级帧的最坏延迟 = 帧长时间 + 3个隐性位的帧间间隔(Intermission)。

这是CAN最天才的设计。ID域用同一个字段解决了"这是什么数据"和"谁先说"两个问题。没有中央调度器。不需要令牌传递。不需要主站轮询。硬件自己搞定一切。这是信息论级的优雅——一个字段承担了寻址和调度两个正交的语义。

穿透:追踪一个发动机转速信号

你在OEM给的DBC(CAN数据库)文件里看到这一行:

BO_ 0x3E8 EMS_1: 8 Engine SG_ EngineSpeed : 16|16@0+ (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_traw=0;uint8_tbit_pos=start_bit;// 逐位提取for(uint8_ti=0;i<length;i++){uint8_tbyte_idx=bit_pos/8;if(byte_idx>=8)return0;// 越界保护uint8_tbit_in_byte=7-(bit_pos%8);// 从 MSB 开始数if(data[byte_idx]&(1<<bit_in_byte))raw|=(1ULL<<i);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=~((1ULL<<length)-1);raw|=sign_mask;}returnraw;}// 使用示例: 从 CAN 帧解析发动机转速voidcan_rx_callback(uint32_tid,uint8_t*data,uint8_tdlc){if(id==0x3E8){// EngineSpeed: start_bit=16, length=16, Motorola, unsigneduint64_traw=can_extract_signal(data,16,16,0,1);floatrpm=raw*1.0f+0.0f;// factor=1.0, offset=0.0update_tacho_needle(rpm);}}

这段代码的背后,你看不到的地方,发生了什么?

发动机ECU一侧——

应用层:EMS控制软件把当前转速1847 rpm写入CAN发送缓冲区(实际上是一个mailbox的Data字段)。

CAN控制器硬件:把1847编码为0x0737,填入data[2]=0x07、data[3]=0x37。组装帧头:SOF(1位显性)→ ID=0x3E8(11位)=01111101000b→ IDE=0(标准帧)→ R0(保留位)→ DLC=8(4位=1000b)→ 数据8字节→ CRC15→ CRC分隔符→ ACK槽(1位, 发送方发隐性, 接收方拉低表示收到)→ ACK分隔符→ EOF(7位隐性)。接着在位填充(Bit Stuffing):如果连续5个相同位,自动插入一个相反的填充位。接收方自动去除。这是为了确保足够的边沿密度让各节点的PLL时钟恢复能锁定。

位时序:每一位分成4个时段——Sync段(固定1Tq)、Prop段(补偿总线传播延迟+收发器延迟)、Phase Seg1和Phase Seg2(用于微调采样点位置)。1 Tq(Time 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.65c(c=光速)——因为在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 ECU:5m × 5ns/m ≈ 25ns。这是单向传播延迟。仲裁需要双向——A 发送 bit,信号传到 B,B 采样,B 可能同时发送——所以最坏往返延迟 ≈ 50ns。CAN 的位时间必须大于这个往返延迟,否则仲裁失效。这就是为什么 1Mbps CAN 的最大总线长度约 40 米、而 5Mbps CAN-FD 的数据段只能在短总线(<5m)上实现。

你的ECU一侧(倒序)——

CAN收发器:接收差分电压。内部比较器的阈值通常设在0.5V-0.9V差分:ΔV>0.9V→显性(0),ΔV<0.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——得到ID=0x3E8、DLC=8、data[8]={…,0x07,0x37,…}。调用你的can_rx_callback。

你的代码:can_extract_signal从data[2]和data[3]拼出0x0737=1847。乘以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_BASE+0x00))// Module Config#defineCAN_CTRL1(*(volatileuint32_t*)(CAN0_BASE+0x04))// Control 1#defineCAN_TIMER(*(volatileuint32_t*)(CAN0_BASE+0x08))// Free Running Timer#defineCAN_RXGMASK(*(volatileuint32_t*)(CAN0_BASE+0x10))// Rx Global Mask#defineCAN_IFLAG1(*(volatileuint32_t*)(CAN0_BASE+0x30))// Interrupt Flags 1#defineCAN_IMASK1(*(volatileuint32_t*)(CAN0_BASE+0x28))// Interrupt Mask 1// Mailbox 区域: 每个 MB 4个 32-bit 寄存器 (CS, ID, WORD0, WORD1)// MB0-MB7 基址 = CAN0_BASE + 0x80#defineMB_CS(n)(*(volatileuint32_t*)(CAN0_BASE+0x80+(n)*0x10+0x0))#defineMB_ID(n)(*(volatileuint32_t*)(CAN0_BASE+0x80+(n)*0x10+0x4))#defineMB_WORD0(n)(*(volatileuint32_t*)(CAN0_BASE+0x80+(n)*0x10+0x8))#defineMB_WORD1(n)(*(volatileuint32_t*)(CAN0_BASE+0x80+(n)*0x10+0xC))// CAN_CTRL1中的位时间配置// 设 PE时钟=48MHz → 1 Tq = 1/48MHz ≈ 20.83ns// 目标 500kbps: 位时间 = 2μs = 96 Tq// 分配: Sync=1, PropSeg=40(41Tq含Sync), PSEG1=32, PSEG2=22, RJW=22// 采样点 = (1+40+32)/(1+40+32+22) ≈ 76.8%voidflexcan0_init_750kbps(void){// 1. 进入冻结模式CAN_MCR|=(1<<24);// FRZ = 1 (请求冻结)CAN_MCR|=(1<<25);// HALT = 1 (或MDIS=0保持模块时钟)while(!(CAN_MCR&(1<<24)));// 等待 FRZACK = 1 (已进入冻结)// 2. 使能模块 (退出软复位)CAN_MCR&=~(1<<25);// 清除 MDIS (使能模块)while(CAN_MCR&(1<<25));// 等待 LPMACK=0 (退出低功耗)// 3. 配置位时序// CTRL1: 只能在冻结模式下修改// PRESDIV=3 → Sclock = 48MHz / (3+1) = 12MHz → Tq=83.33ns// 目标: 16 Tq/bit → 12MHz/16 = 750kbps// 但实际设置更仔细:CAN_CTRL1=(3<<0)// PRESDIV = 3 (Tq=83.33ns @48MHz)|(5<<8)// PSEG1 = 5 (Phase Seg1=6 Tq)|(4<<12)// PSEG2 = 4 (Phase Seg2=5 Tq)|(3<<16)// PROPSEG = 3 (Prop Seg=4 Tq)|(3<<20)// RJW = 3 (同步跳转宽度=4 Tq)|(0<<22);// SMP = 0 (单次采样 @采样点)// 总 Tq/bit = 1(Sync) + 4(Prop) + 6(PS1) + 5(PS2) = 16// 采样点 = (1+4+6)/16 = 68.75%// 位率 = 12MHz / 16 = 750kbps// 4. 配置 MB0 为接收 (RX), MB1 为发送 (TX)// MB0 接收所有 ID (全局掩码先清零)CAN_RXGMASK=0x00000000;// 全局掩码 = 0 (不屏蔽任何位)// MB0: 接收 mailbox — CODE=EMPTY(0x4), 激活后自动接收MB_CS(0)=0x00400000;// CODE=Rx Empty (0x4 << 24)MB_ID(0)=0;// ID=0 (将被RXGMASK不过滤, 接收所有帧)// MB1: 发送 mailbox — CODE=INACTIVE(0x8)MB_CS(1)=0x00880000;// CODE=Tx Inactive (0x8 << 24)// SRR=1(替换远程请求位, 标准帧用)// 5. 清除所有中断标志, 使能 MB0 接收中断CAN_IFLAG1=0xFFFFFFFF;// 写1清除所有中断标志CAN_IMASK1=(1<<0);// 使能 MB0 的接收中断// 6. 退出冻结模式, 进入正常模式CAN_MCR&=~(1<<24);// 清除 FRZwhile(CAN_MCR&(1<<24));// 等待 FRZACK=0// 等待模块准备好while(!(CAN_MCR&(1<<23)));// 等待 NOTRDY=0}

你刚刚算出来的PROP_SEG+PHASE_SEG1+PHASE_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);// CODE=0xC=TX In Progress// 填充 IDMB_ID(1)=(id<<18)&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];// 设置 CODE=Tx (0xC) + DLC + 数据段长度// DLC 放在 CS 的 bit[3:0]MB_CS(1)=(0xC<<24)// CODE=TX Once|(dlc&0xF)// DLC (数据长度)|(0x0<<16);// 不使用 RTR, IDE=0 (标准帧)}

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&(1<<0)){// 读取接收到的数据uint8_tdata[8];uint8_tdlc=MB_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=(1<<0);// 写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通过"读回"来判断仲裁——所以可以加速。

切换机制:仲裁阶段结束后,在BRS(Bit Rate Switch)位发送隐性——CAN-FD控制器检测到BRS=隐性后,在BRS位的采样点与CRC分隔符之间切换时钟分频器。接收节点也在同一时刻切换。整个过程在1 bit的时间内完成。对收发器完全透明——收发器看到的只是更快的差分电压翻转。

二、数据长度扩展到64字节。经典CAN最多8字节。DLC字段在CAN-FD中被重新编码——DLC>8时使用非线性编码(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.3ms(CAN 1Mbps)压缩到~170μs(CAN-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为什么不是"被选的"而是"被推导出来的"。

关键结论:

  1. CAN是汽车约束空间的唯一交点:线束重量必须降到十分之一、收发器成本必须<$1、必须确定性延迟——CAN的每一个设计决策都是在回答一个具体的物理和经济约束。
  2. ID域的仲裁是协议层的天才设计:一个字段同时解决"这是什么数据"和"谁先说"——逐位仲裁在物理层就完成了优先级判决,无需中央调度器。
  3. 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管"活着"(发动机不熄火、刹车不失灵),以太网管"聪明"(自动驾驶感知、云端更新、娱乐)。

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

Electron 完全指南:从入门到发布桌面应用

在 Web 技术统治了浏览器的今天,我们该如何快速地去构建一个真正的桌面应用程序?答案就是 Electron ,该框架使你可以使用前端技术栈开发跨平台的桌面应用——Visual Studio Code、Figma、Slack、Discord、Notion、GitHub Desktop 等众多知名应用都基于 Electron 构建。本文将…

作者头像 李华
网站建设 2026/6/1 2:03:05

量子计算实现Hadamard码高效解码方案

1. Hadamard码解码的量子算法实现 在量子计算领域&#xff0c;纠错码解码一直是个极具挑战性的课题。传统计算模型下&#xff0c;Hadamard码的解码需要指数级复杂度&#xff0c;而量子计算为我们提供了突破这一限制的可能性。本文将详细介绍一种基于GHZ态和量子扇出门的Hadamar…

作者头像 李华
网站建设 2026/6/1 1:58:34

手写 AI 内容摘要系统:从零实现智能文档摘要与关键信息提取

前言 信息爆炸时代&#xff0c;每天产生海量的文档、文章、报告需要阅读。用大模型做摘要已经成了标配&#xff0c;但直接调用 ChatGPT API 做摘要和手写一套完整的内容摘要系统之间&#xff0c;隔着整整一个工程化实现的距离。 我们需要处理长文档分片、多种摘要策略&#x…

作者头像 李华