文末有我笔记软件上的链接:有问题会实时更新
关于CAN与FDCAN:
目前来说在用的CAN主要分为 CAN2.0
和 CAN-FD
。
CAN2.0
数据帧一帧最大可以传输8字节,最高波特率为1000kbps。
CAN-FD
可以向下兼容 CAN2.0
。
CAN-FD
最大的变化在于数据段,一帧最大可以传输64字节,并且数据段波特率可以和其他部分不一样,最大可以到8000kbps。
很多情况下称 CAN2.0
为 bxCAN
。
1. STM32 CAN Controller(CAN控制器)-CAN2.0/bxCAN外设原理介绍:
介绍STM32 CAN外设:
STM32CAN外设的硬件组成,运行原理,实现方式
参考资料:
42. CAN—通讯实验 — [野火]STM32库开发实战指南——基于野火霸道开发板 文档
STM32H7 FDCAN兼容普通CAN使用 基于CubeMX配置_stm32cubeide h7配置fdcan作为普通can-优快云博客
秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4 CAN!_can协议的命令表怎么理解-优快云博客
STM32 CAN使用记录:bxCAN基础通讯_rtos stm32 can-优快云博客
关于STM32 CAN控制器说明:
STM32大多数型号均使用的是bxCAN这个IP核,该IP核工作非常稳定,以至于从STM32F1系列到STM32F7系列均使用此IP核。
下列两张图分别为STM32F1C8Tx系列和STM32F767IITx的CAN配置界面。可以看到配置界面的选项完全相同。
bxCAN的配置例程非常多,应用广泛。FDCAN是2011年博世发布的改进版CAN,FDCAN的性能固然强悍,但是在实际项目中若是多个板之间通信的情况,难免需要让FDCAN兼容CAN通信。本文皆在介绍如何通过CubeMX配置FDCAN使之兼容CAN使用,并讨论和实践FIFO深度参数的实际作用。
1.1. CAN控制器 - 介绍:
- STM32内置bxCAN外设 (Basic Extended CAN)(CAN控制器),支持CAN协议2.0A和2.0B标准
-
- 可以自动发送CAN报文和按照过滤器自动接收指定CAN报文,程序只需处理报文数据而无需关注总线的电平细节
- 可以自动地接收和发送 CAN 报文,支持使用 标准ID 和 扩展 ID 的报文;
- 波特率最高可达1兆位/秒 / 支持最高的通讯速率为 1Mb/s;
支持高速CAN
- 3个可配置优先级的发送邮箱
3个发送缓冲区,可存入3个待发送报文
(CAN控制器需要等待总线空闲再发送,但此时CPU写入多次,就要存多个发送邮箱)
-
- 发送报文的优先级可以使用软件控制,还可以记录发送的时间;
- 2个3级深度的接收FIFO
接收缓冲区:可缓存2*3个报文
-
- 可使用过滤功能只接收或不接收某些 ID 号的报文↓
- 14个过滤器组(互联型28个)
接收时过滤掉特定报文ID的报文,只接收想要的报文
- STM32 CAN中特色功能:
-
- 时间触发通信
- 自动离线恢复
- 自动唤醒
- 禁止自动重传
- 接收FIFO溢出处理方式可配置
- 发送优先级可配置
- 双CAN模式
- STM32CAN外设 不支持使用 DMA 进行数据收发
1.2. CAN控制器 - 结构框图:
STM32 的有两组 CAN 控制器,其中:
- CAN1 是主设备,框图中的“存储访问控制器”是由 CAN1控制的
- CAN2 无法直接访问存储区域
两者关系:
- 使用 CAN2 的时候必须使能 CAN1 外设的时钟
- 有CAN1和CAN2时,CAN2辅助CAN1工作,两者一起管理CAN总线
注意:
只有互联型设备才有CAN1和CAN2,一般只有CAN1;
只有CAN1的,框图中没有CAN2的部分
框图中主要包含 CAN 控制内核、发送邮箱、接收 FIFO 以及验收筛选器,下面对框图中的各个部分进行介绍。
框架示意图如下:
STM32 的有两组 CAN 控制器,其中 CAN1 是主设备,框图中的“存储访问控制器”是由 CAN1控制的,CAN2 无法直接访问存储区域,所以使用 CAN2 的时候必须使能 CAN1 外设的时钟。框图中主要包含 CAN 控制内核、发送邮箱、接收 FIFO 以及验收筛选器,下面对框图中的各个部分进行介绍。
1.3. CAN控制器 - 控制内核(寄存器)【后面有时间再研究】:
CAN 控制内核包含了各种控制寄存器及状态寄存器,我们主要讲解其中的主控制寄存器 CAN_MCR 及位时序寄存器 CAN_BTR
参考:
秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4 CAN!_can协议的命令表怎么理解-优快云博客
主控制寄存器 CAN_MCR
主控制寄存器 CAN_MCR 负责管理 CAN 的工作模式,它使用以下寄存器位实现控制。
(1) DBF 调试冻结功能
DBF(Debug freeze) 调试冻结,使用它可设置 CAN 处于工作状态或禁止收发的状态,禁止收发时仍可访问接收 FIFO 中的数据。这两种状态是当 STM32 芯片处于程序调试模式时才使用的,平时使用并不影响。
(2) TTCM 时间触发模式
TTCM(Time triggered communication mode) 时间触发模式,它用于配置 CAN 的时间触发通信模式,在此模式下,CAN 使用它内部定时器产生时间戳,并把它保存在CAN_RDTxR、CAN_TDTxR 寄存器中。内部定时器在每个 CAN 位时间累加,在接收和发送的帧起始位被采样,并生成时间戳。利用它可以实现 ISO 11898-4 CAN 标准的分时同步通信功能。
(3) ABOM 自动离线管理
ABOM (Automatic bus-off management) 自动离线管理,它用于设置是否使用自动离线管理功能。当节点检测到它发送错误或接收错误超过一定值时,会自动进入离线状态,在离线状态中, CAN 不能接收或发送报文。处于离线状态的时候,可以软件控制恢复或者直接使用这个自动离线管理功能,它会在适当的时候自动恢复。
(4) AWUM 自动唤醒
AWUM (Automatic bus-off management),自动唤醒功能,CAN 外设可以使用软件进入低功耗的睡眠模式,如果使能了这个自动唤醒功能,当 CAN 检测到总线活动的时候,会自动唤醒。
(5) NART 自动重传
NART(No automatic retransmission) 报文自动重传功能,设置这个功能后,当报文发送失败时会自动重传至成功为止。若不使用这个功能,无论发送结果如何,消息只发送一次。
(6) RFLM 锁定模式
RFLM(Receive FIFO locked mode)FIFO 锁定模式,该功能用于锁定接收 FIFO 。锁定后,当接收 FIFO 溢出时,会丢弃下一个接收的报文。若不锁定,则下一个接收到的报文会覆盖原报文。
(7) TXFP 报文发送优先级的判定方法
TXFP(Transmit FIFO priority) 报文发送优先级的判定方法,当 CAN 外设的发送邮箱中有多个待发送报文时,本功能可以控制它是根据报文的 ID 优先级还是报文存进邮箱的顺序来发送。
其他寄存器(待补充)
1.4. CAN控制器 - 基本结构:
1.4.1. 发送部分
- 可配置仲裁规则(发送优先级)
-
- 先来先占
- 按照ID大小
1.4.2. 接收部分
1.5. CAN控制器 - 发送流程:
1.5.1. 框图流程:
1.5.2. 寄存器流程:
基本流程:选择一个空置邮箱 → 写入报文 → 请求发送
其中一个发送邮箱的状态流程图:
发送邮箱状态:
基于不同寄存器真值表
空置状态
:
TME = 1,其他看图
挂号状态
:
-
- 当前邮箱数据准备完成,开始排队;
- 等到当前邮箱位最高优先级(仲裁规则),进入预定状态;
预定状态
:
-
- 此时如果若有更高优先级(ID仲裁规则)邮箱出现,回到挂号状态;
- 当CAN总线为空闲(IDLE)状态,进入发送状态(发送报文)
发送状态
:
-
- 发送完成,回到空置状态
- 发送失败,根据NART(禁止自动重传控制位)处理:
-
-
- 0:重发,回到预定状态
- 1:发送失败,回到空闲状态
-
寄存器介绍:
状态位:
RQCP
:请求完成标志位TXOK
:发送成功标志位
-
- 1:发送成功
TME
:发送邮箱空置状态标志位
-
- 1:当前邮箱为空置状态
配置位:
TXRQ
:发送请求控制位
-
- 1:产生发送请求
ABRQ
:中止发送控制位(手动改变该位,中止发送)
-
- 1:从 预定/挂号
NART
:禁止自动重传配置位
-
- 1:关闭自动重传,CAN报文只被发送1次,不管发送的结果如何(成功、出错或仲裁丢失)
直接回到空闲状态
-
- 0:自动重传,CAN硬件在发送报文失败时会一直自动重传直到发送成功
重发,回到预定状态
TXFP
:发送优先级配置位
-
- 1:优先级由发送请求的顺序来决定,先请求的先发送
- 0:优先级由报文标识符来决定,标识符值小的先发送(标识符值相等时,邮箱号小的报文先发送)
其实就是报文ID越小优先级越高
1.6. CAN控制器 - 接收流程:
1.6.1. 框图流程:
1.6.2. 寄存器流程:
基本流程:接收到一个报文 → 匹配过滤器后进入FIFO 0或FIFO 1 → CPU读取
其中一个FIFO的状态流程图:
FIFO状态:
基于不同寄存器真值表
空状态
:
FMP = 0x00
FOVR = 0,其他状态看图
挂号_n状态
:按照当前排队报文个数的状态
-
- n = FMP寄存器值
- 收到有效报文,n++;读取一个报文-释放邮箱,n--
寄存器介绍:
状态位:
FMP
:报文数目
-
- 0x01/01b:1个报文数
- 0x02/10b:2个报文数
- 0x03/11b:3个报文数
FOVR
:FIFO溢出标志位
-
- 0:没有溢出
配置位:
RFLM
:FIFO溢出处理策略配置位
-
- 1:接收FIFO锁定,FIFO溢出时,新收到的报文会被丢弃
- 0:禁用FIFO锁定,FIFO溢出时,FIFO中最后收到的报文被新报文覆盖
1.7. CAN控制器 - 接收过滤器 / 标识符(报文ID)过滤器:
14个接收过滤器
这里主要是讲解,怎么配置过滤器,才能过滤出我们关注的报文
CAN总线接收到的报文若符合过滤器中的配置,则是我们关注的报文;若不是则直接丢弃,不会被存储。
(0-13)每个过滤器所拥有的寄存器:
- 每个过滤器的核心
== 两个32位寄存器组成
== R1[31:0]和R2[31:0]
FSCx
:位宽设置
选择工作模式的位宽
-
- 置0,16位
- 置1,32位
FBMx
:模式设置
选择工作模式
-
- 置0,屏蔽模式
- 置1,列表模式
FFAx
:关联设置
当前过滤器得到的报文去往哪个FIFO
-
- 置0,FIFO 0
- 置1,FIFO 1
FACTx
:激活设置
使能当前过滤器(过滤器开关)
-
- 置0,禁用
- 置1,启用
过滤器 - 基础工作模式介绍:
注意:
每个过滤器 == 两个32位寄存器(R1+R2)
过滤器中的过滤 == 目标,过滤ID ==目标ID
屏蔽模式:部分匹配ID机制,给ID特征,符合的即可
例如:
温湿度模块(100个),需要在总线上传输100个温度数与100个湿度数据(200个报文ID)
温度报文(ID格式符合0x10x) 湿度报文(ID格式符合0x20x)
0x101 - 第一个模块温度数据 0x201 - 第一个模块湿度数据
0x102 - 第二个模块温度数据 0x202 - 第二个模块湿度数据
列表模式:完全匹配ID机制,给具体ID名单
例如:
0x121,0x221,即只要这两个ID的报文数据;其他都不要
- (位宽)32位和16位区别:
-
- 32位可存扩展/标准ID格式
- 16位只能存标准ID格式
每个过滤器的四种工作模式:
32位屏蔽模式
:
-
- 最大目标ID数:最多过滤两个(标准/扩展)ID
32位列表模式
:
最简单直接
-
- 适用:既存在标准/扩展ID,或者,都为扩展ID的情况
- 组成:两个32位过滤器
- 最大目标ID数:最多过滤两个(标准/扩展)ID
16位屏蔽模式
:
16位列表模式
:
32位列表模式下,如果只过滤标准ID格式(14位),那将浪费另外2*18位空间
-
- 适用:只过滤标准ID的情况
- 组成:四个16位过滤器
- 最大目标ID数:最多过滤四个标准ID
注意:写入寄存器记得<<xxx位
过滤器配置实例
总线上存在的ID | 想要接收的ID | 过滤器模式 | R1[31:0]配置值 | R2[31:0]配置值 |
0x123, 0x234, 0x345, 0x456, 0x567, 0x678 | 0x234, 0x345, 0x567 | 16位/列表 | ID: R1[15:0]=0x234<<5 ID: R1[31:16]=0x345<<5 | ID: R2[15:0]=0x567<<5 ID: R2[31:16]=0x000<<5 |
0x100~0x1FF, 0x200~0x2FF, 0x310~0x31F, 0x320~0x32F | 0x200~0x2FF, 0x320~0x32F | 16位/屏蔽 | ID: R1[15:0]=0x200<<5 Mask: R1[31:16]= (0x700<<5)|0x10|0x8 | ID: R2[15:0]=0x320<<5 Mask: R2[31:16]= (0x7F0<<5)|0x10|0x8 |
0x123, 0x234, 0x345, 0x456, 0x12345678, 0x0789ABCD | 0x123, 0x12345678 | 32位/列表 | ID: R1[31:0]=0x123<<21 | ID: R2[31:0]=(0x12345678<<3)|0x4 |
0x12345600~0x123456FF, 0x0789AB00~0x0789ABFF | 0x12345600~0x123456FF 0x12345600 = 00010010001101000101011000000 000 | 32位/屏蔽 | ID: R1[31:0]=(0x12345600<<3)|0x4 00010010001101000101011000000 000 + 扩展格式100 = 00010010001101000101011000000 100 | Mask: R2[31:0]=(0x1FFFFF00<<3)|0x4|0x2 00011111111111111111111100000 000 + 必须是扩展ID 100 + 必须是数据帧 010 = 00011111111111111111111100000 110 |
任意ID | 只要遥控帧 | 32位/屏蔽 | ID: R1[31:0]=0x2 | Mask: R2[31:0]=0x2 |
任意ID | 所有帧 | 32位/屏蔽 | ID: R1[31:0]=随意 | Mask: R2[31:0]=0 |
1.8. CAN控制器 - 测试模式(非正常工作模式):
通过代码实现【软件控制】bxCAN工作模式为测试模式
- 静默模式
- 环回模式
- 环回静默模式
1.9. CAN控制器 - 工作模式:
软件与硬件共同控制bxCAN外设的工作模式
- 初始化模式
- 正常模式
- 睡眠模式
唤醒措施 - 配置寄存器:
- AWUM:1,自动唤醒;0,手动唤醒(软件)
其他寄存器介绍:
- INRQ:请求进入初始化
- ...
1.10. CAN控制器 - 位时序特性(时序/波特率/位时间计算):
32 CAN外设中的位时序有些区别于CAN协议的位时序,特此补充
CAN协议中的位时序
STM32 CAN控制器外设中的位时序
CAN协议 - 位时序
- SS
- PTS
- PBS1
- PBS2
32 CAN - 位时序:
- SS(SYNC_SEG)
- BS1【与CAN协议的唯一区别】
==CAN协议位时序的PTS+PBS1
因为考虑到PTS与PBS1之间没有操作,32直接合并时间段
- BS2
1.11. CAN控制器 - 中断:
STM32 CAN外设中断向量共有四个:
- 发送中断
- FIFO 0接收/溢出中断
- FIFO 1接收/溢出中断
- 状态改变错误中断
1.12. CAN控制器 - 时间触发通信:
同步时间戳——扩展功能
- 实现:数据帧-时间戳的发送和接收
- 功能:广播时间戳到总线上,各设备接收后,分析时间戳,方便各设备的CAN时序管理【同步CAN设备时间戳】
- 相关寄存器:
-
- TTCM:使能时间触发通信功能
- CAN外设内置一个16位计数器:记录时间戳
- TIME[15:0]:
-
-
- 发送时捕获“记录时间戳寄存器”的值,写到
-
-
-
-
- TIME寄存器
- 数据帧数据段的最后两个字节(必须是第7、8位,所以DLC=8)
-
-
-
-
- 接收时捕获“记录时间戳寄存器”的值,写到接收FIFO的TIME寄存器
-
接收FIFO的TIME寄存器,可能会与“记录时间戳寄存器”的值比对,优化当前CAN时序/时间戳
1.13. CAN控制器 - 错误处理和离线恢复:
32CAN外设的错误处理 与 CAN协议的定义有些不同
CAN协议中的错误处理
STM32 CAN控制器外设中的错误处理
错误计数器
32中设计的特殊功能【区别之处】
- ABOM寄存器:
-
- 置1,开启离线自动恢复
-
-
- 进入离线状态后,就自动开启恢复过程
-
-
- 置0,关闭离线自动恢复
-
-
- 软件必须先请求进入然后再退出初始化模式
-
IRQN置1再清0
-
-
- 随后恢复过程才被开启
-
此时,再检测128此11个隐性(电平1)位,才重新开启CAN设备
2. STM32 CAN Controller(CAN控制器)-CAN2.0/bxCAN外设开发:
参考资料统一写在原理部分
2.1. STM32不同系列的CAN资源:
芯片型号 | CAN外设资源 |
STM32F103C8T6 | CAN1 |
STM32F407ZET6 | |
STM32H7VGT6 | |
2.2. STM32中的CAN硬件部分:
主要是接线
2.2.1. CAN网拓扑结构:
CAN设备挂载到总线上
2.2.2. CAN收发器电路:
STM32-CAN连接外围电路部分:
CAN控制器与CAN电平转换芯片接线
TXD-TXD
RXD-RXD
其他都一样
2.3. STM32的CAN/FDCAN波特率计算器使用:
【完整资料包】CAN分析仪资料20240911.rar - 珠海创芯科技
CAN FD位时序计算器|计算建立可靠CAN总线系统所需的总线时序参数
目的是得到BS1,BS2,分频系数(Prescaler),手动配置波特率和采集点位置
注意:计算出来的波特率配置参数不唯一!!!可能存在很多组
2.3.1. 常用CAN波特率配置项
2.3.2. 使用方法:
例子1:
例子2:
2.4. 基于标准库 - 开发CAN外设:
可参考江协科技
2.5. 基于HAL库 - 开发CAN外设:
秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4 CAN!_程序获取 can协议所有id-优快云博客
STM32(HAL)——CAN通信_stm32 hal can-优快云博客
STM32 HAL库 CAN2.0配置_hal库配置can口通讯-优快云博客
42. CAN—通讯实验 — [野火]STM32库开发实战指南——基于野火霸道开发板 文档
STM32HAL库学习——CAN笔记_stm32 hal can-优快云博客
2.5.1. CAN的库分析:
注意:
- 使用HAL库开发CAN时,旧版本的库与最新的库还是差异挺大的!
- 从 STM32 的 CAN 外设我们了解到它的功能非常多,控制涉及的寄存器也非常丰富,而使用STM32 HAL 库提供的各种结构体及库函数可以简化这些控制过程。
- 跟其它外设一样,STM32HAL 库提供了 CAN 初始化结构体及初始化函数来控制 CAN 的工作方式,提供了收发报文使用的结构体及收发函数,还有配置控制筛选器模式及 ID 的结构体。这些内容都定义在库文件“STM32F4xx_hal_can.h”及“STM32F4xx_hal_can.c”中,编程时我们可以结合这两个文件内的注释使用或参考库帮助文档。
宏定义分析:
/* Exported constants --------------------------------------------------------*/
/** @defgroup CAN_Exported_Constants CAN Exported Constants
* @{
*/
/** @defgroup CAN_Error_Code CAN Error Code
* @{
*/
#define HAL_CAN_ERROR_NONE (0x00000000U) /*!< No error */
#define HAL_CAN_ERROR_EWG (0x00000001U) /*!< Protocol Error Warning */
#define HAL_CAN_ERROR_EPV (0x00000002U) /*!< Error Passive */
#define HAL_CAN_ERROR_BOF (0x00000004U) /*!< Bus-off error */
#define HAL_CAN_ERROR_STF (0x00000008U) /*!< Stuff error */
#define HAL_CAN_ERROR_FOR (0x00000010U) /*!< Form error */
#define HAL_CAN_ERROR_ACK (0x00000020U) /*!< Acknowledgment error */
#define HAL_CAN_ERROR_BR (0x00000040U) /*!< Bit recessive error */
#define HAL_CAN_ERROR_BD (0x00000080U) /*!< Bit dominant error */
#define HAL_CAN_ERROR_CRC (0x00000100U) /*!< CRC error */
#define HAL_CAN_ERROR_RX_FOV0 (0x00000200U) /*!< Rx FIFO0 overrun error */
#define HAL_CAN_ERROR_RX_FOV1 (0x00000400U) /*!< Rx FIFO1 overrun error */
#define HAL_CAN_ERROR_TX_ALST0 (0x00000800U) /*!< TxMailbox 0 transmit failure due to arbitration lost */
#define HAL_CAN_ERROR_TX_TERR0 (0x00001000U) /*!< TxMailbox 0 transmit failure due to transmit error */
#define HAL_CAN_ERROR_TX_ALST1 (0x00002000U) /*!< TxMailbox 1 transmit failure due to arbitration lost */
#define HAL_CAN_ERROR_TX_TERR1 (0x00004000U) /*!< TxMailbox 1 transmit failure due to transmit error */
#define HAL_CAN_ERROR_TX_ALST2 (0x00008000U) /*!< TxMailbox 2 transmit failure due to arbitration lost */
#define HAL_CAN_ERROR_TX_TERR2 (0x00010000U) /*!< TxMailbox 2 transmit failure due to transmit error */
#define HAL_CAN_ERROR_TIMEOUT (0x00020000U) /*!< Timeout error */
#define HAL_CAN_ERROR_NOT_INITIALIZED (0x00040000U) /*!< Peripheral not initialized */
#define HAL_CAN_ERROR_NOT_READY (0x00080000U) /*!< Peripheral not ready */
#define HAL_CAN_ERROR_NOT_STARTED (0x00100000U) /*!< Peripheral not started */
#define HAL_CAN_ERROR_PARAM (0x00200000U) /*!< Parameter error */
#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
#define HAL_CAN_ERROR_INVALID_CALLBACK (0x00400000U) /*!< Invalid Callback error */
#endif /* USE_HAL_CAN_REGISTER_CALLBACKS */
#define HAL_CAN_ERROR_INTERNAL (0x00800000U) /*!< Internal error */
/**
* @}
*/
/** @defgroup CAN_InitStatus CAN InitStatus
* @{
*/
#define CAN_INITSTATUS_FAILED (0x00000000U) /*!< CAN initialization failed */
#define CAN_INITSTATUS_SUCCESS (0x00000001U) /*!< CAN initialization OK */
/**
* @}
*/
/** @defgroup CAN_operating_mode CAN Operating Mode
* @{
*/
#define CAN_MODE_NORMAL (0x00000000U) /*!< Normal mode */
#define CAN_MODE_LOOPBACK ((uint32_t)CAN_BTR_LBKM) /*!< Loopback mode */
#define CAN_MODE_SILENT ((uint32_t)CAN_BTR_SILM) /*!< Silent mode */
#define CAN_MODE_SILENT_LOOPBACK ((uint32_t)(CAN_BTR_LBKM | CAN_BTR_SILM)) /*!< Loopback combined with
silent mode */
/**
* @}
*/
/** @defgroup CAN_synchronisation_jump_width CAN Synchronization Jump Width
* @{
*/
#define CAN_SJW_1TQ (0x00000000U) /*!< 1 time quantum */
#define CAN_SJW_2TQ ((uint32_t)CAN_BTR_SJW_0) /*!< 2 time quantum */
#define CAN_SJW_3TQ ((uint32_t)CAN_BTR_SJW_1) /*!< 3 time quantum */
#define CAN_SJW_4TQ ((uint32_t)CAN_BTR_SJW) /*!< 4 time quantum */
/**
* @}
*/
/** @defgroup CAN_time_quantum_in_bit_segment_1 CAN Time Quantum in Bit Segment 1
* @{
*/
#define CAN_BS1_1TQ (0x00000000U) /*!< 1 time quantum */
#define CAN_BS1_2TQ ((uint32_t)CAN_BTR_TS1_0) /*!< 2 time quantum */
#define CAN_BS1_3TQ ((uint32_t)CAN_BTR_TS1_1) /*!< 3 time quantum */
#define CAN_BS1_4TQ ((uint32_t)(CAN_BTR_TS1_1 | CAN_BTR_TS1_0)) /*!< 4 time quantum */
#define CAN_BS1_5TQ ((uint32_t)CAN_BTR_TS1_2) /*!< 5 time quantum */
#define CAN_BS1_6TQ ((uint32_t)(CAN_BTR_TS1_2 | CAN_BTR_TS1_0)) /*!< 6 time quantum */
#define CAN_BS1_7TQ ((uint32_t)(CAN_BTR_TS1_2 | CAN_BTR_TS1_1)) /*!< 7 time quantum */
#define CAN_BS1_8TQ ((uint32_t)(CAN_BTR_TS1_2 | CAN_BTR_TS1_1 | CAN_BTR_TS1_0)) /*!< 8 time quantum */
#define CAN_BS1_9TQ ((uint32_t)CAN_BTR_TS1_3) /*!< 9 time quantum */
#define CAN_BS1_10TQ ((uint32_t)(CAN_BTR_TS1_3 | CAN_BTR_TS1_0)) /*!< 10 time quantum */
#define CAN_BS1_11TQ ((uint32_t)(CAN_BTR_TS1_3 | CAN_BTR_TS1_1)) /*!< 11 time quantum */
#define CAN_BS1_12TQ ((uint32_t)(CAN_BTR_TS1_3 | CAN_BTR_TS1_1 | CAN_BTR_TS1_0)) /*!< 12 time quantum */
#define CAN_BS1_13TQ ((uint32_t)(CAN_BTR_TS1_3 | CAN_BTR_TS1_2)) /*!< 13 time quantum */
#define CAN_BS1_14TQ ((uint32_t)(CAN_BTR_TS1_3 | CAN_BTR_TS1_2 | CAN_BTR_TS1_0)) /*!< 14 time quantum */
#define CAN_BS1_15TQ ((uint32_t)(CAN_BTR_TS1_3 | CAN_BTR_TS1_2 | CAN_BTR_TS1_1)) /*!< 15 time quantum */
#define CAN_BS1_16TQ ((uint32_t)CAN_BTR_TS1) /*!< 16 time quantum */
/**
* @}
*/
/** @defgroup CAN_time_quantum_in_bit_segment_2 CAN Time Quantum in Bit Segment 2
* @{
*/
#define CAN_BS2_1TQ (0x00000000U) /*!< 1 time quantum */
#define CAN_BS2_2TQ ((uint32_t)CAN_BTR_TS2_0) /*!< 2 time quantum */
#define CAN_BS2_3TQ ((uint32_t)CAN_BTR_TS2_1) /*!< 3 time quantum */
#define CAN_BS2_4TQ ((uint32_t)(CAN_BTR_TS2_1 | CAN_BTR_TS2_0)) /*!< 4 time quantum */
#define CAN_BS2_5TQ ((uint32_t)CAN_BTR_TS2_2) /*!< 5 time quantum */
#define CAN_BS2_6TQ ((uint32_t)(CAN_BTR_TS2_2 | CAN_BTR_TS2_0)) /*!< 6 time quantum */
#define CAN_BS2_7TQ ((uint32_t)(CAN_BTR_TS2_2 | CAN_BTR_TS2_1)) /*!< 7 time quantum */
#define CAN_BS2_8TQ ((uint32_t)CAN_BTR_TS2) /*!< 8 time quantum */
/**
* @}
*/
/** @defgroup CAN_filter_mode CAN Filter Mode
* @{
*/
#define CAN_FILTERMODE_IDMASK (0x00000000U) /*!< Identifier mask mode */
#define CAN_FILTERMODE_IDLIST (0x00000001U) /*!< Identifier list mode */
/**
* @}
*/
/** @defgroup CAN_filter_scale CAN Filter Scale
* @{
*/
#define CAN_FILTERSCALE_16BIT (0x00000000U) /*!< Two 16-bit filters */
#define CAN_FILTERSCALE_32BIT (0x00000001U) /*!< One 32-bit filter */
/**
* @}
*/
/** @defgroup CAN_filter_activation CAN Filter Activation
* @{
*/
#define CAN_FILTER_DISABLE (0x00000000U) /*!< Disable filter */
#define CAN_FILTER_ENABLE (0x00000001U) /*!< Enable filter */
/**
* @}
*/
/** @defgroup CAN_filter_FIFO CAN Filter FIFO
* @{
*/
#define CAN_FILTER_FIFO0 (0x00000000U) /*!< Filter FIFO 0 assignment for filter x */
#define CAN_FILTER_FIFO1 (0x00000001U) /*!< Filter FIFO 1 assignment for filter x */
/**
* @}
*/
/** @defgroup CAN_identifier_type CAN Identifier Type
* @{
*/
#define CAN_ID_STD (0x00000000U) /*!< Standard Id */
#define CAN_ID_EXT (0x00000004U) /*!< Extended Id */
/**
* @}
*/
/** @defgroup CAN_remote_transmission_request CAN Remote Transmission Request
* @{
*/
#define CAN_RTR_DATA (0x00000000U) /*!< Data frame */
#define CAN_RTR_REMOTE (0x00000002U) /*!< Remote frame */
/**
* @}
*/
/** @defgroup CAN_receive_FIFO_number CAN Receive FIFO Number
* @{
*/
#define CAN_RX_FIFO0 (0x00000000U) /*!< CAN receive FIFO 0 */
#define CAN_RX_FIFO1 (0x00000001U) /*!< CAN receive FIFO 1 */
/**
* @}
*/
/** @defgroup CAN_Tx_Mailboxes CAN Tx Mailboxes
* @{
*/
#define CAN_TX_MAILBOX0 (0x00000001U) /*!< Tx Mailbox 0 */
#define CAN_TX_MAILBOX1 (0x00000002U) /*!< Tx Mailbox 1 */
#define CAN_TX_MAILBOX2 (0x00000004U) /*!< Tx Mailbox 2 */
/**
* @}
*/
/** @defgroup CAN_flags CAN Flags
* @{
*/
/* Transmit Flags */
#define CAN_FLAG_RQCP0 (0x00000500U) /*!< Request complete MailBox 0 flag */
#define CAN_FLAG_TXOK0 (0x00000501U) /*!< Transmission OK MailBox 0 flag */
#define CAN_FLAG_ALST0 (0x00000502U) /*!< Arbitration Lost MailBox 0 flag */
#define CAN_FLAG_TERR0 (0x00000503U) /*!< Transmission error MailBox 0 flag */
#define CAN_FLAG_RQCP1 (0x00000508U) /*!< Request complete MailBox1 flag */
#define CAN_FLAG_TXOK1 (0x00000509U) /*!< Transmission OK MailBox 1 flag */
#define CAN_FLAG_ALST1 (0x0000050AU) /*!< Arbitration Lost MailBox 1 flag */
#define CAN_FLAG_TERR1 (0x0000050BU) /*!< Transmission error MailBox 1 flag */
#define CAN_FLAG_RQCP2 (0x00000510U) /*!< Request complete MailBox2 flag */
#define CAN_FLAG_TXOK2 (0x00000511U) /*!< Transmission OK MailBox 2 flag */
#define CAN_FLAG_ALST2 (0x00000512U) /*!< Arbitration Lost MailBox 2 flag */
#define CAN_FLAG_TERR2 (0x00000513U) /*!< Transmission error MailBox 2 flag */
#define CAN_FLAG_TME0 (0x0000051AU) /*!< Transmit mailbox 0 empty flag */
#define CAN_FLAG_TME1 (0x0000051BU) /*!< Transmit mailbox 1 empty flag */
#define CAN_FLAG_TME2 (0x0000051CU) /*!< Transmit mailbox 2 empty flag */
#define CAN_FLAG_LOW0 (0x0000051DU) /*!< Lowest priority mailbox 0 flag */
#define CAN_FLAG_LOW1 (0x0000051EU) /*!< Lowest priority mailbox 1 flag */
#define CAN_FLAG_LOW2 (0x0000051FU) /*!< Lowest priority mailbox 2 flag */
/* Receive Flags */
#define CAN_FLAG_FF0 (0x00000203U) /*!< RX FIFO 0 Full flag */
#define CAN_FLAG_FOV0 (0x00000204U) /*!< RX FIFO 0 Overrun flag */
#define CAN_FLAG_FF1 (0x00000403U) /*!< RX FIFO 1 Full flag */
#define CAN_FLAG_FOV1 (0x00000404U) /*!< RX FIFO 1 Overrun flag */
/* Operating Mode Flags */
#define CAN_FLAG_INAK (0x00000100U) /*!< Initialization acknowledge flag */
#define CAN_FLAG_SLAK (0x00000101U) /*!< Sleep acknowledge flag */
#define CAN_FLAG_ERRI (0x00000102U) /*!< Error flag */
#define CAN_FLAG_WKU (0x00000103U) /*!< Wake up interrupt flag */
#define CAN_FLAG_SLAKI (0x00000104U) /*!< Sleep acknowledge interrupt flag */
/* Error Flags */
#define CAN_FLAG_EWG (0x00000300U) /*!< Error warning flag */
#define CAN_FLAG_EPV (0x00000301U) /*!< Error passive flag */
#define CAN_FLAG_BOF (0x00000302U) /*!< Bus-Off flag */
/**
* @}
*/
/** @defgroup CAN_Interrupts CAN Interrupts
* @{
*/
/* Transmit Interrupt */
#define CAN_IT_TX_MAILBOX_EMPTY ((uint32_t)CAN_IER_TMEIE) /*!< Transmit mailbox empty interrupt */
/* Receive Interrupts */
#define CAN_IT_RX_FIFO0_MSG_PENDING ((uint32_t)CAN_IER_FMPIE0) /*!< FIFO 0 message pending interrupt */
#define CAN_IT_RX_FIFO0_FULL ((uint32_t)CAN_IER_FFIE0) /*!< FIFO 0 full interrupt */
#define CAN_IT_RX_FIFO0_OVERRUN ((uint32_t)CAN_IER_FOVIE0) /*!< FIFO 0 overrun interrupt */
#define CAN_IT_RX_FIFO1_MSG_PENDING ((uint32_t)CAN_IER_FMPIE1) /*!< FIFO 1 message pending interrupt */
#define CAN_IT_RX_FIFO1_FULL ((uint32_t)CAN_IER_FFIE1) /*!< FIFO 1 full interrupt */
#define CAN_IT_RX_FIFO1_OVERRUN ((uint32_t)CAN_IER_FOVIE1) /*!< FIFO 1 overrun interrupt */
/* Operating Mode Interrupts */
#define CAN_IT_WAKEUP ((uint32_t)CAN_IER_WKUIE) /*!< Wake-up interrupt */
#define CAN_IT_SLEEP_ACK ((uint32_t)CAN_IER_SLKIE) /*!< Sleep acknowledge interrupt */
/* Error Interrupts */
#define CAN_IT_ERROR_WARNING ((uint32_t)CAN_IER_EWGIE) /*!< Error warning interrupt */
#define CAN_IT_ERROR_PASSIVE ((uint32_t)CAN_IER_EPVIE) /*!< Error passive interrupt */
#define CAN_IT_BUSOFF ((uint32_t)CAN_IER_BOFIE) /*!< Bus-off interrupt */
#define CAN_IT_LAST_ERROR_CODE ((uint32_t)CAN_IER_LECIE) /*!< Last error code interrupt */
#define CAN_IT_ERROR ((uint32_t)CAN_IER_ERRIE) /*!< Error Interrupt */
结构体分析:
CAN初始化结构体 - CAN_InitTypeDef
:
typedef struct
{
uint32_t Prescaler; /* 配置 CAN 外设的时钟分频,可设置为 1-1024*/
uint32_t Mode; /* 配置 CAN 的工作模式,回环或正常模式 */
uint32_t SyncJumpWidth; /* 配置 SJW 极限值 */
uint32_t TimeSeg1; /* 配置 BS1 段长度 */
uint32_t TimeSeg2; /* 配置 BS2 段长度 */
FunctionalState TimeTriggeredMode; /* 是否使能 TTCM 时间触发功能 */
FunctionalState AutoBusOff; /* 是否使能 ABOM 自动离线管理功能 */
FunctionalState AutoWakeUp; /* 是否使能 AWUM 自动唤醒功能 */
FunctionalState AutoRetransmission; /* 是否使能 NART 自动重传功能 */
FunctionalState ReceiveFifoLocked; /* 是否使能 RFLM 锁定 FIFO 功能 */
FunctionalState TransmitFifoPriority; /* 配置 TXFP 报文优先级的判定方法 */
}
CAN_InitTypeDef; /* CAN初始化结构体 */
使用示例:
/**
* @brief 初始化CAN总线
* @param hcan CAN编号
* @param Callback_Function 处理回调函数
*/
void CAN_Init(CAN_HandleTypeDef *hcan)
{
HAL_CAN_Start(hcan);
__HAL_CAN_ENABLE_IT(hcan, CAN_IT_RX_FIFO0_MSG_PENDING);
__HAL_CAN_ENABLE_IT(hcan, CAN_IT_RX_FIFO1_MSG_PENDING);
}
其中括号内的文字是对应参数在 STM32 HAL 库中定义的宏
CAN初始化结构体 - 成员变量分析:
- Prescaler
本成员设置 CAN 外设的时钟分频,它可控制时间片 Tq 的时间长度,这里设置的值最终会减 1 后再写入 BRP 寄存器位,即前面介绍的 Tq 计算公式:
- Tq = (BRP[9:0]+1) x TPCLK
- 等效于:Tq = CAN_Prescaler x TPCLK
- Mode
本成员设置 CAN 的工作模式,可设置为正常模式 (CAN_MODE_NORMAL)、回环模式 (CAN_MODE_LOOPBACK)、静默模式 (CAN_MODE_SILENT) 以及回环静默模式(CAN_MODE_SILENT_LOOPBACK)。
- SyncJumpWidth
本成员可以配置 SJW 的极限长度,即 CAN 重新同步时单次可增加或缩短的最大长度,它可以被配置为 1-4Tq(CAN_SJW_1/2/3/4tq)。
- TimeSeg1
本成员用于设置 CAN 位时序中的 BS1 段的长度,它可以被配置为 1-16 个 Tq 长度(CAN_BS1_1/2/3…16tq)。
- TimeSeg2
本成员用于设置 CAN 位时序中的 BS2 段的长度,它可以被配置为 1-8 个 Tq 长度(CAN_BS2_1/2/3…8tq)。SYNC_SEG、 BS1 段及 BS2 段的长度加起来即一个数据位的长度,即前面介绍的原来
- 计算公式:T1bit =1Tq+TS1+TS2=1+ (TS1[3:0] + 1)+ (TS2[2:0] + 1)
- 等效于:T1bit= 1Tq+CAN_BS1+CAN_BS2
- TimeTriggeredMode
本成员用于设置是否使用时间触发功能 (ENABLE/DISABLE),时间触发功能在某些CAN 标准中会使用到。
- AutoBusOff
本成员用于设置是否使用自动离线管理 (ENABLE/DISABLE),使用自动离线管理可以在节点出错离线后适时自动恢复,不需要软件干预。
- AutoWakeUp
本成员用于设置是否使用自动唤醒功能 (ENABLE/DISABLE),使能自动唤醒功能后它会在监测到总线活动后自动唤醒。
- AutoRetransmission
本成员用于设置是否使用自动重传功能 (ENABLE/DISABLE),使用自动重传功能时,会一直发送报文直到成功为止。
- ReceiveFifoLocked
本成员用于设置是否使用锁定接收 FIFO(ENABLE/DISABLE),锁定接收 FIFO 后,若FIFO 溢出时会丢弃新数据,否则在 FIFO 溢出时以新数据覆盖旧数据。
- TransmitFifoPriority
本成员用于设置发送报文的优先级判定方法 (ENABLE/DISABLE),使能时,以报文存入发送邮箱的先后顺序来发送,否则按照报文 ID 的优先级来发送。配置完这些结构体成员后,我们调用库函数 HAL_CAN_Init 即可把这些参数写入到 CAN 控制寄存器中,实现 CAN 的初始化
CAN报文发送及接收结构体 - CAN_TxHeaderTypeDef
/CAN_RxHeaderTypeDefCAN
在发送或接收报文时,需要往发送邮箱中写入报文信息或从接收 FIFO 中读取报文信息;
利用STM32HAL 库的发送及接收结构体可以方便地完成这样的工作;
/* CAN发送结构体 */
typedef struct
{
uint32_t StdId; /* 存储报文的标准标识符 11 位,0-0x7FF. */
uint32_t ExtId; /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */
uint32_t IDE; /* 存储 IDE 扩展标志 */
uint32_t RTR; /* 存储 RTR 远程帧标志 */
uint32_t DLC; /* 存储报文数据段的长度,0-8 */
FunctionalState TransmitGlobalTime; /* [可选]最后这个是时间触发模式用的,开启后会自动把时间戳添加到最后两字节的数据中 */
}
CAN_TxHeaderTypeDef;
取值:
{
StdId :如果将要发送的报文使用标准ID,那么这个成员便记录标准ID的值
取值: 0x0 ~ 0x7FF
ExtId :如果将要发送的报文使用扩展ID,那么这个成员便记录扩展ID的值
取值: 0x0 ~ 0x1FFFFFFF
IDE :用来决定报文使用标准ID还是扩准ID
取值: CAN_ID_STD 或 CAN_ID_EXT
RTR :用来决定报文是数据帧要是遥控帧
取值: CAN_RTR_DATA 或 CAN_RTR_REMOTE
DLC :用来记录数据帧的数据长度,单位字节(如果要发送的是遥控帧,该成员中的内容不起作用)
取值:0 ~ 8
TransmitGlobalTime :目前没有用到,选择 DISABLE
取值: ENABLE 或 DISABLE
}
使用示例:
{
/**
* @brief 发送数据帧
*
* @param hcan CAN编号
* @param ID ID
* @param Data 被发送的数据指针
* @param Length 长度
* @return uint8_t 执行状态
*/
uint8_t CAN_Send_Data(CAN_HandleTypeDef *hcan, uint16_t ID, uint8_t *Data, uint16_t Length)
{
CAN_TxHeaderTypeDef tx_header;
uint32_t used_mailbox;
// 检测关键传参
assert_param(hcan != NULL);
tx_header.StdId = ID;
tx_header.ExtId = 0;
tx_header.IDE = 0;
tx_header.RTR = 0;
tx_header.DLC = Length;
return (HAL_CAN_AddTxMessage(hcan, &tx_header, Data, &used_mailbox));
}
CAN_Send_Data(&hcan1, 0x114, &Send_Data, 1);
}
==============================================================
/* CAN接收结构体 */
typedef struct
{
uint32_t StdId; /* 存储报文的标准标识符 11 位,0-0x7FF. */
uint32_t ExtId; /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */
uint32_t IDE; /* 存储 IDE 扩展标志 */
uint32_t RTR; /* 存储 RTR 远程帧标志 */
uint32_t DLC; /* 存储报文数据段的长度,0-8 */
uint32_t Timestamp; /* [可选]只有使能了时间触发模式才有用,记录时间戳 */
uint32_t FilterMatchIndex; /* [可选]这条报文被接收是通过哪个筛选器 */
}
CAN_RxHeaderTypeDef;
使用示例:
{
CAN_RxHeaderTypeDef header;
uint8_t data;
/**
* @brief HAL库CAN接收FIFO1中断
*
* @param hcan CAN编号
*/
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
HAL_CAN_GetRxMessage(hcan, CAN_FILTER_FIFO1, &header, &data);
//接收后的措施
}
}
CAN报文发送及接收结构体 - 成员变量分析:
- StdId:标准ID(格式)
本成员存储的是报文的 11 位标准标识符,范围是 0-0x7FF。
- ExtId:扩展ID
本成员存储的是报文的 29 位扩展标识符,范围是 0-0x1FFFFFFF。ExtId 与 StdId 这两个成员根据下面的 IDE 位配置,只有一个是有效的。
- IDE:标准/扩展 (ID) 帧格式
本成员存储的是扩展标志 IDE 位,当它的值为宏 CAN_ID_STD 时表示本报文是标准帧,使用 StdId 成员存储报文 ID;当它的值为宏 CAN_ID_EXT 时表示本报文是扩展帧,使用 ExtId 成员存储报文 ID。
- RTR:总线帧格式
本成员存储的是报文类型标志 RTR 位,当它的值为宏 CAN_RTR_Data 时表示本报文是数据帧;当它的值为宏 CAN_RTR_Remote 时表示本报文是遥控帧,由于遥控帧没有数据段,所以当报文是遥控帧时,数据是无效的
- DLC:数据帧数据段的数据字节长度
本成员存储的是数据帧数据段的长度,它的值的范围是 0-8,当报文是遥控帧时 DLC值为 0。
CAN接收过滤器 / CAN 筛选器 结构体 - CAN_FilterTypeDef
:
typedef struct
{
uint32_t FilterIdHigh; /*CAN_FxR1 寄存器的高 16 位 */
uint32_t FilterIdLow; /*CAN_FxR1 寄存器的低 16 位 */
uint32_t FilterMaskIdHigh; /*CAN_FxR2 寄存器的高 16 位 */
uint32_t FilterMaskIdLow; /*CAN_FxR2 寄存器的低 16 位 */
uint32_t FilterFIFOAssignment; /* 设置经过筛选后数据存储到哪个接收 FIFO */
uint32_t FilterBank; /* 筛选器编号,范围 0-27,数据手册上说0-27是CAN1/CAN2共享,但是实测发现并不是这样,CAN1是0-13,CAN2是14-27 */
uint32_t FilterMode; /* 筛选器模式 */
uint32_t FilterScale; /* 设置筛选器的尺度 */
uint32_t FilterActivation; /* 是否使能本筛选器 */
uint32_t SlaveStartFilterBank;
}
CAN_FilterTypeDef; /* CAN接收过滤器结构体 */
取值:
{
FilterIdHigh :CAN_FiR1寄存器的高16位,用于填写筛选码。
具体的格式要根据16位、32位;掩码模式、列表模式来确定。
取值: 0x0 ~ 0xFFFF
FilterIdLow :CAN_FiR1寄存器的低16位
FilterMaskIdHigh :CAN_FiR2寄存器的高16位
FilterMaskIdLow :CAN_FiR2寄存器的低16位
FilterFIFOAssignment :通过筛选器的报文存在FIFO0还是FIFO1中
取值:CAN_FILTER_FIFO0 或 CAN_FILTER_FIFO1
FilterBank :本次配置的筛选器号
取值:对于单CAN为 0 ~ 13;对于双CAN为 0 ~ 27
FilterMode :筛选模式,掩码模式或列表模式。
取值:CAN_FILTERMODE_IDMASK 或 CAN_FILTERMODE_IDMASK
FilterScale :筛选码大小,16位或32位。
取值:CAN_FILTERSCALE_16BIT 或 CAN_FILTERSCALE_32BIT
FilterActivation :使能或失能此筛选器。
取值:CAN_FILTER_DISABLE 或 CAN_FILTER_ENABLE
SlaveStartFilterBank :为从CAN(CAN2)分配的筛选器个数。如果只使用单个CAN,可忽略此成员
取值:0 ~ 27
}
使用示例:
{
// 滤波器编号
#define CAN_FILTER(x) ((x) << 3)
// 接收队列
#define CAN_FIFO_0 (0 << 2)
#define CAN_FIFO_1 (1 << 2)
//标准帧或扩展帧
#define CAN_STDID (0 << 1)
#define CAN_EXTID (1 << 1)
// 数据帧或遥控帧
#define CAN_DATA_TYPE (0 << 0)
#define CAN_REMOTE_TYPE (1 << 0)
/**
* @brief 配置CAN的滤波器
*
* @param hcan CAN编号
* @param Object_Para 滤波器序号 | 接收后存入的FIFOx | 报文ID类型(标准/扩展) | 帧类型(数据/遥控)
* @param ID 目标ID(标准/扩展)
* @param Mask_ID 屏蔽位(0x3ff, 0x1fffffff) - 需要哪些位相同(同1否0)
*/
void CAN_Filter_Mask_Config(CAN_HandleTypeDef *hcan, uint8_t Object_Para, uint32_t ID, uint32_t Mask_ID)
{
CAN_FilterTypeDef can_filter_init_structure;
// 检测关键传参
assert_param(hcan != NULL);
if ((Object_Para & 0x02))
{
// 标准帧
// 掩码后ID的高16bit
can_filter_init_structure.FilterIdHigh = ID << 3 >> 16;
// 掩码后ID的低16bit
can_filter_init_structure.FilterIdLow = ID << 3 | ((Object_Para & 0x03) << 1);
// ID掩码值高16bit
can_filter_init_structure.FilterMaskIdHigh = Mask_ID << 3 << 16;
// ID掩码值低16bit
can_filter_init_structure.FilterMaskIdLow = Mask_ID << 3 | ((Object_Para & 0x03) << 1);
}
else
{
// 扩展帧
// 掩码后ID的高16bit
can_filter_init_structure.FilterIdHigh = ID << 5;
// 掩码后ID的低16bit
can_filter_init_structure.FilterIdLow = ((Object_Para & 0x03) << 1);
// ID掩码值高16bit
can_filter_init_structure.FilterMaskIdHigh = Mask_ID << 5;
// ID掩码值低16bit
can_filter_init_structure.FilterMaskIdLow = ((Object_Para & 0x03) << 1);
}
// 滤波器序号, 0-27, 共28个滤波器, can1是0~13, can2是14~27
can_filter_init_structure.FilterBank = Object_Para >> 3;
// 滤波器绑定FIFOx, 只能绑定一个
can_filter_init_structure.FilterFIFOAssignment = (Object_Para >> 2) & 0x01;
// 使能滤波器
can_filter_init_structure.FilterActivation = ENABLE;
// 滤波器模式, 设置ID掩码模式
can_filter_init_structure.FilterMode = CAN_FILTERMODE_IDMASK;
// 32位滤波
can_filter_init_structure.FilterScale = CAN_FILTERSCALE_32BIT;
//从机模式选择开始单元
can_filter_init_structure.SlaveStartFilterBank = 14;
HAL_CAN_ConfigFilter(hcan, &can_filter_init_structure);
}
CAN_Filter_Mask_Config(&hcan1, CAN_FILTER(13) | CAN_FIFO_1 | CAN_STDID | CAN_DATA_TYPE, 0x114, 0x7ff);
}
注意:
FilterIdHigh、FilterIdLow、FilterMaskIdLow、FilterMaskIdLow这四个成员不是它的名字那么简单,他们的功能取决于使用的过滤器模式。若为列表模式,则不管是FilterId还是FilterMaskId都表示的是列表成员。上述四个成员都是16位的,一定注意下面说的移位操作
在32位过滤器中,一组High和Low检验同一个CAN ID;16位过滤器,则可分别独立检验一个ID
CAN接收过滤器 / CAN 筛选器 结构体 - 成员变量分析:
- FilterIdHigh
FilterIdHigh 成员用于存储要筛选的 ID,若筛选器工作在 32 位模式,它存储的是所筛选 ID 的高 16 位;若筛选器工作在 16 位模式,它存储的就是一个完整的要筛选的 ID。
- FilterIdLow
类似地,FilterIdLow 成员也是用于存储要筛选的 ID,若筛选器工作在 32 位模式,它存储的是所筛选 ID 的低 16 位;若筛选器工作在 16 位模式,它存储的就是一个完整的要筛选的 ID。
- FilterMaskIdHigh
FilterMaskIdHigh 存储的内容分两种情况,当筛选器工作在标识符列表模式时,它的功能与 FilterIdHigh 相同,都是存储要筛选的 ID;而当筛选器工作在掩码模式时,它存储的是 FilterIdHigh 成员对应的掩码,与 FilterIdLow 组成一组筛选器。
- FilterMaskIdLow
类似地, FilterMaskIdLow 存储的内容也分两种情况,当筛选器工作在标识符列表模式时,它的功能与 FilterIdLow 相同,都是存储要筛选的 ID;而当筛选器工作在掩码模式时,它存储的是 FilterIdLow 成员对应的掩码,与 FilterIdLow 组成一组筛选器。上面四个结构体的存储的内容很容易让人糊涂,请结合前面的图 39_0_15 和下面的表 39‑7 理解,如果还搞不清楚,再结合库函数 FilterInit 的源码来分析。
表 不同模式下各结构体成员的内容
对这些结构体成员赋值的时候,还要注意寄存器位的映射,即注意哪部分代表 STID,哪部分代表 EXID 以及 IDE、RTR 位。
- FilterFIFOAssignment
本成员用于设置当报文通过筛选器的匹配后,该报文会被存储到哪一个接收 FIFO,它的可选值为 FIFO0 或 FIFO1(宏 CAN_FILTER_FIFO0/1)。
- FilterBank
本成员用于设置筛选器的编号,即本过滤器结构体配置的是哪一组筛选器,CAN 一共有 28 个筛选器,所以它的可输入参数范围为 0-27。
- FilterMode
本 成 员 用 于 设 置 筛 选 器的 工 作 模 式, 可 以 设 置 为 列 表 模 式 (宏CAN_FILTERMODE_IDLIST) 及掩码模式 (宏 CAN_FILTERMODE_IDMASK)。
- FilterScale
本成员用于设置筛选器的尺度,可以设置为 32 位长 (宏 CAN_FILTERSCALE_32BIT)及 16 位长 (宏 CAN_FILTERSCALE_16BIT)。
- FilterActivation
本成员用于设置是否激活这个筛选器 (宏 ENABLE/DISABLE)。
函数分析:
基本函数——控制函数:
函数 | 功能 | 备注 |
HAL_CAN_Start | 开启CAN通讯 | |
HAL_CAN_Stop | 关闭CAN通讯 | |
HAL_CAN_RequestSleep | 尝试进入休眠模式 | |
HAL_CAN_WakeUp | 从休眠模式中唤醒 | |
HAL_CAN_IsSleepActive | 检查是否成功进入休眠模式 | |
HAL_CAN_AddTxMessage | 向 Tx 邮箱中增加一个消息,并且激活对应的传输请求 | 发送消息 |
HAL_CAN_AbortTxRequest | 请求中断传输 | |
HAL_CAN_GetTxMailboxesFreeLevel | Return Tx mailboxes free level | |
HAL_CAN_IsTxMessagePending | 检查是否有传输请求在指定的 Tx 邮箱上等待 | |
HAL_CAN_GetRxMessage | 从Rx FIFO 收取一个 CAN 帧 | 接收消息 |
HAL_CAN_GetRxFifoFillLevel | Return Rx FIFO fill level | |
HAL_CAN_ConfigFilter | 配置CAN过滤器 | 接收过滤器配置——如果有目标报文ID/目标报文ID格式,可以过滤掉其他的报文ID的数据 |
HAL_CAN_ActivateNotification(CAN_HandleTypeDef *hcan, uint32_t ActiveITs) | 使能中断 第二个参数:使能哪个中断,在stm32f4xx_HAL_Driver.h中,搜索Receive Interrupts可以查到各宏定义 | HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); 或者 CAN_IT_RX_FIFO1_MSG_PENDING 使能FIFO0 FIFO1的接收中断、其他(发送/错误中断) |
__HAL_CAN_ENABLE_IT(hcan,CAN_IT_RX_FIFO0_MSG_PENDING); __HAL_CAN_ENABLE_IT(hcan,CAN_IT_RX_FIFO1_MSG_PENDING); | 使能FIFO0 FIFO1的接收中断 |
各函数详细信息:
HAL_StatusTypeDef HAL_CAN_Start(CAN_HandleTypeDef *hcan);
功能:开启CAN,一般一开始就要打开
参数:CAN句柄指针,&hcan1 或 &hcan2
返回值:返回值:HAL状态
HAL_StatusTypeDef HAL_CAN_ConfigFilter(CAN_HandleTypeDef *hcan, CAN_FilterTypeDef *sFilterConfig)
功能:设置接收过滤器
参数:第一个,CAN句柄指针,&hcan1 或 &hcan2
第二个,过滤器类型定义结构体指针
返回值:HAL状态
HAL_StatusTypeDef HAL_CAN_ActivateNotification(CAN_HandleTypeDef *hcan, uint32_t ActiveITs)
功能:使能中断
参数:第一个,CAN句柄指针,&hcan1 或 &hcan2
第二个,使能哪个中断,在stm32f4xx_HAL_Driver.h中,搜索Receive Interrupts可以查到各宏定义
返回值:返回值:HAL状态
HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan,
CAN_TxHeaderTypeDef *pHeader,
uint8_t aData[],
uint32_t *pTxMailbox);
功能:增加一个消息到第一个空闲的 Tx 邮箱,并且激活对应的传输请求
参数:第一个,CAN句柄指针,&hcan1 或 &hcan2
第二个,发送报文定义结构体的指针
第三个,数据帧数组的内容,长度不能超过定义的长度
第四个,Tx邮箱,可填 (uint32_t*)CAN_TX_MAILBOX0 ,CAN_TX_MAILBOX1 , CAN_TX_MAILBOX3
返回值:HAL状态
HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan,
uint32_t RxFifo,
CAN_RxHeaderTypeDef *pHeader,
uint8_t aData[]);
功能:从Rx FIFO收取一个 CAN 帧
参数:第一个,CAN句柄指针,&hcan1 或 &hcan2
第二个,Rx FIFO,可填 CAN_RX_FIFO0, CAN_RX_FIFO1
第三个,接收报文定义结构体的指针
第四个,接受数据数组
返回值:HAL状态
中断服务函数:
函数 | 功能 |
HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) | FIFO0的接收中断回调函数 / 处理CAN的FIFO0接收完成中断 |
HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) | FIFO1的接收中断回调函数 / 处理CAN的FIFO1接收完成中断 |
void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan) | FIFO溢出中断 处理CAN接收FIFO0满中断 |
void HAL_CAN_RxFifo1FullCallback(CAN_HandleTypeDef *hcan) | 处理CAN接收FIFO1满中断 |
发送中断回调函数 | |
错误/状态变化中断回调函数 |
各函数详细信息:
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan);
(根据使用0可能替换成1)
eg:
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &can_Rx, recvBuf);
/*下面是你用来处理收到数据的代码,可以通过串口把内容发送出来,也可以用来控制某些外设*/
}
功能:接收回调函数,CAN回调后进行的操作。一般在此接收数据
参数:第一个,CAN句柄指针
返回值:void
2.5.2. CAN的CubeMX配置分析:
CAN界面总览:
这两个都一样!
补充:第一个配置波特率下面会自动计算最终波特率的,有时候显示!
配置分析:
Parameter Settings标签页:
参数 | 意思 |
Prescaler | 预分频,即位时序提到的APB1 peripheral clocks继续分一次频 |
Time Quantum | 最小时间单位Tq,自动计算出来的,不需要填写 |
Time Quanta in Bit Segment 1 | PBS1段长度 |
Time Quanta in Bit Segment 2 | PBS2段长度 |
ReSynchronization Jump Width | 重同步跳跃宽度,即位时序提到的SJW |
Time Triggered Communication Mode | 是否使能时间触发 |
Automatic Bus-Off Management | 是否使能自动离线管理 |
Automatic Wake-Up Mode | 是否使能自动唤醒 |
Qutomatic Retransmission | 是否使能自动重传 |
Receive Fifo Locked Mode | 是否使能锁定FIFO |
Transmit Fifo Priority | 配置报文优先级的判断方法 |
Oprating Mode | 操作模式 |
波特率配置:
- APB1时钟频率获取:
配置参数:
配置完这些结构体成员后,我们调用库函数 HAL_CAN_Init 即可把这些参数写入到 CAN 控制寄存器中,实现 CAN 的初始化
Timer Triggered Communication Mode
:时间戳功能
时间戳功能,具体看分析
是否使用时间触发功能 (ENABLE/DISABLE),时间触发功能在某些CAN 标准中会使用到。
Automatic Bus-Off Management
:【必选】设备错误后,自动恢复/手动恢复错误CAN设备
是否使用自动离线管理功能 (ENABLE/DISABLE)
-
- 使用自动离线管理可以在出错时离线后适时自动恢复,不需要软件干预。
- 对应-ABOM寄存器:
-
-
- 置1,开启离线自动恢复
-
-
-
-
- 进入离线状态后,就自动开启恢复过程
-
-
-
-
- 置0,关闭离线自动恢复
-
-
-
-
- 软件必须先请求进入然后再退出初始化模式
-
-
IRQN置1再清0
-
-
-
- 随后恢复过程才被开启
-
-
此时,再检测128此11个隐性(电平1)位,才重新开启CAN设备
Automatic Wake-Up Mode
:【必选】总线活动后CAN设备自动唤醒
是否使用自动唤醒功能 (ENABLE/DISABLE)
-
- 使能自动唤醒功能后它会在监测到总线活动后自动唤醒
Automatic Retransmission
:报文发送失败后自动重发
是否使用自动重传功能 (ENABLE/DISABLE)
-
- 使用自动重传功能时,会一直发送报文直到成功为止
Receive Fifo Locked Mode
:FIFO溢出后数据处理(丢弃/覆盖)
是否使用锁定接收 FIFO(ENABLE/DISABLE)
-
- 锁定接收 FIFO 后,若FIFO 溢出时会丢弃新数据
- 否则在 FIFO 溢出时以新数据覆盖旧数据
Transmit Fifo Priority
:发送邮箱优先级判定方法(排队/ID大小)
发送报文的优先级判定方法 (ENABLE/DISABLE)
-
- 使能时,以报文存入发送邮箱的先后顺序来发送
- 否则,按照报文 ID 的优先级来发送
配置模式:
1、正常模式:CAN外设正常地向CAN总线发送数据并从CAN总线上接收数据。
2、回环模式:CAN外设正常向CAN总线发送数据,同时接收自己发送的数据,但不从CAN总线上接收数据。
3、静默模式:CAN外设不向CAN总线发送数据,仅从CAN总线上接收数据,但不会应答。一般用于检测CAN总线的流量。
4、静默回环模式:CAN外设不会往CAN总线收发数据,仅给自己发送。一般用于自检。
NVIC Settings 标签页:
- CAN TX:发送中断
- CAN RX0:FIFO 0接收/溢出中断
- CAN RX1:FIFO 1接收/溢出中断
STM32有2个3级深度的接收缓冲区:FIFO0和FIFO1,每个FIFO都可以存放3个完整的报文,它们完全由硬件来管理
- 如果是来自FIFO0的接收中断,则用CAN1_RX0_IRQn中断来处理
- 如果是来自FIFO1的接收中断,则用CAN1_RX1_IRQn中断来处理
- CAN SCE:status chanege error,错误和状态变化中断!
2.5.3. CAN的工程开发(CubeMX配置+Project程序编写):
1、正常模式:CAN外设正常地向CAN总线发送数据并从CAN总线上接收数据。
2、回环模式:CAN外设正常向CAN总线发送数据,同时接收自己发送的数据,但不从CAN总线上接收数据。
3、静默模式:CAN外设不向CAN总线发送数据,仅从CAN总线上接收数据,但不会应答。一般用于检测CAN总线的流量。
4、静默回环模式:CAN外设不会往CAN总线收发数据,仅给自己发送。一般用于自检。
【开发流程】CAN开发编程核心(配置完CubeMX后)
CubeMX配置部分在分析中有介绍
1、设置接收过滤器【必须配置,否则接收不了,没用到特殊模式也要!一般是32位屏蔽模式,屏蔽位全给0即可】
如果有目标接收需求可加
-
- 定义接收过滤器类型定义结构体(
CAN_FilterTypeDef
)变量 - 配置CAN接收过滤器
HAL_CAN_ConfigFilter()
- 定义接收过滤器类型定义结构体(
/* 过滤器配置项 - 传参 */
// 这里不采用stm32fxxx_hal_can.h内的宏是为了更好控制,但是需要对can过滤器有一定程度理解
// 第4位 - 滤波器编号(0-27, 共28个滤波器, can1是0~13, can2是14~27)
#define CAN_FILTER(x) ((x) << 3)
// 第3位 - 指定的接收队列
#define CAN_FILTER_FIFO_0 (0 << 2)
#define CAN_FILTER_FIFO_1 (1 << 2)
// 第2位 - 格式帧:标准帧或扩展帧(ID格式)
#define CAN_IDE_STD (0 << 1)
#define CAN_IDE_EXT (1 << 1)
// 第1位 - 格式帧:数据帧或遥控帧
#define CAN_RTR_DATA_FRAME (0 << 0)
#define CAN_RTR_REMOTE_FRAME (1 << 0)
// 具体接收报文格式要求,要将传入的格式帧(ID,IDE,RTR)和屏蔽位一起看[具体看过滤器原理]
/*******************************************************************************
* @funticon_name: CAN_Filter_Mask_Config
* @brief CAN的过滤器配置 - 32位屏蔽工作模式
* @return {*}
* @note 使用: 第二个传入的参数要去driver_can.h的宏定义中查看!
* CAN_Filter_Mask_Config(&hcan1, CAN_FILTER(13) | CAN_FIFO_1 | CAN_STDID | CAN_DATA_TYPE, 0x114, 0x7ff);
* @param {CAN_HandleTypeDef} *hcan CAN编号
* @param {uint8_t} Object_Para 滤波器序号 | 接收后存入的FIFOx | 报文ID类型(标准/扩展) | 帧类型(数据/遥控)
* [注意,如果要添加参数,去.h的宏定义中添加,再来这里补充]
* (取值:CAN_FILTER(x)
* CAN_FILTER_FIFO_0 / CAN_FILTER_FIFO_1
* CAN_IDE_STD / CAN_IDE_EXT
* CAN_RTR_DATA_FRAME / CAN_RTR_REMOTE_FRAME)
* @param {uint32_t} ID 目标ID - 标准/扩展格式
* @param {uint32_t} Mask_ID 屏蔽位 - 如(id-0x3ff, 屏蔽位-0x1fffffff) - 需要哪些位相同(同1否0)
*******************************************************************************/
void CAN_FilterConfig_32Bit_Mask(CAN_HandleTypeDef *hcan, uint8_t Object_Para, uint32_t ID, uint32_t Mask_ID)
{
CAN_FilterTypeDef can_filter_init_structure;
// 检测关键传参
assert_param(hcan != NULL);
// 过滤器过滤参数:
// 标准ID帧 - 格式帧(ID,IDE,RTR)和屏蔽位(1同0任)
if ((Object_Para & 0x02))
{
// 掩码后ID的高16bit
can_filter_init_structure.FilterIdHigh = ID << 3 >> 16;
// 掩码后ID的低16bit
can_filter_init_structure.FilterIdLow = ID << 3 | ((Object_Para & 0x03) << 1);
// ID掩码值高16bit
can_filter_init_structure.FilterMaskIdHigh = Mask_ID << 3 << 16;
// ID掩码值低16bit
can_filter_init_structure.FilterMaskIdLow = Mask_ID << 3 | ((Object_Para & 0x03) << 1);
}
// 扩展ID帧 - 格式帧(ID,IDE,RTR)和屏蔽位
else
{
// 掩码后ID的高16bit
can_filter_init_structure.FilterIdHigh = ID << 5;
// 掩码后ID的低16bit
can_filter_init_structure.FilterIdLow = ((Object_Para & 0x03) << 1);
// ID掩码值高16bit
can_filter_init_structure.FilterMaskIdHigh = Mask_ID << 5;
// ID掩码值低16bit
can_filter_init_structure.FilterMaskIdLow = ((Object_Para & 0x03) << 1);
}
// 过滤器工作参数:
// 滤波器序号, 0-27, 共28个滤波器, can1是0~13, can2是14~27
can_filter_init_structure.FilterBank = Object_Para >> 3;
// 滤波器绑定FIFOx, 只能绑定一个
can_filter_init_structure.FilterFIFOAssignment = (Object_Para >> 2) & 0x01;
// 滤波器模式, 设置ID掩码模式
can_filter_init_structure.FilterMode = CAN_FILTERMODE_IDMASK;
// 32位滤波
can_filter_init_structure.FilterScale = CAN_FILTERSCALE_32BIT;
// 使能滤波器
can_filter_init_structure.FilterActivation = ENABLE;
// 从机模式选择开始单元,从CAN(CAN2)分配的筛选器个数。如果只使用单个CAN,可忽略此成员
// can_filter_init_structure.SlaveStartFilterBank = 14;
HAL_CAN_ConfigFilter(hcan, &can_filter_init_structure);
}
使用:
// 过滤器配置
CAN_FilterConfig_32Bit_Mask(hcan, CAN_FILTER(13) | CAN_FILTER_FIFO_0 | CAN_IDE_STD | CAN_RTR_DATA_FRAME, 0, 0);
CAN_FilterTypeDef can_Filter = {0};
can_Filter.FilterIdHigh = 0;
can_Filter.FilterIdLow = 0;
can_Filter.FilterMaskIdHigh = 0;
can_Filter.FilterMaskIdLow = 0;
can_Filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
can_Filter.FilterBank = 0;
can_Filter.FilterMode = CAN_FILTERMODE_IDMASK;
can_Filter.FilterScale = CAN_FILTERSCALE_32BIT;
can_Filter.FilterActivation = CAN_FILTER_ENABLE;
HAL_CAN_ConfigFilter(&hcan1, &can_Filter);
2、开启CAN,使能CAN中断,HAL_CAN_Start()
,__HAL_CAN_ENABLE_IT()
/ HAL_CAN_ActivateNotification()
/**
* @brief 初始化CAN总线
* @param hcan CAN编号
* @param Callback_Function 处理回调函数
*/
void CAN_Init(CAN_HandleTypeDef *hcan)
{
/* 使能CAN */
HAL_CAN_Start(hcan);
/* 使能CAN中断 */
__HAL_CAN_ENABLE_IT(hcan, CAN_IT_RX_FIFO0_MSG_PENDING);
__HAL_CAN_ENABLE_IT(hcan, CAN_IT_RX_FIFO1_MSG_PENDING);
或者
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO1_MSG_PENDING);
}
3、CAN发送:HAL_CAN_AddTxMessage()
发送标准格式数据帧:
标准格式和扩展格式发送的区别主要是在CAN_TxHeaderTypeDef变量的成员RTR中
/**
* @brief 发送标准格式-数据帧
* @param hcan CAN编号
* @param ID 报文ID - 如0x114,0x212等
* @param Data 被发送的数据指针
* @param Length 发送数据长度
* @return uint8_t 执行状态
*/
uint8_t CAN_Send_StdId_DataFrames(CAN_HandleTypeDef *hcan, uint16_t StdId, uint8_t *Tx_Data, uint16_t Tx_Length)
{
CAN_TxHeaderTypeDef tx_header;
uint32_t used_mailbox;
// 检测关键传参
assert_param(hcan != NULL);
tx_header.StdId = StdId; // 标准ID
tx_header.ExtId = 0; // 扩展ID
tx_header.IDE = 0; // 总线帧格式
tx_header.RTR = 0; // 标准/扩展ID格式
tx_header.DLC = Tx_Length; // 发送数据长度
return (HAL_CAN_AddTxMessage(hcan, &tx_header, Tx_Data, &used_mailbox));
}
后续可以写一个全概括的函数(集成)/单独写也可以(方便用)
CAN_Send_Data(&hcan1, 0x114, &Send_Data, 1);
can_Tx.StdId = 0x123;
can_Tx.ExtId = 0;
can_Tx.IDE = CAN_ID_STD;
can_Tx.RTR = CAN_RTR_DATA;
can_Tx.DLC = 5;
can_Tx.TransmitGlobalTime = DISABLE;
HAL_CAN_AddTxMessage(&hcan1, &can_Tx, sendBuf, &box);
4、CAN接收:HAL_CAN_RxFifo0MsgPendingCallback()
/HAL_CAN_GetRxMessage()
注意:要在初始化函数中开启中断HAL_CAN_ActivateNotification()
/__HAL_CAN_ENABLE_IT()
-
- 重写FIFO接收中断回调函数 -
HAL_CAN_RxFifo0MsgPendingCallback
- 重写FIFO接收中断回调函数 -
注意,接收中断和溢出中断的回调函数不同
-
- 定义接收报文定义结构体(
CAN_RxHeaderTypeDef
)结构体变量。 - 用接收函数
HAL_CAN_GetRxMessage
接收
- 定义接收报文定义结构体(
CAN_RxHeaderTypeDef header;
uint8_t data;
/**
* @brief HAL库CAN接收FIFO0中断
* @param hcan CAN编号
*/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
HAL_CAN_GetRxMessage(hcan, CAN_FILTER_FIFO0, &header, &data);
// 接收后的措施
}
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &can_Rx, recvBuf);
/*下面是你用来处理收到数据的代码,可以通过串口把内容发送出来,也可以用来控制某些外设*/
}
【例程1】:Normal模式、500K波特率、裸机(定时发送,中断接收)
开发环境:
- 芯片型号:STM32F103C8T6
- 是否有OS:FreeRTOS
CubeMX配置:
后面的例程默认在这上面更改
波特率配置:500Kbps
用波特率计算器算
配置参数:出错后 - 自动恢复 | 自动唤醒
工作模式:正常模式
Project程序开发:
初始化:
/* CAN1初始化 */
// 过滤器配置
CAN_FilterC nfig_32Bit_Mask(&hcan, CAN_FILTER(13) | CAN_FILTER_FIFO_0 | CAN_IDE_STD | CAN_RTR_DATA_FRAME, 0, 0);
// CAN初始化
CAN_Init(&hcan);
发送:
for(int i = 0; i < 8; i++)
{
CAN_data_EleControl.Tx_Data_Buf[i] = 0;
}
while(1)
{
mdelay(100);
for(int i = 0; i < 8; i++)
CAN_data_EleControl.Tx_Data_Buf[i] += i;
CAN_SendMsg_DataFrame(&CAN_data_EleControl, CAN_ID_STD, 0x123, 0, 6);
}
接收:
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
if (hcan == CAN_data_EleControl.hCanHandle)
{
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &CAN_data_EleControl.hRxHeader, CAN_data_EleControl.Rx_Data_Buf) != HAL_OK)
{
// 处理错误
printf("\r\nCAN1 Rx FIFO0 ERROR.\r\n");
}
// 显示CAN接收到的数据帧(参数,数据段,...),有时候不显示,但是都是正常的效果
printf("\r\n==>>CAN1 RX FIFF0<<==\r\n");
printf("LEN :%d\r\n", CAN_data_EleControl.hRxHeader.DLC);
printf("StdID:%d\r\n", CAN_data_EleControl.hRxHeader.StdId);
printf("ExtID:%d\r\n", CAN_data_EleControl.hRxHeader.ExtId);
printf("DATA :");
for(uint8_t i = 0; i < CAN_data_EleControl.hRxHeader.DLC; i++)
printf("%d ", CAN_data_EleControl.Rx_Data_Buf[i]);
printf("\r\n");
}
}
效果:
【例程2】:Normal模式、500K波特率、RTOS(定时发送,中断接收)
CubeMX配置:
Project程序:
效果图:
- 先拿回环模式测试一下:
- 正常模式:多设备通信
【例程5】:Normal模式、1000K波特率、RTOS(定时发送,中断接收)
就基于例程2改了波特率
【例程3】:测试模式-回环模式/Loopbacke模式、500K波特率【如果开发有问题,就来这个模式测试一下】
STM32CubeMX与HAL库学习--简单的CAN回环测试_stm32f407 can回环测试-优快云博客
CubeMX配置:
Project程序开发:
该工作模式:CAN外设正常向CAN总线发送数据,同时接收自己发送的数据,但不从CAN总线上接收数据。
也就是在自己的FIFO接收中断回调函数中,接收的是自己的数据
发送:
for(int i = 0; i < 8; i++)
{
CAN_data_EleControl.Tx_Data_Buf[i] = 0;
}
while(1)
{
mdelay(100);
for(int i = 0; i < 8; i++)
CAN_data_EleControl.Tx_Data_Buf[i] += i;
CAN_SendMsg_DataFrame(&CAN_data_EleControl, CAN_ID_STD, 0x123, 0, 6);
}
接收:
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
if (hcan == CAN_data_EleControl.hCanHandle)
{
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &CAN_data_EleControl.hRxHeader, CAN_data_EleControl.Rx_Data_Buf) != HAL_OK)
{
// 处理错误
printf("\r\nCAN1 Rx FIFO0 ERROR.\r\n");
}
// 显示CAN接收到的数据帧(参数,数据段,...),有时候不显示,但是都是正常的效果
printf("\r\n==>>CAN1 RX FIFF0<<==\r\n");
printf("LEN :%d\r\n", CAN_data_EleControl.hRxHeader.DLC);
printf("StdID:%d\r\n", CAN_data_EleControl.hRxHeader.StdId);
printf("ExtID:%d\r\n", CAN_data_EleControl.hRxHeader.ExtId);
printf("DATA :");
for(uint8_t i = 0; i < CAN_data_EleControl.hRxHeader.DLC; i++)
printf("%d ", CAN_data_EleControl.Rx_Data_Buf[i]);
printf("\r\n");
}
}
效果:
【例程4】:测试模式-静默回环模式/Loopback combined with Silent模式、500K波特率
STM32F407 CAN 静默回环模式 配置_can回环模式-优快云博客
3. STM32开发CAN的 问题与调试
3.1. 开发遇到的问题
3.1.1. STM32CAN在回环模式正常工作,正常模式时CAN设备之间无法通信:
- 一定要先用回环模式测试一下
- 就算没用到过滤器,也要开启这个
主要是犯了这个错误
- CAN模块(TJA100?),要接5v,供地!!
主要是犯了这个错误
3.2. 调试心得
- 如果使用CAN盒调试CAN通信时,出现发送或者接收数据失败,将CAN配置为静默回环模式,先排除硬件文件,保证软件无误
- 发送5帧数据只能接收到一帧数据,说明CAN发送较快,发送完一帧数据之后可以等待发送完成在发送下一帧数据,修改CAN配置
- 如果静默回环模式配置正确,使用CAN盒通信时只能接收错误帧,检查CAN波特率是否正确。有两种可能性,第一种就是CAN的波特率计算错误,第二种就是系统时钟配置错误,倒是CAN时钟不是默认时钟
笔记软件上链接:
https://www.yuque.com/u41716106/fgbb99/wnq8g855lf4hz1bx?singleDoc# 《【CAN开发】STM32 CAN(bxCAN/CAN2.0) Controller(CAN控制器)外设开发》