1. 问题现象与背景分析
最近在调试基于APM32F103的CAN总线通信时,遇到了一个让人头疼的问题——数据包频繁丢失。最初以为是软件配置问题,反复检查了波特率设置、过滤器配置和中断处理逻辑,但问题依旧存在。直到深入追踪时钟树配置,才发现这个看似简单的CAN通信问题,背后隐藏着一个硬件设计上的"坑":为了节省成本,项目中使用HSI(内部高速时钟)作为系统时钟源,而非更稳定的HSE(外部高速时钟)。
这个问题在工业控制、汽车电子等对通信可靠性要求较高的场景中尤为致命。当节点密集、总线负载较高时,HSI时钟的精度不足会导致CAN通信出现位定时误差,进而引发数据包丢失。更棘手的是,这种问题往往表现为偶发性故障,给排查带来很大难度。
2. 时钟源选择对CAN通信的影响机制
2.1 CAN总线对时钟精度的要求
CAN总线采用NRZ(非归零)编码和位填充技术,其通信质量高度依赖精确的位定时。根据ISO 11898标准,在1Mbps速率下,位定时误差必须控制在±0.3%以内。APM32F103的CAN控制器使用系统时钟分频后作为时间基准,当时钟源存在偏差时,会导致:
- 采样点位置偏移,可能错过正确的信号电平
- 位填充规则失效,引发格式错误
- 同步跳转宽度(SJW)补偿不足,无法纠正时钟偏差
2.2 HSI与HSE的实测性能对比
我们对两种时钟源进行了对比测试(环境温度25℃±3℃,VDD=3.3V):
| 参数 | HSI(8MHz) | HSE(8MHz晶振) |
|---|---|---|
| 出厂校准精度 | ±1% | ±10ppm |
| 温漂(-40~85℃) | ±3% | ±50ppm |
| 长期稳定性 | ±2% | ±5ppm/year |
| 启动时间 | 2μs | 1~10ms |
实测发现,使用HSI时CAN通信的误码率比HSE高2个数量级,在高温环境下尤为明显。当环境温度超过60℃时,HSI时钟偏差可能达到4%,远超CAN总线容忍极限。
3. 问题复现与诊断方法
3.1 搭建测试环境
为了准确复现问题,建议搭建以下测试环境:
- 使用两台APM32F103开发板,配置为CAN通信节点
- 接入CAN总线分析仪(如PCAN-USB Pro)
- 编写测试脚本,以1ms间隔发送包含递增计数的标准数据帧
- 监控总线负载,逐步提高到70%以上
3.2 关键诊断步骤
-
检查时钟配置寄存器(RCC_CFGR)确认时钟源
c复制if(RCC->CFGR & RCC_CFGR_SWS_HSI) { // 当前使用HSI作为系统时钟 } -
测量实际CAN波特率误差:
c复制// 在CAN接收中断中计算时间间隔 uint32_t last_tick = 0; void CAN1_RX0_IRQHandler() { uint32_t current = DWT->CYCCNT; uint32_t interval = current - last_tick; last_tick = current; // 计算实际波特率偏差 } -
使用示波器捕捉CAN_H和CAN_L信号,观察位宽一致性
4. 解决方案与优化建议
4.1 硬件层面改进
-
更换时钟源:优先使用8MHz外部晶振作为HSE,配置方法:
c复制RCC_HSEConfig(RCC_HSE_ON); while(!RCC_WaitForHSEStartUp()); RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); // 72MHz RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); -
增加时钟监控:启用CSS(时钟安全系统),当HSE故障时触发中断:
c复制
RCC_ClockSecuritySystemCmd(ENABLE); NVIC_EnableIRQ(RCC_IRQn); -
优化PCB布局:
- 晶振距离MCU不超过1cm
- 避免时钟线路与高频信号平行走线
- 为晶振添加接地保护环
4.2 软件层面优化
-
调整CAN位定时参数:
c复制CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq; CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq; CAN_InitStructure.CAN_Prescaler = 6; // 72MHz/(1+6+3)/6 = 1Mbps -
实现自动重传机制:
c复制CAN_InitStructure.CAN_NART = DISABLE; // 启用自动重传 -
增加软件容错处理:
c复制uint8_t retry_count = 0; while(CAN_Transmit(CAN1, &TxMessage) == CAN_TxStatus_NoMailBox) { if(++retry_count > 3) break; Delay(1); }
5. 实测数据对比
在不同时钟源配置下进行24小时压力测试的结果:
| 测试条件 | 发送帧数 | 丢失帧数 | 错误帧数 | 最大延迟 |
|---|---|---|---|---|
| HSI@25℃ | 1,000,000 | 2,341 | 156 | 3.2ms |
| HSI@85℃ | 1,000,000 | 15,672 | 893 | 12.7ms |
| HSE@25℃ | 1,000,000 | 0 | 0 | 1.1ms |
| HSE@85℃ | 1,000,000 | 0 | 0 | 1.1ms |
6. 经验总结与避坑指南
-
选型阶段:
- 对时序敏感的外设(CAN、USB、Ethernet)必须评估时钟精度需求
- 工业级应用建议预留HSE电路,即使初始设计使用HSI
-
调试技巧:
- 出现偶发通信故障时,首先检查时钟配置
- 使用频谱分析仪测量时钟信号质量
- 在高温环境下进行长时间老化测试
-
替代方案:
- 若必须使用HSI,可降低CAN波特率(如500kbps)
- 增加硬件CRC校验和软件重传机制
- 考虑使用带时钟校准功能的型号(如APM32F103xC/D)
这个案例给我的深刻教训是:在成本敏感型项目中,硬件选型的妥协可能带来更高的后期调试成本。对于关键通信接口,时钟源的稳定性应该作为设计红线,不能轻易让步。
