当前位置: 首页 > backend >正文

STM32 CAN通信 HAL库实战教程:从零到测试成功

STM32 CAN通信 HAL库实战教程:从零到测试成功

我打印的是陀螺仪的数据
<我打印的是陀螺仪的数据>

目录

  1. 简介:为什么学习CAN通信
  2. CAN通信基础概念
  3. STM32 CAN硬件配置
  4. CAN初始化详解
  5. CAN数据发送实现
  6. CAN数据接收实现
  7. 测试与验证方法
  8. 常见问题与解决
  9. 总结与拓展

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中:

  1. 使能CAN1外设
  2. 配置CAN引脚为复用功能
  3. 设置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. 测试与验证方法

测试环境搭建

  1. 使用两块STM32开发板
  2. 通过CAN总线连接(确保终端电阻正确)
  3. 使用PCAN分析工具或串口调试工具监控通信

测试步骤

  1. 在主函数中调用MyCAN_Init()初始化CAN
  2. 使用MyCAN_Transmit()发送测试数据
  3. 在接收端使用MyCAN_ReceiveFlag()和MyCAN_Receive()接收数据
  4. 验证数据完整性、标识符和顺序

常见测试场景

  • 单次发送接收测试
  • 连续发送压力测试
  • 不同波特率兼容性测试
  • 异常帧处理测试

8. 常见问题与解决

问题1:无法接收到数据

  • 检查引脚配置是否正确(注意复用功能是否使能)
  • 确认波特率设置是否一致
  • 检查过滤器配置是否允许接收该ID
  • 确认CAN总线终端电阻是否连接正确

问题2:数据发送后无响应

  • 检查Tx引脚是否配置为复用推挽输出
  • 确认发送函数中TxMailbox是否正确
  • 检查CAN初始化是否成功
  • 使用CAN分析工具观察总线状态

问题3:接收数据错误

  • 检查数据长度码(DLC)是否正确
  • 确认接收缓冲区是否足够大
  • 检查发送端数据是否正确填充
  • 确认接收函数是否正确解析标识符

调试技巧

  • 使用HAL_CAN_ActivateNotification()启用中断通知
  • 实现CAN错误回调函数进行错误处理
  • 使用逻辑分析仪观察CAN_H和CAN_L信号

9. 总结与拓展

通过本教程,我们完整实现了STM32基于HAL库的CAN通信功能。从硬件配置到软件实现,再到测试验证,我们掌握了CAN通信的核心技术。

拓展方向

  1. 实现基于CAN的自定义协议栈
  2. 开发CAN数据加密传输功能
  3. 设计CAN网络故障诊断系统
  4. 优化CAN通信的实时性能

希望本教程能帮助你在嵌入式开发中高效利用CAN总线技术。如果有任何问题,欢迎在评论区交流!

开发环境:STM32CubeIDE 1.10.0 | 目标芯片:STM32F103C8T6 | 波特率:500kbps (APB Clock = 72MHz)

通过实际项目验证的代码,直接复制到你的工程即可运行!

http://www.xdnf.cn/news/2526.html

相关文章:

  • 【计算机网络分类全解析】从局域网到广域网的工程实践
  • 【三大特性】虚表 内存分布
  • Marmoset Toolbag 5.0 中文汉化版 八猴软件中文汉化版 免费下载
  • C# 类(Class)教程
  • 浮点数:IEEE 754标准
  • PCIe 转 U.2 接双硬盘指南 - 超微(Supermicro)主板
  • Mysql如何高效的查询数据是否存在
  • 理解 Kubernetes 初始访问向量(一)——控制平面
  • 【Webpack \ Vite】多环境配置
  • makefile总结
  • 关于Spark知识点与代码测试的学习总结
  • 单片机 + 图像处理芯片 + TFT彩屏 复选框控件
  • 30-算法打卡-字符串-重复的子字符串-leetcode(459)-第三十天
  • 使用 Cherry Studio 调用高德 MCP 服务
  • NFS从零部署
  • 华为 CCE 查看节点剩余可调度cpu核数
  • 从零实现分布式WebSocket组件:设计模式深度实践指南
  • 路由协议基础
  • babel和loader的关系
  • 微深节能 平板小车运动监测与控制系统 格雷母线
  • LeetCode题解1297. 子串的最大出现次数
  • 低压电工常见知识点
  • 麒麟系统通过 Service 启动 JAR 包的完整指南
  • 【KWDB 创作者计划】_KWDB引领数据库技术革新的璀璨之星
  • 业务校验工具包-validate-utils介绍
  • spring-rabbit的CachingConnectionFactory默认参数导致消费者Channel数量暴增问题解决
  • go语言八股文(三)
  • Deep Dark Sea 局域網文件共享即時匿名聊天去數據庫部署
  • Ldap高效数据同步- MirrorMode双主复制模式配置详解(下)
  • spring项目rabbitmq es项目启动命令