最近在搞一个STM32的CAN总线项目,遇到了个让人头大的问题:STM32通过TJA1050芯片进行CAN通信,怎么调都收发不正常。波特率、滤波器、中断都检查了无数遍,确认配置没问题,硬件连接也没松动,实在是让人摸不着头脑。甚至怀疑是不是TJA1050芯片本身有问题,换了好几个新的TJA1050芯片测试依然没效果。期间也各种百度、Google,尝试了很多方法,最终才发现问题所在,这里记录一下排查过程,希望能帮助到遇到类似问题的同学。
问题场景重现:波特率无误,收发静默
我的STM32 CAN分析仪硬件平台是STM32F103C8T6,CAN收发器是TJA1050。软件方面使用标准库开发。现象是:
- CAN总线分析仪(上位机)能正常发送数据,单片机(下位机)接收不到。
- 单片机也能发送数据,但总线分析仪同样收不到。
- 示波器观察CAN_H和CAN_L的波形,发送时波形正常,但静止时电平不符合CAN总线规范(正常应该是2.5V左右)。
- 确认波特率配置正确,和其他设备测试也是OK的。
底层原理深度剖析:TJA1050、波特率与供电的三角关系
首先回顾一下CAN总线的几个关键点:
- CAN总线规范:CAN(Controller Area Network)是一种多主总线,常用于汽车电子、工业控制等领域。其物理层使用差分信号,具有抗干扰能力强的特点。
- TJA1050芯片:TJA1050是NXP(原Philips)推出的一款CAN收发器芯片,负责CAN控制器和物理总线之间的信号转换。
- 波特率:CAN总线的通信速率,必须保证总线上所有设备的波特率一致,才能正常通信。计算方法涉及时钟频率、分频系数等参数,需要仔细核对。
- 终端电阻:CAN总线的两端需要接120欧姆的终端电阻,用于匹配阻抗,减少信号反射。
- 共模电压:CAN_H和CAN_L的电压相对于地线的电压。通常情况下,两者都应该在2.5V左右。
按理说,如果这些都没问题,CAN通信应该正常工作。但实际情况并非如此。经过仔细排查,我发现问题出在两个方面:
- USB扩展坞供电不足:我使用USB扩展坞给STM32开发板供电,这个扩展坞同时也连接了其他设备。当CAN总线上有较大数据流量时,STM32开发板的供电电压会短暂下降,导致TJA1050工作不稳定,无法正常收发数据。这就像高并发场景下,服务器CPU负载过高,导致系统响应缓慢一样。
- 波特率的精细调整:虽然我一直认为波特率是正确的,但实际上存在一定的偏差。在使用CAN分析仪时,我对波特率进行了更精确的校准,发现即使是1%的偏差,也可能导致通信失败。这跟Nginx配置不当,导致并发连接数上不去是一个道理,需要精益求精。
代码/配置解决方案:优化波特率,替换电源
针对上述问题,我的解决方案如下:
- 替换电源:不再使用USB扩展坞供电,而是使用独立的5V电源给STM32开发板供电。这样可以保证供电电压的稳定性,避免TJA1050因供电不足而工作异常。
- 精细调整波特率:通过调整STM32的CAN初始化代码中的Prescaler和BS1/BS2的值,对波特率进行精确校准。可以使用CAN分析仪实时监测总线上的数据,并根据实际情况进行微调。
以下是STM32的CAN初始化代码片段,重点关注波特率相关的配置:
void CAN_Config(void)
{
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
/* CAN register init */
CAN_DeInit(CAN1);
/* CAN cell init */
CAN_InitStructure.CAN_TTCM = DISABLE; //时间触发通信模式
CAN_InitStructure.CAN_ABOM = DISABLE; //自动离线管理
CAN_InitStructure.CAN_AWUM = DISABLE; //自动唤醒模式
CAN_InitStructure.CAN_NART = DISABLE; //禁止报文自动重传
CAN_InitStructure.CAN_RFLM = DISABLE; //接收FIFO锁定模式
CAN_InitStructure.CAN_TXFP = DISABLE; //发送FIFO优先级
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; //正常工作模式
// 波特率配置:Prescaler和BS1/BS2的值需要根据实际情况进行调整
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; //同步跳转宽度1个时间单位
CAN_InitStructure.CAN_BS1 = CAN_BS1_8tq; //时间段1为8个时间单位
CAN_InitStructure.CAN_BS2 = CAN_BS2_7tq; //时间段2为7个时间单位
CAN_InitStructure.CAN_Prescaler = 6; //分频系数。调整此值以微调波特率 (72M/(6*(1+8+7)) = 500k)
CAN_Init(CAN1, &CAN_InitStructure);
// CAN filter init
CAN_FilterInitStructure.CAN_FilterNumber = 0; //过滤器编号
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //屏蔽位模式
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //32位过滤器
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000; //过滤器高16位ID
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000; //过滤器低16位ID
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000; //屏蔽位高16位
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000; //屏蔽位低16位
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0; //过滤器关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活过滤器
CAN_FilterInit(&CAN_FilterInitStructure);
}
实战避坑经验总结:关注细节,排除干扰
总结一下这次STM32 CAN通信问题的排查经验:
- 电源稳定性至关重要:CAN总线对电源电压的稳定性要求较高,尤其是在高速通信时。使用独立的、稳定的电源是保证CAN通信正常工作的基础。
- 波特率校准需要精益求精:即使是微小的波特率偏差,也可能导致通信失败。使用CAN分析仪进行精确校准是必不可少的步骤。
- 检查终端电阻:确保CAN总线的两端都连接了120欧姆的终端电阻,避免信号反射。
- 排除干扰源:CAN总线容易受到电磁干扰,尽量避免将CAN总线靠近高频设备或强电磁场。
- 使用示波器观察波形:通过示波器观察CAN_H和CAN_L的波形,可以帮助判断总线状态是否正常。
希望这些经验能帮助大家少走弯路,顺利解决STM32 CAN通信问题。
冠军资讯
夜雨听风