STM32 CAN通信 HAL库实战教程:从零到测试成功
STM32 CAN通信 HAL库实战教程:从零到测试成功
<我打印的是陀螺仪的数据>
目录
- 简介:为什么学习CAN通信
- CAN通信基础概念
- STM32 CAN硬件配置
- CAN初始化详解
- CAN数据发送实现
- CAN数据接收实现
- 测试与验证方法
- 常见问题与解决
- 总结与拓展
1. 简介:为什么学习CAN通信
在嵌入式系统中,CAN(Controller Area Network)总线是汽车电子、工业控制和物联网设备中不可或缺的通信协议。与UART、SPI等串行通信相比,CAN总线具有以下显著优势:
- 多主多从架构,支持多节点通信
- 内置错误检测和自动重传机制
- 高可靠性,适合嘈杂的工业环境
- 基于优先级的报文仲裁机制
本教程将基于STM32 HAL库,手把手教你实现CAN通信功能。我们不讲空洞的理论,直接上代码,目标是让你的开发板在1小时内实现可靠的CAN数据传输。
2. CAN通信基础概念
在深入代码之前,我们需要掌握几个关键概念:
2.1 CAN帧结构
CAN协议定义了两种帧格式:
- 标准帧(11位标识符)
- 扩展帧(29位标识符)
每帧数据包含:
- 标识符(决定数据优先级)
- 控制字段(包含数据长度码DLC)
- 数据字段(最多8字节)
- CRC校验字段
- 应答字段(接收节点在此确认收到)
2.2 CAN通信模式
- 正常模式:完整参与总线通信和错误检测
- 只听模式:接收数据但不参与总线活动
- 回环模式:发送的数据直接回传到接收缓冲区(用于自测试)
2.3 波特率计算
CAN波特率由以下参数决定:
- 时钟预分频(Prescaler)
- 同步跳变宽度(SJW)
- 时间段1(TimeSeg1,采样点前时间)
- 时间段2(TimeSeg2,采样点后时间)
公式:Tbit = (Prescaler × (TimeSeg1 + TimeSeg2 + 1)) / APB Clock
3. STM32 CAN硬件配置
3.1 CAN引脚配置
STM32F1系列的CAN引脚映射:
- CAN1_RX:PA11 或 PB8
- CAN1_TX:PA12 或 PB9
推荐使用PA11和PA12,因为它们属于低密度引脚,冲突可能性更低。
3.2 电路连接
基本连接要求:
- CAN_H和CAN_L需要通过终端电阻(通常120Ω)连接
- STM32的CAN引脚通过收发器芯片(如TJA1050)连接到CAN总线
- 电源部分需要做好滤波处理
3.3 外围设备配置
在STM32CubeMX中:
- 使能CAN1外设
- 配置CAN引脚为复用功能
- 设置CAN过滤器模式
4. CAN初始化详解
// CAN 初始化函数
void MyCAN_Init(void)
{// 1. 使能时钟__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_CAN1_CLK_ENABLE();// 2. 配置GPIO引脚GPIO_InitTypeDef GPIO_InitStruct = {0};// RX引脚配置 (PA11)GPIO_InitStruct.Pin = GPIO_PIN_11;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// TX引脚配置 (PA12)GPIO_InitStruct.Pin = GPIO_PIN_12;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 3. 配置CAN参数CAN_HandleTypeDef hcan;hcan.Instance = CAN1;hcan.Init.Prescaler = 48; // 时钟预分频hcan.Init.Mode = CAN_MODE_NORMAL;hcan.Init.SyncJumpWidth = CAN_SJW_2TQ;hcan.Init.TimeSeg1 = CAN_BS1_2TQ;hcan.Init.TimeSeg2 = CAN_BS2_3TQ;HAL_CAN_Init(&hcan);// 4. 配置过滤器CAN_FilterTypeDef canfilterconfig;canfilterconfig.FilterBank = 0;canfilterconfig.FilterIdHigh = 0x0000;canfilterconfig.FilterIdLow = 0x0000;canfilterconfig.FilterMaskIdHigh = 0x0000;canfilterconfig.FilterMaskIdLow = 0x0000;canfilterconfig.FilterMode = CAN_FILTERMODE_IDMASK;canfilterconfig.FilterScale = CAN_FILTERSCALE_32BIT;canfilterconfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;canfilterconfig.FilterActivation = CAN_FILTER_ENABLE;HAL_CAN_ConfigFilter(&hcan, &canfilterconfig);// 5. 启动CAN并启用中断HAL_CAN_Start(&hcan);HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING);
}
关键参数解释
- Prescaler:时钟预分频系数,决定位时钟周期
- SyncJumpWidth:同步跳变宽度,影响位定时同步
- TimeSeg1和TimeSeg2:决定采样点位置,通常TimeSeg1 + TimeSeg2 = 8TQ(时间量子)
5. CAN数据发送实现
void MyCAN_Transmit(uint32_t ID, uint8_t Length, uint8_t *Data)
{CAN_TxHeaderTypeDef TxHeader;uint32_t TxMailbox;// 配置发送帧头TxHeader.StdId = ID;TxHeader.ExtId = ID;TxHeader.IDE = CAN_ID_STD; // 标准帧TxHeader.RTR = CAN_RTR_DATA; // 数据帧TxHeader.DLC = Length;TxHeader.TransmitGlobalTime = DISABLE;// 发送数据并等待完成if (HAL_CAN_AddTxMessage(&hcan, &TxHeader, Data, &TxMailbox) != HAL_OK)return;// 等待发送完成或超时uint32_t Timeout = 0;while (HAL_CAN_IsTxMessagePending(&hcan, TxMailbox)){if (++Timeout > 100000)break;}
}
使用方法
uint8_t data[] = {0x11, 0x22, 0x33, 0x44};
MyCAN_Transmit(0x123, 4, data);
6. CAN数据接收实现
uint8_t MyCAN_ReceiveFlag(void)
{return HAL_CAN_GetRxFifoFillLevel(&hcan, CAN_RX_FIFO0) > 0;
}void MyCAN_Receive(uint32_t *ID, uint8_t *Length, uint8_t *Data)
{CAN_RxHeaderTypeDef RxHeader;if (HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, Data) != HAL_OK)return;// 解析标识符*ID = RxHeader.IDE == CAN_ID_STD ? RxHeader.StdId : RxHeader.ExtId;// 获取数据长度*Length = RxHeader.DLC;
}
使用方法
uint32_t rx_id;
uint8_t rx_len, rx_data[8];if (MyCAN_ReceiveFlag()) {MyCAN_Receive(&rx_id, &rx_len, rx_data);// 处理接收到的数据
}
7. 测试与验证方法
测试环境搭建
- 使用两块STM32开发板
- 通过CAN总线连接(确保终端电阻正确)
- 使用PCAN分析工具或串口调试工具监控通信
测试步骤
- 在主函数中调用MyCAN_Init()初始化CAN
- 使用MyCAN_Transmit()发送测试数据
- 在接收端使用MyCAN_ReceiveFlag()和MyCAN_Receive()接收数据
- 验证数据完整性、标识符和顺序
常见测试场景
- 单次发送接收测试
- 连续发送压力测试
- 不同波特率兼容性测试
- 异常帧处理测试
8. 常见问题与解决
问题1:无法接收到数据
- 检查引脚配置是否正确(注意复用功能是否使能)
- 确认波特率设置是否一致
- 检查过滤器配置是否允许接收该ID
- 确认CAN总线终端电阻是否连接正确
问题2:数据发送后无响应
- 检查Tx引脚是否配置为复用推挽输出
- 确认发送函数中TxMailbox是否正确
- 检查CAN初始化是否成功
- 使用CAN分析工具观察总线状态
问题3:接收数据错误
- 检查数据长度码(DLC)是否正确
- 确认接收缓冲区是否足够大
- 检查发送端数据是否正确填充
- 确认接收函数是否正确解析标识符
调试技巧
- 使用HAL_CAN_ActivateNotification()启用中断通知
- 实现CAN错误回调函数进行错误处理
- 使用逻辑分析仪观察CAN_H和CAN_L信号
9. 总结与拓展
通过本教程,我们完整实现了STM32基于HAL库的CAN通信功能。从硬件配置到软件实现,再到测试验证,我们掌握了CAN通信的核心技术。
拓展方向
- 实现基于CAN的自定义协议栈
- 开发CAN数据加密传输功能
- 设计CAN网络故障诊断系统
- 优化CAN通信的实时性能
希望本教程能帮助你在嵌入式开发中高效利用CAN总线技术。如果有任何问题,欢迎在评论区交流!
开发环境:STM32CubeIDE 1.10.0 | 目标芯片:STM32F103C8T6 | 波特率:500kbps (APB Clock = 72MHz)
通过实际项目验证的代码,直接复制到你的工程即可运行!