1. CAN通信基础与STM32硬件准备
CAN总线是工业控制和车载通信中最常用的现场总线之一,它的多主架构和强抗干扰能力特别适合复杂电磁环境。STM32F1系列内置的bxCAN控制器支持CAN 2.0A/B协议,最高速率1Mbps。实际项目中我常用500Kbps的波特率,这个速率在20米总线长度下依然稳定。
硬件连接上需要注意三点:首先确认开发板的CAN收发器型号(如TJA1050),其次检查CANH/CANL终端电阻(通常需要120Ω),最后注意GPIO复用功能。以STM32F103C8T6为例,默认使用PA11(CAN_RX)和PA12(CAN_TX),但部分开发板可能不同。
2. CubeMX工程配置详解
新建工程时建议选择非中文路径,避免Keil编译异常。时钟配置是第一个关键点:
- 在Clock Configuration选项卡启用外部晶振(HSE)
- 确保APB1时钟为36MHz(CAN时钟源)
- 在Configuration选项卡的CAN设置中:
- 模式选择Normal
- 分频系数Prescaler设为9
- BS1=5,BS2=2(对应500Kbps)
- SJW保持默认1tq
中断配置同样重要,我习惯将CAN RX0中断优先级设为1(NVIC设置),这样能及时处理接收数据。工程生成时记得勾选"Generate peripheral initialization as a pair of .c/.h files",方便后续代码管理。
3. HAL库CAN初始化代码解析
CubeMX生成的can.c文件包含关键初始化逻辑:
CAN_FilterTypeDef sFilterConfig; hcan.Instance = CAN1; hcan.Init.Prescaler = 9; hcan.Init.Mode = CAN_MODE_NORMAL; hcan.Init.SyncJumpWidth = CAN_SJW_1TQ; hcan.Init.TimeSeg1 = CAN_BS1_5TQ; hcan.Init.TimeSeg2 = CAN_BS2_2TQ; /* 其他参数保持默认 */ if (HAL_CAN_Init(&hcan) != HAL_OK) { Error_Handler(); }滤波器配置是容易出错的地方。比如需要接收0x101和0x102两个标准ID时:
sFilterConfig.FilterBank = 0; sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; sFilterConfig.FilterIdHigh = 0x101 << 5; // STDID[10:0]左移5位 sFilterConfig.FilterIdLow = 0x102 << 5; sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);4. 数据收发实战代码
发送函数需要注意报文格式设置。下面是一个发送8字节数据的典型实现:
void CAN_SendData(uint8_t* data) { CAN_TxHeaderTypeDef txHeader; uint32_t txMailbox; txHeader.StdId = 0x201; // 标准ID txHeader.ExtId = 0; // 扩展ID未使用 txHeader.IDE = CAN_ID_STD; txHeader.RTR = CAN_RTR_DATA; txHeader.DLC = 8; // 数据长度 txHeader.TransmitGlobalTime = DISABLE; if(HAL_CAN_AddTxMessage(&hcan, &txHeader, data, &txMailbox) != HAL_OK) { // 错误处理 } }接收端采用中断方式更高效。在stm32f1xx_it.c中添加:
void USB_LP_CAN1_RX0_IRQHandler(void) { HAL_CAN_IRQHandler(&hcan); }然后在主文件中实现回调函数:
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { CAN_RxHeaderTypeDef rxHeader; uint8_t rxData[8]; if(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rxHeader, rxData) == HAL_OK) { // 处理接收到的数据 printf("Received ID: 0x%03X\n", rxHeader.StdId); } }5. 常见问题排查与优化
遇到通信失败时,建议按以下步骤排查:
- 用示波器检查CANH/CANL波形,正常应为差分信号
- 确认终端电阻连接正确
- 检查波特率配置(特别是BS1/BS2值)
- 测试环回模式(CubeMX中配置为Loopback)
性能优化方面,我有两个实用建议:
- 启用自动重传(hcan.Init.AutoRetransmission = ENABLE)
- 对于高频小数据包,可以减小BS1/BS2值提高速率,但要保证总线采样点在75%左右
调试时发现,如果滤波器配置不当会导致接收中断频繁触发但无有效数据。这时可以先用掩码模式接收所有ID(FilterMaskIdHigh/Low设为0),待通信正常后再细化过滤规则。