STM32学习笔记:深入浅出解析CAN总线
一、引言:为什么工程师必须掌握CAN总线?
在完成SPI教程后,许多读者问:“STM32还有哪些工业级通信协议值得学习?” 答案无疑是CAN总线。作为汽车电子神经脉络和工业控制骨干网络,CAN总线具有:
- 多主仲裁机制:无中心节点冲突自动解决
- 抗干扰能力:差分信号(CANH/CANL)可承受±36V共模电压
- 错误检测率99.9%:CRC校验+ACK应答+帧格式检查
- 传输距离:40米@1Mbps,10公里@5Kbps
据2023年汽车电子报告,单辆燃油车平均搭载120个CAN节点,电动车高达200+节点
二、CAN协议核心机制解析
2.1 物理层:工业环境的生存之道
// 典型CAN收发器电路(TJA1050)_______
CAN_TX --| TXD | 120Ω| |----- CAN_H -----||----> 总线| TJA | ||
MCU | 1050 |----- CAN_L -----||
CAN_RX --| RXD | 120Ω|______|
关键参数:
- 显性电平:CANH=3.5V, CANL=1.5V (差分2V)
- 隐性电平:CANH=CANL=2.5V (差分0V)
- 终端电阻必须放置总线两端(阻抗匹配防反射)
2.2 数据帧:高效传输的秘密
# CAN 2.0B扩展帧结构(29位ID)
| SOF | ID[28:18] | SRR | IDE | ID[17:0] | RTR | r0 | DLC | Data[0-8] | CRC | DEL | ACK | EOF |
核心字段:
- 仲裁场:29位ID(优先级数值越小越高)
- 控制场:DLC=0~8(数据长度码)
- 数据场:最大64位(CAN FD可达64字节)
2.3 非破坏性仲裁机制
三、STM32 bxCAN外设深度剖析
3.1 邮箱系统:高效数据管理
typedef struct {__IO uint32_t TIR; // 标识符+请求类型__IO uint32_t TDTR; // 数据长度+时间戳__IO uint32_t TDLR; // 数据低4字节__IO uint32_t TDHR; // 数据高4字节
} CAN_TxMailBox_TypeDef; // 发送邮箱寄存器// 接收FIFO结构
typedef struct {__IO uint32_t RIR; __IO uint32_t RDTR;__IO uint32_t RDLR;__IO uint32_t RDHR;
} CAN_FIFOMailBox_TypeDef;
3.2 过滤器配置:精准数据接收
32位掩码模式配置示例:
// 接收ID范围:0x100 ~ 0x10F
uint32_t FilterId = 0x100 << 21; // 标准ID左移21位
uint32_t FilterMask = 0x7F0 << 21; // 高7位必须匹配CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterIdHigh = FilterId >> 16;
sFilterConfig.FilterIdLow = FilterId & 0xFFFF;
sFilterConfig.FilterMaskIdHigh = FilterMask >> 16;
sFilterConfig.FilterMaskIdLow = FilterMask & 0xFFFF;
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);
四、硬件实战:双机通信系统搭建
4.1 材料清单
器件 | 型号 | 数量 |
---|---|---|
STM32开发板 | STM32F407VET6 | 2 |
CAN收发器 | TJA1050 | 2 |
终端电阻 | 120Ω 1%精度 | 2 |
双绞线 | AWG22屏蔽线 | 1米 |
4.2 接线图
[STM32A] [总线] [STM32B]CAN_TX--->TXD | |CAN_RX<---RXD | || | |+--CAN_H----+---------------+| |+--CAN_L----+---------------+| | |GND----------GND------------GND
注意:终端电阻仅加在总线两端的收发器上!
五、CubeMX配置详解
5.1 位时序计算(500Kbps示例)
APB1时钟 = 42MHz
预分频器 = 6
时间量子tq = 6 / 42MHz = 142.86ns位时间 = 14tq (推荐配置)Sync_Seg = 1tqProp_Seg = 2tqPhase_Seg1 = 6tqPhase_Seg2 = 5tq
采样点 = (1+2+6)/14 ≈ 78.6% (符合汽车标准)
六、HAL库代码实现
6.1 CAN初始化
CAN_HandleTypeDef hcan;void CAN_Init(void) {hcan.Instance = CAN1;hcan.Init.Prescaler = 6;hcan.Init.Mode = CAN_MODE_NORMAL;hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;hcan.Init.TimeSeg1 = CAN_BS1_6TQ;hcan.Init.TimeSeg2 = CAN_BS2_5TQ;hcan.Init.TimeTriggeredMode = DISABLE;hcan.Init.AutoBusOff = DISABLE;hcan.Init.AutoWakeUp = DISABLE;hcan.Init.AutoRetransmission = ENABLE; // 自动重传hcan.Init.ReceiveFifoLocked = DISABLE;hcan.Init.TransmitFifoPriority = DISABLE;HAL_CAN_Init(&hcan);// 启动CANHAL_CAN_Start(&hcan);
}
6.2 数据发送函数
#define TX_ID_STD 0x101 // 标准帧ID
#define TX_ID_EXT 0x1800A001 // 扩展帧IDvoid CAN_SendMessage(uint8_t* data, uint8_t len, uint32_t id, uint32_t ide) {CAN_TxHeaderTypeDef TxHeader;uint32_t TxMailbox;if(ide == CAN_ID_STD) {TxHeader.StdId = id;TxHeader.IDE = CAN_ID_STD;} else {TxHeader.ExtId = id;TxHeader.IDE = CAN_ID_EXT;}TxHeader.RTR = CAN_RTR_DATA;TxHeader.DLC = len;TxHeader.TransmitGlobalTime = DISABLE;// 发送到邮箱(超时100ms)HAL_CAN_AddTxMessage(&hcan, &TxHeader, data, &TxMailbox);
}
6.3 中断接收(FIFO0)
// 中断回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {CAN_RxHeaderTypeDef RxHeader;uint8_t RxData[8];HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData);// 打印接收数据printf("ID: 0x%lX, DLC: %d, Data: ", (RxHeader.IDE == CAN_ID_STD) ? RxHeader.StdId : RxHeader.ExtId,RxHeader.DLC);for(int i=0; i<RxHeader.DLC; i++)printf("%02X ", RxData[i]);printf("\n");
}
七、双机通信实验
7.1 测试场景设计
测试用例 | 发送节点A | 预期接收节点B |
---|---|---|
标准帧测试 | ID=0x101, Data=0xAA | 应正确接收并打印 |
扩展帧测试 | ID=0x1800A001 | 需配置扩展ID过滤器 |
波特率容错测试 | 临时改为480Kbps | 应出现错误帧 |
7.2 抗干扰压力测试
// 在总线上注入噪声(实验室方法)
1. 使用函数发生器在CANH-GND间注入100KHz正弦波
2. 幅值从0.5V逐步增加到2.5V
3. 监控错误计数器:HAL_CAN_GetError(&hcan, CAN_ERROR_CNT_REG);
八、高级调试技巧
8.1 使用CAN分析仪诊断
# 使用candump工具监听(Linux环境)
$ candump can0 -t a -x can0 101 [1] 55 # 标准帧ID=0x101, 数据0x55can0 1800A001 [8] A1 B2 C3 D4 E5 F6 # 扩展帧can0 ERROR [0] // 错误帧
8.2 常见故障排查表
现象 | 可能原因 | 解决方案 |
---|---|---|
完全无通信 | 终端电阻缺失 | 总线两端加120Ω电阻 |
能发不能收 | 过滤器配置错误 | 检查ID掩码设置 |
通信随机失败 | 波特率容差不足 | 调整BS1/BS2优化采样点 |
错误帧持续出现 | 节点硬件故障 | 逐个断开节点定位 |
九、工程化建议
9.1 提升通信可靠性
// 错误处理增强代码
void CAN_ErrorCallback(CAN_HandleTypeDef *hcan) {uint32_t esr = hcan->Instance->ESR;if(esr & CAN_ESR_BOFF) {// 总线关闭状态,需软件恢复HAL_CAN_ResetErrorState(hcan);}if(esr & CAN_ESR_EPVF) {// 错误被动状态,建议重启通信HAL_CAN_Stop(hcan);CAN_Init();}
}
9.2 CANopen协议集成准备
// COB-ID分配示例
#define NMT_ID 0x000 // 网络管理帧
#define PDO1_TX_ID 0x181 // 过程数据对象
#define SDO_RX_ID 0x601 // 服务数据对象
十、附录:关键资源
-
官方文档:
- ISO11898-1:2015 CAN协议标准
- STM32F4xx参考手册
-
工具链:
- PCAN-View:官方CAN分析软件
- BusMaster:开源CAN总线工具