第1章:CAN通信软件基础

各位同学,咱们今天聊聊CAN通信的软件基础。说实话,这部分内容看着基础,但我在实际项目中见过太多人在这里栽跟头。你想想看,一辆新能源车的CAN网络要是初始化没做好,轻则丢帧,重则整车瘫痪。所以咱们得把地基打牢。

1.1 CAN驱动初始化

CAN驱动初始化,说白了就是让CAN控制器进入工作状态。我习惯把初始化分成三步:时钟使能、引脚配置、模式设置。嗯,这里要注意顺序——顺序错了,芯片可能直接罢工。

波特率设置

波特率是CAN通信的命门。我记得刚入行时,有次调试一个BMS系统,怎么都收不到报文。折腾了两天,最后发现是波特率差了0.5%。从那以后,我每次都会用示波器实测一下。

波特率计算公式很简单:

波特率 = 时钟频率 / (BRP + 1) / (TSEG1 + TSEG2 + 3)

其中BRP是波特率预分频器,TSEG1和TSEG2是时间段。我给大家一个常用配置表:

目标波特率 时钟频率(MHz) BRP TSEG1 TSEG2
500 kbps 8 1 13 2
250 kbps 8 3 13 2
125 kbps 8 7 13 2
注意:采样点位置建议设置在75%-85%之间。我曾经遇到过采样点设在50%的项目,结果总线一长就丢帧,排查了整整一周。

过滤器配置

过滤器配置,说白了就是给CAN控制器装个筛子。你想想看,总线上那么多报文,你不可能全收吧?那CPU得累死。

我常用的过滤器模式有两种:

  • 掩码模式:只关心某些ID位。比如我只收0x100-0x1FF的报文,掩码设成0x700就行。
  • 列表模式:精确匹配几个ID。适合接收关键报文,比如BMS的故障码。

给大家看个实际代码:

// 配置过滤器,只接收ID为0x100和0x200的报文
CAN_FilterInitTypeDef filter;
filter.FilterNumber = 0;
filter.FilterMode = CAN_FilterMode_IdMask;
filter.FilterScale = CAN_FilterScale_32bit;
filter.FilterIdHigh = 0x100 << 5;  // 标准ID左移5位
filter.FilterIdLow = 0x0000;
filter.FilterMaskIdHigh = 0x7FF << 5;  // 掩码
filter.FilterMaskIdLow = 0x0000;
filter.FilterFIFOAssignment = CAN_FIFO0;
filter.FilterActivation = ENABLE;
CAN_FilterInit(&filter);
小技巧:我个人习惯把关键报文(比如心跳、故障码)放在FIFO0,普通报文放FIFO1。这样优先级高的报文不会被淹没。

1.2 CAN报文发送与接收

报文收发是CAN通信的核心。这里有两种模式:轮询和中断。我建议新手先用轮询,等理解了流程再切中断。

轮询模式

轮询模式,说白了就是CPU不停地问:「有报文没?有报文没?」。简单粗暴,但效率低。适合调试阶段。

// 轮询发送
CAN_Transmit(CAN1, &txMsg);
while(CAN_TransmitStatus(CAN1, 0) != CAN_TxStatus_Ok);

// 轮询接收
if(CAN_MessagePending(CAN1, CAN_FIFO0) > 0) {
    CAN_Receive(CAN1, CAN_FIFO0, &rxMsg);
    // 处理报文
}

中断模式

中断模式就聪明多了。有报文来了,CAN控制器喊一声:「CPU,来活了!」。CPU放下手头的事,赶紧处理。效率高,但要注意中断服务程序别写太长。

// 中断接收服务函数
void CAN_RX0_IRQHandler(void) {
    CanRxMsg rxMsg;
    CAN_Receive(CAN1, CAN_FIFO0, &rxMsg);
    
    // 快速处理,比如存到环形缓冲区
    RingBuf_Write(&canBuf, &rxMsg);
    
    // 千万别在这里做复杂运算!
}
避坑指南:我曾经在中断里做了个CRC校验,结果导致其他中断响应延迟,整车控制器直接报超时故障。记住,中断里只做最必要的事。

1.3 CAN错误处理

CAN总线不是完美的,错误处理是必修课。CAN控制器有三种错误状态:主动错误、被动错误、总线关闭。我给大家画个流程图:

正常状态
    ↓ (错误计数 > 127)
主动错误状态
    ↓ (错误计数 > 255)
被动错误状态
    ↓ (错误计数 > 255,且检测到128次总线空闲)
总线关闭状态

主动错误

主动错误状态下,节点还能正常收发,但会发送主动错误帧。说白了就是:「我知道错了,我改!」。这时候一般不用太担心,可能是偶发干扰。

被动错误

被动错误就有点麻烦了。节点只能发送被动错误帧,而且发送优先级降低。我遇到过的情况是,某个节点线束接触不良,反复进入被动错误。排查起来很头疼。

总线关闭

总线关闭是最严重的。节点完全退出总线,不发送任何报文。这时候必须软件复位才能恢复。嗯,这里要注意——复位前一定要记录错误原因,不然下次还会犯。

实战经验:我建议在代码里加个错误状态监控。一旦检测到总线关闭,立即记录错误码和上下文,然后尝试恢复。别傻等着。

1.4 CAN总线负载率计算

总线负载率,说白了就是总线有多忙。计算公式很简单:

负载率 = (实际传输的位数 / 总线最大传输位数) × 100%

举个例子:500kbps的总线,1秒内最多传500,000位。如果实际传了250,000位,负载率就是50%。

我给大家一个经验值:

  • < 30%:很轻松,随便加报文
  • 30%-60%:正常范围,但要注意峰值
  • 60%-80%:有点紧张,建议优化
  • > 80%:危险!随时可能丢帧
计算小工具:我个人习惯用Excel做个模板。输入每个报文的ID、数据长度、发送周期,自动算出负载率。调试时特别方便。

好了,这一章的内容就到这里。CAN通信软件基础看着简单,但每个细节都可能影响整车性能。下一章咱们聊聊CAN诊断协议,那才是真正考验功力的时候。