AHL微控制器实战:手把手教你用CAN总线实现两块开发板间的‘聊天’(附完整代码)

AHL微控制器实战:手把手教你用CAN总线实现两块开发板间的‘聊天’(附完整代码) AHL微控制器实战手把手教你用CAN总线实现两块开发板间的‘聊天’附完整代码CAN总线作为工业级通信协议在汽车电子和工业控制领域应用广泛。但对于嵌入式初学者来说协议手册里晦涩的帧格式、仲裁机制和错误处理往往让人望而生畏。本文将用两块AHL开发板搭建一个聊天室通过发送自定义消息的趣味项目带你真正理解CAN通信的底层原理和实战技巧。1. 硬件准备与电路连接1.1 所需材料清单AHL开发板 ×2型号建议AHL-STM32F103杜邦线若干建议使用不同颜色区分功能USB转TTL串口模块用于调试输出120Ω终端电阻 ×2注意虽然标准CAN网络需要收发器芯片如TJA1050但AHL开发板的CAN控制器支持回环测试模式可直接通过导线互联实现短距离通信。1.2 物理连接示意图开发板A 开发板B CAN_H ------------------- CAN_H CAN_L ------------------- CAN_L GND ------------------- GND关键接线细节使用双绞线连接CAN_H和CAN_L可用网线中的一对双绞线替代终端电阻并联在每块板的CAN_H与CAN_L之间确保共地连接GND互联2. 开发环境配置2.1 工具链安装AHL开发支持多种开发环境推荐组合# 安装ARM-GCC工具链Linux示例 sudo apt install gcc-arm-none-eabi # 安装OpenOCD调试工具 sudo apt install openocd # 安装VS Code插件 code --install-extension marus25.cortex-debug2.2 工程模板结构典型AHL项目目录应包含/Drivers # HAL库驱动 /Inc # 头文件 /Src # 源文件 /MDK-ARM # Keil工程文件 /SW4STM32 # SystemWorkbench工程3. CAN通信核心代码实现3.1 初始化配置// can.c #include ahl_can.h void CAN_Init(CAN_HandleTypeDef *hcan) { hcan-Instance CAN1; hcan-Init.Mode CAN_MODE_NORMAL; hcan-Init.AutoBusOff DISABLE; hcan-Init.AutoWakeUp DISABLE; hcan-Init.AutoRetransmission ENABLE; hcan-Init.ReceiveFifoLocked DISABLE; hcan-Init.TimeTriggeredMode DISABLE; hcan-Init.TransmitFifoPriority DISABLE; CAN_FilterTypeDef sFilterConfig; sFilterConfig.FilterBank 0; sFilterConfig.FilterMode CAN_FILTERMODE_IDMASK; sFilterConfig.FilterScale CAN_FILTERSCALE_32BIT; sFilterConfig.FilterIdHigh 0x0000; sFilterConfig.FilterIdLow 0x0000; sFilterConfig.FilterMaskIdHigh 0x0000; sFilterConfig.FilterMaskIdLow 0x0000; sFilterConfig.FilterFIFOAssignment CAN_RX_FIFO0; sFilterConfig.FilterActivation ENABLE; HAL_CAN_ConfigFilter(hcan, sFilterConfig); HAL_CAN_Start(hcan); HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); }3.2 消息发送函数uint8_t CAN_SendMessage(CAN_HandleTypeDef *hcan, uint32_t id, uint8_t* data) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailbox; TxHeader.StdId id; TxHeader.ExtId 0; TxHeader.RTR CAN_RTR_DATA; TxHeader.IDE CAN_ID_STD; TxHeader.DLC 8; TxHeader.TransmitGlobalTime DISABLE; if(HAL_CAN_AddTxMessage(hcan, TxHeader, data, TxMailbox) ! HAL_OK) { return 1; // 发送失败 } return 0; }3.3 接收中断处理// stm32f1xx_it.c void CAN1_RX0_IRQHandler(void) { CAN_RxHeaderTypeDef RxHeader; uint8_t RxData[8]; HAL_CAN_GetRxMessage(hcan1, CAN_RX_FIFO0, RxHeader, RxData); // 将接收到的数据通过串口打印 printf(Received from ID 0x%lx: %s\n, RxHeader.StdId, RxData); }4. 聊天功能实现技巧4.1 消息协议设计建议采用简单文本协议格式偏移量内容说明0T消息类型标识1-7ASCII字符实际消息内容示例代码实现void SendChatMessage(const char* name) { uint8_t msg[8] {T}; strncpy((char*)msg[1], name, 7); CAN_SendMessage(hcan1, 0x123, msg); }4.2 抗干扰措施总线错误处理void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) { uint32_t error HAL_CAN_GetError(hcan); if(error HAL_CAN_ERROR_BUS_OFF) { // 总线关闭恢复流程 HAL_CAN_ResetError(hcan); HAL_CAN_Start(hcan); } }超时重发机制# 伪代码示例 def send_with_retry(data, max_retry3): for i in range(max_retry): if can_send(data) SUCCESS: return True time.sleep(0.1) return False5. 调试与性能优化5.1 常见问题排查表现象可能原因解决方案无法接收到任何消息波特率不匹配检查双方CAN初始化参数一致性只能发送不能接收过滤器配置错误确认过滤器ID掩码设置通信距离短未接终端电阻在总线两端添加120Ω电阻数据包丢失总线负载过高降低发送频率或增大CAN波特率5.2 性能优化建议中断优先级配置HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 5, 0); HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);DMA传输优化// 在CAN初始化后添加 HAL_CAN_ActivateNotification(hcan1, CAN_IT_TX_MAILBOX_EMPTY);波特率计算工具def calc_can_baud(clock_hz, tq, bs1, bs2): prescaler clock_hz / (tq * (1 bs1 bs2)) return round(prescaler) # 示例36MHz时钟1Mbps波特率 print(calc_can_baud(36e6, 12, 5, 2)) # 输出26. 项目扩展方向6.1 多节点组网通过扩展ID过滤器实现多板通信void CAN_SetupMultiNodeFilter(uint16_t base_id) { CAN_FilterTypeDef filter; filter.FilterIdHigh (base_id 5) 0xFFFF; filter.FilterMaskIdHigh 0xFFE0; // 匹配高11位 // ...其他过滤器配置 HAL_CAN_ConfigFilter(hcan1, filter); }6.2 数据加密传输简单XOR加密示例void CAN_EncryptSend(uint8_t* data, uint8_t key) { for(int i0; i8; i) { data[i] ^ key; } CAN_SendMessage(hcan1, 0x123, data); }6.3 无线CAN中继利用ESP32实现无线转发// ESP32伪代码 void setup() { Serial2.begin(500000); // 连接CAN转串口模块 WiFi.begin(SSID, password); } void loop() { if(Serial2.available()) { String can_msg Serial2.readString(); // 通过MQTT转发到云端 mqttClient.publish(can/forward, can_msg.c_str()); } }在完成这个项目的过程中最让我印象深刻的是CAN总线的错误恢复机制。有一次故意短接CAN线路造成总线关闭看到系统自动恢复通信的过程才真正理解工业级协议的可靠性设计。建议初学者可以故意制造各种异常情况观察系统的反应机制——这比单纯看手册要直观得多。