【CAN开发】STM32 CAN(bxCAN/CAN2.0) Controller(CAN控制器)外设开发

文末有我笔记软件上的链接:有问题会实时更新

关于CAN与FDCAN:

目前来说在用的CAN主要分为 CAN2.0 CAN-FD

CAN2.0 数据帧一帧最大可以传输8字节,最高波特率为1000kbps。

CAN-FD 可以向下兼容 CAN2.0

CAN-FD最大的变化在于数据段,一帧最大可以传输64字节,并且数据段波特率可以和其他部分不一样,最大可以到8000kbps。

很多情况下称 CAN2.0bxCAN

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波特率计算器使用:

STM32CAN通信波特率计算器:STM32 CAN通信波特率计算器欢迎使用STM32 CAN通信波特率计算器!本资源文件旨在帮助开发者快速计算和理解STM32微控制器在CAN通信中的波特率设置 - GitCode

【完整资料包】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初始化结构体 - 成员变量分析:

  1. Prescaler

本成员设置 CAN 外设的时钟分频,它可控制时间片 Tq 的时间长度,这里设置的值最终会减 1 后再写入 BRP 寄存器位,即前面介绍的 Tq 计算公式:

  • Tq = (BRP[9:0]+1) x TPCLK
  • 等效于:Tq = CAN_Prescaler x TPCLK
  1. Mode

本成员设置 CAN 的工作模式,可设置为正常模式 (CAN_MODE_NORMAL)、回环模式 (CAN_MODE_LOOPBACK)、静默模式 (CAN_MODE_SILENT) 以及回环静默模式(CAN_MODE_SILENT_LOOPBACK)。

  1. SyncJumpWidth

本成员可以配置 SJW 的极限长度,即 CAN 重新同步时单次可增加或缩短的最大长度,它可以被配置为 1-4Tq(CAN_SJW_1/2/3/4tq)。

  1. TimeSeg1

本成员用于设置 CAN 位时序中的 BS1 段的长度,它可以被配置为 1-16 个 Tq 长度(CAN_BS1_1/2/3…16tq)。

  1. 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
  1. TimeTriggeredMode

本成员用于设置是否使用时间触发功能 (ENABLE/DISABLE),时间触发功能在某些CAN 标准中会使用到。

  1. AutoBusOff

本成员用于设置是否使用自动离线管理 (ENABLE/DISABLE),使用自动离线管理可以在节点出错离线后适时自动恢复,不需要软件干预。

  1. AutoWakeUp

本成员用于设置是否使用自动唤醒功能 (ENABLE/DISABLE),使能自动唤醒功能后它会在监测到总线活动后自动唤醒。

  1. AutoRetransmission

本成员用于设置是否使用自动重传功能 (ENABLE/DISABLE),使用自动重传功能时,会一直发送报文直到成功为止。

  1. ReceiveFifoLocked

本成员用于设置是否使用锁定接收 FIFO(ENABLE/DISABLE),锁定接收 FIFO 后,若FIFO 溢出时会丢弃新数据,否则在 FIFO 溢出时以新数据覆盖旧数据。

  1. 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报文发送及接收结构体 - 成员变量分析:

  1. StdId:标准ID(格式)

本成员存储的是报文的 11 位标准标识符,范围是 0-0x7FF。

  1. ExtId:扩展ID

本成员存储的是报文的 29 位扩展标识符,范围是 0-0x1FFFFFFF。ExtId 与 StdId 这两个成员根据下面的 IDE 位配置,只有一个是有效的。

  1. IDE:标准/扩展 (ID) 帧格式

本成员存储的是扩展标志 IDE 位,当它的值为宏 CAN_ID_STD 时表示本报文是标准帧,使用 StdId 成员存储报文 ID;当它的值为宏 CAN_ID_EXT 时表示本报文是扩展帧,使用 ExtId 成员存储报文 ID。

  1. RTR:总线帧格式

本成员存储的是报文类型标志 RTR 位,当它的值为宏 CAN_RTR_Data 时表示本报文是数据帧;当它的值为宏 CAN_RTR_Remote 时表示本报文是遥控帧,由于遥控帧没有数据段,所以当报文是遥控帧时,数据是无效的

  1. 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 筛选器 结构体 - 成员变量分析:

  1. FilterIdHigh

FilterIdHigh 成员用于存储要筛选的 ID,若筛选器工作在 32 位模式,它存储的是所筛选 ID 的高 16 位;若筛选器工作在 16 位模式,它存储的就是一个完整的要筛选的 ID。

  1. FilterIdLow

类似地,FilterIdLow 成员也是用于存储要筛选的 ID,若筛选器工作在 32 位模式,它存储的是所筛选 ID 的低 16 位;若筛选器工作在 16 位模式,它存储的就是一个完整的要筛选的 ID。

  1. FilterMaskIdHigh

FilterMaskIdHigh 存储的内容分两种情况,当筛选器工作在标识符列表模式时,它的功能与 FilterIdHigh 相同,都是存储要筛选的 ID;而当筛选器工作在掩码模式时,它存储的是 FilterIdHigh 成员对应的掩码,与 FilterIdLow 组成一组筛选器。

  1. FilterMaskIdLow

类似地, FilterMaskIdLow 存储的内容也分两种情况,当筛选器工作在标识符列表模式时,它的功能与 FilterIdLow 相同,都是存储要筛选的 ID;而当筛选器工作在掩码模式时,它存储的是 FilterIdLow 成员对应的掩码,与 FilterIdLow 组成一组筛选器。上面四个结构体的存储的内容很容易让人糊涂,请结合前面的图 39_0_15 和下面的表 39‑7 理解,如果还搞不清楚,再结合库函数 FilterInit 的源码来分析。

表 不同模式下各结构体成员的内容

对这些结构体成员赋值的时候,还要注意寄存器位的映射,即注意哪部分代表 STID,哪部分代表 EXID 以及 IDE、RTR 位。

  1. FilterFIFOAssignment

本成员用于设置当报文通过筛选器的匹配后,该报文会被存储到哪一个接收 FIFO,它的可选值为 FIFO0 或 FIFO1(宏 CAN_FILTER_FIFO0/1)。

  1. FilterBank

本成员用于设置筛选器的编号,即本过滤器结构体配置的是哪一组筛选器,CAN 一共有 28 个筛选器,所以它的可输入参数范围为 0-27。

  1. FilterMode

本 成 员 用 于 设 置 筛 选 器的 工 作 模 式, 可 以 设 置 为 列 表 模 式 (宏CAN_FILTERMODE_IDLIST) 及掩码模式 (宏 CAN_FILTERMODE_IDMASK)。

  1. FilterScale

本成员用于设置筛选器的尺度,可以设置为 32 位长 (宏 CAN_FILTERSCALE_32BIT)及 16 位长 (宏 CAN_FILTERSCALE_16BIT)。

  1. 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即可】

如果有目标接收需求可加

    1. 定义接收过滤器类型定义结构体(CAN_FilterTypeDef)变量
    2. 配置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()

    1. 重写FIFO接收中断回调函数 - HAL_CAN_RxFifo0MsgPendingCallback

注意,接收中断和溢出中断的回调函数不同

    1. 定义接收报文定义结构体(CAN_RxHeaderTypeDef)结构体变量。
    2. 用接收函数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设备之间无法通信:

  1. 一定要先用回环模式测试一下
  2. 就算没用到过滤器也要开启这个

主要是犯了这个错误

  1. CAN模块(TJA100?),要接5v,供地!!

主要是犯了这个错误

3.2. 调试心得

  1. 如果使用CAN盒调试CAN通信时,出现发送或者接收数据失败,将CAN配置为静默回环模式,先排除硬件文件,保证软件无误
  2. 发送5帧数据只能接收到一帧数据,说明CAN发送较快,发送完一帧数据之后可以等待发送完成在发送下一帧数据,修改CAN配置
  3. 如果静默回环模式配置正确,使用CAN盒通信时只能接收错误帧,检查CAN波特率是否正确。有两种可能性,第一种就是CAN的波特率计算错误,第二种就是系统时钟配置错误,倒是CAN时钟不是默认时钟

笔记软件上链接:

https://www.yuque.com/u41716106/fgbb99/wnq8g855lf4hz1bx?singleDoc# 《【CAN开发】STM32 CAN(bxCAN/CAN2.0) Controller(CAN控制器)外设开发》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值