STM32 HAL库实战:用CAN总线实现按键控制上位机通信(附完整工程)
STM32 HAL库实战:用CAN总线实现按键控制上位机通信(附完整工程)
CAN总线在工业控制、汽车电子等领域应用广泛,但对于初学者来说,如何快速上手CAN通信往往是个挑战。本文将带你从零开始,通过一个按键触发CAN通信的实战项目,掌握STM32 HAL库中CAN模块的核心用法。
1. 环境准备与硬件连接
在开始编码前,我们需要准备好开发环境和硬件。推荐使用正点原子精英开发板(STM32F103系列),它内置了CAN控制器和收发器,省去了外接CAN模块的麻烦。
所需硬件清单:
- STM32开发板(带CAN接口)
- USB转CAN适配器(用于连接上位机)
- 杜邦线若干
- 按键开关
- LED指示灯
硬件连接示意图:
| 开发板引脚 | 连接目标 | 备注 |
|---|---|---|
| CAN_H | USB-CAN适配器H | 使用双绞线连接更佳 |
| CAN_L | USB-CAN适配器L | 避免过长走线 |
| PE4 | 按键 | 下拉电阻10KΩ |
| PE5 | LED | 串联220Ω限流电阻 |
提示:如果使用其他型号开发板,请参考原理图确认CAN引脚位置,部分型号可能需要外接CAN收发器芯片如TJA1050。
2. CubeMX工程配置
STM32CubeMX是ST官方提供的图形化配置工具,能大幅简化外设初始化流程。以下是关键配置步骤:
2.1 时钟树配置
- 选择外部高速时钟(HSE)
- 设置系统时钟为72MHz
- CAN时钟源选择APB1(36MHz)
2.2 CAN外设配置
/* CAN初始化参数 */ 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; hcan.Init.TimeTriggeredMode = DISABLE; hcan.Init.AutoBusOff = DISABLE; hcan.Init.AutoWakeUp = DISABLE; hcan.Init.AutoRetransmission = DISABLE; hcan.Init.ReceiveFifoLocked = DISABLE; hcan.Init.TransmitFifoPriority = DISABLE;波特率计算公式:
CAN波特率 = APB1时钟 / (Prescaler * (TimeSeg1 + TimeSeg2 + 1)) = 36MHz / (9 * (5 + 2 + 1)) = 500Kbps2.3 GPIO配置
- PE4:输入模式,上拉(连接按键)
- PE5:输出模式,推挽(连接LED)
3. CAN通信核心代码实现
3.1 发送功能实现
首先定义发送数据结构和函数:
/* CAN发送报文头 */ CAN_TxHeaderTypeDef TxHeader; uint8_t TxData[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; void CAN_SendMessage(void) { uint32_t mailbox; TxHeader.StdId = 0x123; // 标准ID TxHeader.ExtId = 0x0000; // 扩展ID(标准帧时无效) TxHeader.IDE = CAN_ID_STD; // 使用标准帧 TxHeader.RTR = CAN_RTR_DATA; // 数据帧 TxHeader.DLC = 8; // 数据长度 if(HAL_CAN_AddTxMessage(&hcan, &TxHeader, TxData, &mailbox) != HAL_OK) { Error_Handler(); } }在main.c的while循环中添加按键检测:
while (1) { if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET) { HAL_Delay(50); // 消抖 if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET) { HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // LED状态翻转 CAN_SendMessage(); // 发送CAN数据 while(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET); // 等待按键释放 } } }3.2 接收功能实现
配置接收过滤器并启用中断:
void CAN_Filter_Config(void) { CAN_FilterTypeDef filter; filter.FilterBank = 0; filter.FilterMode = CAN_FILTERMODE_IDMASK; filter.FilterScale = CAN_FILTERSCALE_32BIT; filter.FilterIdHigh = 0x0000; filter.FilterIdLow = 0x0000; filter.FilterMaskIdHigh = 0x0000; filter.FilterMaskIdLow = 0x0000; // 接收所有报文 filter.FilterFIFOAssignment = CAN_FILTERFIFO0; filter.FilterActivation = ENABLE; HAL_CAN_ConfigFilter(&hcan, &filter); HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING); }实现接收回调函数:
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) { // 通过串口打印接收到的数据 char msg[50]; sprintf(msg, "ID:0x%03X, Data:%02X %02X %02X %02X %02X %02X %02X %02X\r\n", RxHeader.StdId, RxData[0], RxData[1], RxData[2], RxData[3], RxData[4], RxData[5], RxData[6], RxData[7]); HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY); } }4. 调试技巧与常见问题
4.1 常见错误排查
CAN通信失败
- 检查终端电阻:CAN总线两端需接120Ω终端电阻
- 确认波特率设置:确保所有节点波特率一致
- 检查硬件连接:CAN_H和CAN_L不能接反
接收不到数据
- 验证过滤器配置:过于严格的过滤会丢弃报文
- 检查中断优先级:CAN接收中断可能被其他高优先级中断阻塞
数据发送不成功
- 查看CAN控制器状态寄存器:
uint32_t status = HAL_CAN_GetError(&hcan); printf("CAN Error: 0x%lX\n", status);
- 查看CAN控制器状态寄存器:
4.2 上位机测试工具
推荐使用以下工具验证通信:
- CANTest:轻量级CAN调试工具
- CANalyzer:专业级分析工具(适合复杂场景)
- Python-can:基于Python的CAN库(适合自动化测试)
示例Python接收代码:
import can bus = can.interface.Bus(channel='COM3', bustype='slcan') while True: msg = bus.recv() print(f"ID:{msg.arbitration_id:X} Data:{msg.data.hex()}")5. 工程优化与扩展
5.1 提高通信可靠性
- 添加CRC校验:
uint32_t Calculate_CRC32(uint8_t *data, uint32_t length) { uint32_t crc = 0xFFFFFFFF; // CRC32计算实现... return crc ^ 0xFFFFFFFF; } - 实现超时重传机制
- 增加心跳包检测
5.2 多节点通信设计
当系统中有多个CAN节点时:
- 为每个设备分配唯一ID
- 设计合理的通信协议:
typedef struct { uint32_t id; uint8_t cmd; uint8_t len; uint8_t data[8]; uint16_t checksum; } CAN_Message; - 使用扩展帧(29位ID)满足更多节点需求
5.3 性能优化技巧
- 使用DMA传输减少CPU开销
- 合理设置发送邮箱优先级
- 启用自动重传功能(hcan.Init.AutoRetransmission = ENABLE)
完整工程已上传至GitHub仓库,包含:
- CubeMX工程文件
- Keil MDK工程
- 上位机测试脚本
- 详细说明文档
