【CAN开发】STM32 FDCAN(CAN-FD) Controller(FDCAN控制器)外设开发

1. STM32 FDCAN Controller(FDCAN控制器)外设原理介绍:

介绍STM32 FDCAN外设:

这里主要是讲FDCAN与CAN的区别

参考资料:借鉴了很多大佬的资料,在此感谢,如有侵权,联系立删

STM32H7 FDCAN兼容普通CAN使用 基于CubeMX配置_stm32cubeide h7配置fdcan作为普通can-CSDN博客

https://www.st.com/content/st_com/en/search.html#q=AN5348-t=resources-page=1

STM32 CAN使用记录:FDCAN基础通讯_stm32 canfd-CSDN博客

CAN基础概念_can时钟40mhz-CSDN博客

开发和原理-挺全:STM32H7系列FDCAN配置成经典CAN的经验教程和注意事项_stm32h7 can-CSDN博客

【STM32H7教程】第91章 STM32H7的FDCAN总线基础知识和HAL库API - 硬汉嵌入式 - 博客园

📎安富莱_STM32-V7开发板_用户手册,含BSP驱动包设计(V3.5).pdf

1.1. FDCAN协议 - 简介(须知):

STM32中FDCAN和传统的bxCAN的区别:

  • 两者协议本身的区别
  • 在STM32中这两个外设也有较大不同

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

很多情况下称 CAN2.0bxCAN

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

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

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

综上,其实可以这样子理解CAN协议

  • 第一代CAN协议:CAN
  • 第二代CAN协议:CAN2.0/bxCAN
  • 第三代CAN协议:CAN-FD/FDCAN

硬件层未改变,升级了协议层

协议都是向下兼容的!

1.2. FDCAN控制器 - 简介:

1.2.1. STM32H7的CAN FD简介:

STM32H7的CAN FD符合ISO 11898-12015标准。STM32器件上的FDCAN的功能如下所示:

  1. 符合CAN协议2.0版A,B和ISO 11898-1:2015,-4。

ISO 11898-1:2015,-4是classic CAN和CAN FD协议标准

综上,协议是向下兼容的

  1. 可访问的10 KB RAM内存,最多可分配2560个字。
  2. 改进了接收过滤。
  3. 两个可配置的接收FIFO。
  4. 多达64个专用接收缓冲区。
  5. 接收高优先级消息时的单独信令。
  6. 多达32个专用发送缓冲区。
  7. 可配置的发送FIFO和发送队列。
  8. 可配置的发送事件FIFO。
  9. 时钟校准单元。
  10. 收发器延迟补偿。

1.2.2. CAN FD特性:

(1)兼容经典CAN,可以遵循ISO 11898-1做数据收发。

(2)提升错误检测,支持高达CRC 21位的校验和。

(3)消息优先级。

(4)保证延迟时间。

(5)配置灵活性。

(6)具有时间同步的组播接收。

(7)系统范围内的数据一致性,每条消息最多64个字节。

(8)多主机。

(9)错误检测和信号。

(10)区分节点的临时错误和永久性故障以及自动关闭缺陷节点。

1.3. FDCAN控制器 - 与CAN控制器的区别/改进:

偏向于了解,因为目前开发主要是用FCAN兼容CAN的方式;

用到什么学什么

【STM32H7教程】第91章 STM32H7的FDCAN总线基础知识和HAL库API - 硬汉嵌入式 - 博客园

1.3.1.1. 最大传输速率:8000Kpbs,(CAN:1000Kpbs)
1.3.1.2. 一帧最大字节数:64字节,(CAN:8字节)

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

1.3.1.3. FIFIO和Filter分布变化:
  • bxCAN中FIFIO和Filter都是设定好一定组数的,我们是现成的拿来用
  • 而FDCAN中提供了一定的内存,用户可以手动分配各个FIFIO和Filter的大小

1.3.1.4. 其他(待补充)

此处为语雀内容卡片,点击链接查看:【知识点补充】CAN(CAN2.0/bxCAN)、FDCAN(CAN-FD)概念补充 · 语雀

1.4. FDCAN控制器 - 结构框图

通过这个框图要了解到以下信息:

(1)CANFD1和CANFD2共用一个RAM空间。

(2)每个CANFD都有自己的内核。

(3)CAN内核实现协议控制和收发移位寄存器。

(4)Tx handler控制消息从CAN消息RAM到CAN内核。

(5)Rx handler控制CAN内核到CAN消息RAM。

首先从整个CAN宏观来看FIFO在消息RAM中,该消息RAM总共有10KB,此RAM为CAN的专用RAM,和系统SRAM没有关系。此RAM已经划分好了每个区域的用途和大小,如下图。

由此图可见接收FIFO的每个单元的深度均为64,无论帧长为多少,深度都是64,只不过在用作普通CAN时只用了前8个单元。

每个FIFO总共有这样的单元64个,接收部分总共有这样的两个FIFO。

即FIFO全压满时可以暂存128条信息,这非常利于批量处理信息。

发送部分的FIFO与发送同理,只是空间小一些。

当然,消息RAM是FDCAN1和FDCAN2公用的,在实际使用中如果两个FDCAN都是用了,分配FIFO时要注意。

在初期学习时发现大多数参考资料和历程均只用了1个FIFO单元,之后仔细看了数据手册才发现这些资源是不用白不用哦。

1.5. FDCAN控制器 - 波特率计算公式与工具(得到两组Prescaler、TimeSeg1、TimeSeg2,Sync Jump Width):

FDCAN的波特率参数不太一样,有两组(数据段,仲裁段)参数(4个)

📎ZCANPRO.zip

1.5.1. 波特率的计算公式

波特率的计算公式 (这里只是了解一下波特率是怎么计算的 后续我们可以使用波特率计算器)

  • FDCANFrequency(FDCAN频率):即为上述我们配置的FDCAN时钟频率,为120MHz
  • Prescaler(预分频器):FDCAN频率还需要除预分频系数得到一个更小的频率 方便我们后续计算 假设TimeSeg1和TimeSeg2的值范围在1-32和1-16 如果FDCAN频率过大算出来可能达不到我们想要的波特率。

比如说我们需要配置1Mdps波特率 如果没有Prescaler(预分频器) BaudRate = 120MHz / (1+32+16) = 2.449Mdps 达不到我们的期望值 而TimeSeg1和TimeSeg2的值已达到最大值。

TimeSeg1和TimeSeg2即我们需要配置的参数。具体原理可参考下图。

我们回到STM32CubeMX 中的FDCAN1配置界面

  • 我们会发现怎么有两个不同的Prescaler、TimeSeg1、TimeSeg2组合,一个是Data,一个是Nominal 即数据和仲裁 即对应的CAN通信的数据域和仲裁域
  • 两者还有一个参数一样 Sync Jump Width(同步跳转段的宽度)缩写SJW 后续是我们配置选择TimeSeg1和TimeSeg2的重要参考

数据段-波特率参数

仲裁段-波特率参数

1.5.2. 打开后,设备类型选择任意带有CANFD并且TCP和UDP结尾的都可以(计算方式是一样的 我们选择CANFDNET_TCP)

1.5.3. CAN(FD)波特率推荐设置

在看了网上很多资料和别人配置的代码 发现很多配置都符合这些规则,应该是一种根据CAN通信原理最优化配置的规范,那我们配置波特率也按这个设置来。

  1. BRP尽量小:即Prescaler(预分频器)的值尽量要小。
  2. 不用管,因为经典CAN仲裁域和数据域BRP是一样的。
  3. SJW尽量大且尽量保持与TSEG2一致 :即Sync Jump Width和TimeSeg2两个参数尽量保证一致且较大。
  4. 我们配置的波特率一般为经典CAN模式支持的最大值即1Mbps 1000kbps>800kbps 所以我们选择采样率(SMP)为 75%左右的参数
  5. 不用管
  6. 不用管

所以我们选择参数的要求是 采样率(SMP)为75%左右的同时 BRP(Prescaler(预分频器))的值尽量要小,且Sync Jump Width和TimeSeg2两个参数尽量保证一致且较大。

1.5.4. 用计算器算一下理想的波特率参数(案例)

首先将时钟改为我们CubeMX配置的FDCAN时钟频率120MHz 波特率改为我们期望的1Mbps点击设备类型旁边的计算得到一组参数,点击SMP 参数会根据采样率大小进行排序

我们找到采样率(SMP)为75%左右的参数

根据 BRP(Prescaler(预分频器)尽量较小 我们应该选择BRP为2的那套参数,但实际上并不行。因为我们回到CubeMX,点击Data Time Seg1 可以看到 该参数的最大值为32 如果选择44,15,1,2那套参数 Data Time Seg1 最大值会超出 ,所以我们选择BRP为3的那套。

最后我们将SJW(同步跳转宽度)改为10 即满足TimeSeg2两个参数尽量保证一致且较大 验证一下。发现该组参数满足上述所有条件。

所以最后

Prescaler = 3 Data Time Seg1 = 29 Data Time Seg2 = 10 Sync Jump Width = 10【一共四个参数,比CAN多一个】

1.6. 后面自己看手册学吧

简单学要懂的!

1.7. FDCAN控制器 - RAM区管理

1.8. FDCAN控制器 - 过滤区

CAN 总线的标识符过滤器很重要

配置思路差不多

过滤器是用于接收的,发送的时候无需设置过滤器,这点一定要整明白。

根据发送端发送来的帧数据, 通过标识符过滤器就可以设置仅接收需要接收的帧数据有效降低 CPU 多余的处理时间。

FDCAN 外设可以配置两套验收滤波器:用于存储或拒绝接收到的消息。

  • 一套用于标准标识符
  • 一种是扩展标识符

128 个过滤元素可以配置用来配置 128 个 11bit 标准 ID 和 64 个过滤元素可以用来配置 64 个 29bit 扩展 ID。

1.9. FDCAN控制器 - 接收区

相比CAN控制器,新增功能:

  • 多了个专用的RxBuffer
  • FIFO0、FIFO1、RxBuffer的元素个数(0-64个)和存储数据字节大小(8-64字节)都可以配置

CAN FD控制器的接收区分为两个,Rx FIFO和专用Rx缓冲区

1.9.1. FIFO0和FIFO1:

可以在 CAN 消息 RAM 中配置两个 Rx FIFO(FIFO0和FIFO1)。

  • FIFO中的FIFO元素个数可配置(0-64个FIFO元素)
    • 每个 Rx FIFO 部分最多可存储 64 个元素。
    • 每个元素存储在一个 Rx FIFO 元素中。
  • FIFO元素大小可配置(0-64字节)
    • 可以通过 FDCAN_RXESC 寄存器分别为每个 Rx FIFO 配置 Rx FIFO 元素的大小。
      • 一个大小 Rx FIFO 元素由前面 RAM 管理中指定的公式定义。
      • Rx FIFO 元素大小定义了可以存储一个接收元素的数据字段字节数

1.9.2. 专用的Rx Buffer:

FDCAN 支持多达 64 个专用 Rx buffers。

  • Rx buffers的个数可配置
    • 每个专用的 Rx buffers 可以存储一个元素。
  • 每个Rx buffers的数据字段大小可配置(0-64字节)
    • 专用 Rx buffers 的大小可以通过 FDCAN_RXESC 寄存器进行配置,由前面 RAM 组织中描述的公式定义。
    • Rx buffers 大小定义可以存储多少个接收元素的数据字段字节。

1.9.3. 两者区别:

如前面所述,FDCAN 具有两种机制:

专用 Rx buffer 或 Rx FIFO 0/1 可以将配置为存储接收到的元素。

1.10. FDCAN控制器 - 发送区

暂时还当CAN控制器的发送邮箱来用

1.10.1. Tx Events

1.10.2. Tx Buffers

1.10.3. Tx FIFO(普通FIFO模式/Queue模式)

1.11. FDCAN控制器 - 工作模式

STM32H7的CAN FD外设四种测试模式:限制操作模式,总线监控模式,外部环回模式和内部环回模式 - STM32H7 - 硬汉嵌入式论坛 - Powered by Discuz!

在FDCAN的操作模式下,除了正常操作外,还有几种测试模式可用。

那里测试模式只能用于生产测试或自测以及校准单元。

必须将FDCAN_CCCR中的TEST位设置为1,以允许对FDCAN测试寄存器和存储器的写访问。测试模式和功能的配置。

FDCAN兼容CAN2.0的工作模式:

  • 正常模式(Normal mode)

FDCAN以下列模式之一工作:

  1. 限制操作模式(Restricted-operation mode)
  2. 总线监控模式(Bus-monitoring mode)
  3. 外部环回模式(External loop-back mode)
  4. 内部环回模式(Internal loop-back mode)

1.11.1. 限制操作模式(Restricted-operation mode)

注: 只要FDCAN处于受限操作模式,FDCAN_TX引脚就处于隐性状态。

显性位被传输,以此确认接收到有效帧。

在受限操作模式下,FDCAN能够:

  • 接收数据帧
  • 接收远程帧
  • 确认有效帧

该模式不支持:

  • 数据帧发送
  • 远程帧发送
  • 活动错误帧或过载帧发送

通过FDCAN_CCCR中的ASM位将FDCAN设置为受限操作模式。当Tx handler 无法从CAN读取消息RAM准时或时钟校准激活时,将自动进入受限操作模式。在这种模式下,应用程序测试不同的比特率,并在应用程序收到有效的帧之后退出受限操作模式。

下图说明了在受限操作模式下FDCAN_TX和FDCAN_RX引脚的连接

1.11.2. 总线监控模式(Bus-monitoring mode)

为了分析总线上的通信量而又不受主要位传输的影响,用户可以设置FDCAN_CCCR中的MON位,可以在总线监视模式下使用FDCAN。在总线监视模式下,FDCAN能够:

  • 接收有效的数据帧
  • 接收有效的远程帧

该模式不支持:

  • 传输开始
  • 确认有效帧(与受限操作模式不同)

在总线监视模式下,FDCAN仅在总线上发送隐性位。

下图显示了总线监视模式下FDCAN_TX和FDCAN_RX引脚的连接。

1.11.3. 测试/回环模式(内部环回模式、外部环回模式):内部环回是内部收发

CAN总线协议属于链路层协议,在使用时必须有完好可以使用的物理层做支持,但是在调试时,不确定物理层是否正常工作时,可以先调试链路层的设置是否正确。

STM32H7的FDCAN有内部回环和外部回环两种,常用的是内部回环模式。

  • 内部回环模式将完全断开与CAN相连接的IO,并置Tx引脚为1。
  • 而外部回环模式可以用于物理层测试,此时需要有另一台可以在正常模式下工作的主机,使用外部回环模式会将数据在内部直接接收的同时从CAN的Tx引脚发送出去,若另一台主机此时正确接收了消息,则证明测试主机的物理层的发送功能完好。
1.11.3.1. 外部环回模式(External loop-back mode)

提供此模式用于硬件自检。

用户可以通过以下方式将FDCAN设置为外部环回模式:

将1写入FDCAN_TEST中的LBCK位,并将0写入FDCAN_CCCR中的MON位。

FDCAN对待自己的发送的消息作为已接收的消息,如果它们通过Rx FIFO中的接受过滤,则将其存储。为了独立于外部信号,FDCAN忽略确认错误(隐性位在确认插槽中采样)。 FDCAN实现内部TX和RX连接。

下图显示了外部环回模式下的FDCAN_TX和FDCAN_RX引脚的连接。

1.11.3.2. 内部环回模式(Internal loop-back mode)

提供此模式用于硬件自检。

用户可以通过以下方式将FDCAN设置为内部环回模式:

向FDCAN_TEST中的LBCK位写入1,并向FDCAN_CCCR中的MON位写入1。

可以测试FDCAN,而不会影响连接到FDCAN_TX和FDCAN_RX的正在运行的CAN系统针脚。 FDCAN_RX引脚与FDCAN断开连接,并且FDCAN_TX引脚保持隐性。

下图显示了内部环回模式下FDCAN_TX和FDCAN_RX引脚的连接

2. STM32 FDCAN Controller(FDCAN控制器)外设开发:

STM32H7系列FDCAN配置成经典CAN的经验教程和注意事项_stm32h7 can-CSDN博客

主要是在上面原理部分

登录 - Gitee.com

STM32H7的CAN FD外设四种测试模式:限制操作模式,总线监控模式,外部环回模式和内部环回模式 - STM32H7 - 硬汉嵌入式论坛 - Powered by Discuz!

2.1. 基于HAL库 - 开发FDCAN外设:

这里的开发环境是:STM32H743VGT6

2.1.1. FDCAN的HAL库分析:

2.1.1.1. FDCAN结构体
2.1.1.2. FDCAN的函数
2.1.1.3. FDCAN的中断回调函数

记得勾选中断

/**
  * @brief This function handles FDCAN1 interrupt 0.
  */
void FDCAN1_IT0_IRQHandler(void)
{
  /* USER CODE BEGIN FDCAN1_IT0_IRQn 0 */

  /* USER CODE END FDCAN1_IT0_IRQn 0 */
  HAL_FDCAN_IRQHandler(&hfdcan1);
  /* USER CODE BEGIN FDCAN1_IT0_IRQn 1 */

  /* USER CODE END FDCAN1_IT0_IRQn 1 */
}
==============================================================================
                          ##### Callback functions[所有回调函数] #####
==============================================================================
    [..]
    This subsection provides the following callback functions:
      (+) HAL_FDCAN_ClockCalibrationCallback
      (+) HAL_FDCAN_TxEventFifoCallback
      (+) HAL_FDCAN_RxFifo0Callback
      (+) HAL_FDCAN_RxFifo1Callback
      (+) HAL_FDCAN_TxFifoEmptyCallback
      (+) HAL_FDCAN_TxBufferCompleteCallback
      (+) HAL_FDCAN_TxBufferAbortCallback
      (+) HAL_FDCAN_RxBufferNewMessageCallback
      (+) HAL_FDCAN_HighPriorityMessageCallback
      (+) HAL_FDCAN_TimestampWraparoundCallback
      (+) HAL_FDCAN_TimeoutOccurredCallback
      (+) HAL_FDCAN_ErrorCallback
      (+) HAL_FDCAN_ErrorStatusCallback
      (+) HAL_FDCAN_TT_ScheduleSyncCallback
      (+) HAL_FDCAN_TT_TimeMarkCallback
      (+) HAL_FDCAN_TT_StopWatchCallback
      (+) HAL_FDCAN_TT_GlobalTimeCallback


===========FDCAN(1/2)的接收中断回调函数:===========
    分别是FIFO0和FIFO1的中断回调,根据Cube和过滤器配置的FIFO区
    收到数据时就会进入对应的中断,然后通过HAL_FDCAN_GetRxMessage获取接收的各种数据
    再根据自己的通信协议进行数据处理
/**
  * @brief  Rx FIFO 0 callback.
  * @param  hfdcan pointer to an FDCAN_HandleTypeDef structure that contains
  *         the configuration information for the specified FDCAN.
  * @param  RxFifo0ITs indicates which Rx FIFO 0 interrupts are signaled.
  *         This parameter can be any combination of @arg FDCAN_Rx_Fifo0_Interrupts.
  * @retval None
  */
__weak void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hfdcan);
  UNUSED(RxFifo0ITs);

  /* NOTE: This function Should not be modified, when the callback is needed,
            the HAL_FDCAN_RxFifo0Callback could be implemented in the user file
   */
}

FDCAN1_RxFifo0RxHandler是我自己定义的数据处理函数

2.1.2. FDCAN的CubeMX配置项分析:

FDCAN时钟配置:(APB1时钟)


基础配置项(除去波特率部分):


  • Frame Format (帧格式)

因为我们配置的是经典CAN模式

    • 选择Classic mode,经典模式
    • Classic modeFD mode without BitRate Switching:波特率一致,仲裁段和数据点波特率相同
    • FD mode with BitRate Switching:双波特率,仲裁段和数据点波特率不同

  • Mode (模式)

    • 选择Normal mode,正常即可
    • 限制操作模式(Restricted-operation mode)
    • 总线监控模式(Bus-monitoring mode)
    • 外部环回模式(External loop-back mode)
    • 内部环回模式(Internal loop-back mode)

  • Auto RetransmissionTransmit Pause
    • 这两个都是FDCAN的特性 全部Disable就行
  • Protocol Exception (协议异常处理)
    • 这个打不打开都一样,打开可能有一点点好处,我这里选择了 Enable,关闭也无所谓

……

波特率部分在下面讲


  • Message Ram Offset (RAM消息地址偏移)
    • 取值范围:0-2560
    • 使用几个FDCAN就将2560平均分给几个,比如使用一个FDCAN1就填0,范围就是将RAM地消息0-2560分给FDCAN1。
    • 使用FDCAN1和FDCAN2:
      • FDCAN1就填0,FDCAN1范围就是0-1280
      • FDCAN2就填1280,FDCAN2范围则是1281-2560。
    • 三个就以此类推。
  • Std Filters Nbr(标准ID过滤数量) :至少取>=1的值
    • 这个参数的配置非常容易踩坑,它的参数作用是规定你需要过滤掉标准CANID的数量
    • 通常我们我们不需要过滤任何ID,但这里绝对不能设置为0,如果设置为0则进入不了CAN的中断,必须设置为1即以上
      • 设置为1也不代表我们需要在代码初始化过滤器时过滤掉一个ID,可将一个参数设置为0x00000000,即可不过滤掉任何ID。
  • Ext Filters Nbr (扩展ID过滤数量)
    • 经典CAN没有扩展ID,就写0。
    • 使用CAN-FD就另说,……(其他)

波特率配置项:0-8000kpbs

重申:获取波特率参数(四个参数-Prescaler/xxx Time Seg1/xxx Time Seg2/xxx Sync Jump Width):

Prescaler = BRP

Time Seg1 = TSEG1

Time Seg2 = TSEG2

Sync Jump Width(同步跳转段的宽度) = 10

【一共四个参数,比CAN多一个;SJW参数是数据/仲裁段共用的!!!】

  • 两个不同的Prescaler、TimeSeg1、TimeSeg2组合,一个是Data,一个是Nominal 即数据和仲裁 即对应的CAN通信的数据域和仲裁域
  • 两者还有一个参数一样 Sync Jump Width(同步跳转段的宽度)缩写SJW 后续是我们配置选择TimeSeg1和TimeSeg2的重要参考

  • 补充:
    • Baud Rate是自动计算的最终波特率
    • 数据段(最大波特率8Mbps)和仲裁段(最大波特率1Mbps)的波特率可以不同,也可以相同

数据域的波特率配置:


  • Data Sync Jump Width (数据域的同步跳转宽度)
    • 根据波特率计算器得到的填入
  • Data PrescalerData Sync Jump WidthData Time Seg1Data Time Seg2(数据段 - 波特率参数)
    • 根据波特率计算器得到的填入
仲裁域的波特率配置:


  • Nominal Sync Jump Width (仲裁域的同步跳转宽度)
    • 根据波特率计算器得到的填入
  • Data PrescalerData Sync Jump WidthData Time Seg1Data Time Seg2(仲裁段 - 波特率参数)
    • 根据波特率计算器得到的填入

接收区配置项:可同时接收(0-64)个(8-64)字节

Nbr结尾代表对应接收区的的数量,Size就是一个接收区开辟的最大字节数。

【知识点和案例】

接下来我们要明确一个概念:即你的一路CAN中在不加延时的情况下,一次最多能发送或接收的最大帧数据包的数量。我把它叫做CAN收发最大带宽数

这么说可能有点云里雾里,举两个例子:

  • 以达妙系列电机为例,在不加延时的情况下,一次只能给两个电机发送指令,如果连续给三个达妙电机发送控制包,第三个电机将收不到控制指令也不会返回电机数据(达妙电机控制方式就是发一帧回一帧,发不出去肯定就收不到)所以这路CAN的最大发送带宽数和最大接受带宽数都是2。
  • 再拿大疆的电机(3508、6020等)举例,官方手册上说一次两帧数据包最多可以控制8个电机且收到电机反馈数据(实际测试大概只能控制6个),那大疆的电机CAN的最大发送带宽数为2,最大接收带宽数为8(实际上4-6)

这个概念关乎我们配置接下来的接收区和发送区的FIFO数量和字节数。


接收区:分为Fifo接收和Buffers接收
  • FIFO接收

其中Fifo区有两个,Fifo0和Fifo1,选择哪个Fifo不仅要在CubeMX配置,在程序中的CAN过滤器初始化中也要配置 。Nbr结尾代表对应接收区的的数量,Size就是一个接收区开辟的最大字节数。

  • Buffers接收

首先我们不使用Buffers接收,RxBuffers Nbr直接填0。

那我们Fifo0和Fifo1数量如何选择,就关乎到上面说的CAN收发最大带宽数,开辟的数量不能小于CAN的接收最大带宽数,(发送也是一样),比如我这路CAN的最大接收带宽数为2,如果只开辟一个Fifo区的话,那第二个电机的数据就会收不到,所以我们配置的数量要大于等于2。

那有人可能要问了,我要控制四个电机发送四帧数据包不是要开辟四个吗,上面说了,是不加延时的情况下最大的接收数量,通过每两电机延时200us-1ms可以控制两个以上,Fifo数量也只需要2个,当然多开一点也无所谓。

Rx Fifo Elmt Size我们选择8bytes就行,经典CAN一帧数据包最大就8bytes,当然选择多的也可以。具体使用哪个Fifo在Cube里配置完后,程序中CAN过滤器初始化也要选择!!(后面会说)

我们使用一路CAN控制达妙电机,并用Fifo0来接收电机数据的最低配置为

  • Rx Fifo0 Elmts Nbr :2
  • Rx Fifo0 Elmt Size :8 bytes data filed

一次接收2个一节的信息

  • Rx Fifo1 Elmts Nbr :0(不用的Nbr给0)
  • Rx Fifo1 Elmt Size :8 bytes data filed
  • Rx Buffers Nbr :0(不用的Nbr给0)
  • Rx Buffer Size :8 bytes data filed


FIFO0和FIFO1配置:

不用的Nbr给0

  • Rx Fifo0 Elmts Nbr(指定Rx FIFO 0元素的数量)
    • 0-64个
  • Rx Fifo0 Elmt Size(指定Rx FIFO 1元素的数据字段大小(字节)

    • 8-64 bytes data filed(字节)
  • Rx Fifo1 Elmts Nbr(指定Rx FIFO 1元素的数量)
    • 0-64个
  • Rx Fifo1 Elmt Size(指定Rx FIFO 1元素的数据字段大小(字节)
    • 8-64 bytes data filed(字节)

Rx Buffer配置:

不用的Nbr给0

  • Rx Buffers Nbr(指定Rx Buffer元素的数量)
    • 0-64个
  • Rx Buffer Size(每个Rx Buffer元素的数据字段大小)
    • 8-64 bytes data filed(字节)

发送区配置:可同时发送(0-32)个(8-64)字节

发送区和接收区差不多,不过多赘述了

不使用Events和Buffers发送,直接写0,我们采用FIFO发送不用Queue(队列),选择FIFO mode

Nbr数量根据CAN收发最大带宽数配置,大于等于这个数,Size选择8字节就行,大一点也可以


Tx Events配置:
  • Tx Events Nbr(指定Tx Events元素的数量)
    • 0-32个
Tx Buffers配置:
  • Tx Buffers Nbr(指定Tx Buffers元素的数量)
    • 0-32个
Tx Fifo/Tx Queue配置:
  • Tx Fifo Queue Elmts Nbr(指定Tx Fifo/Tx Queue元素的数目)
    • 0-32个
  • Tx Fifo Queue Mode(选择FIFO模式)

    • Tx FIFO模式 / Tx 队列模式
Tx元素数据字段大小配置:(必配置)
  • Tx Elmt Size(指定Tx元素数据字段大小)

    • 8-64 bytes data filed

中断配置(NVIC):


【开发流程】FDCAN开发编程核心(配置完CubeMX后)

CubeMX配置部分在分析中有介绍

数据结构设计:

bsp_FDCAN/CAN结构体(拿FDCAN举例)

/**
 * @name:   FDCAN_data
 * @brief   自定义FDCAN数据结构体
 * @note    none
 */
typedef struct
{
    /* FDCAN句柄 */
    FDCAN_HandleTypeDef *hFDCanHandle;

    /* FDCAN发送结构体 */
    FDCAN_TxHeaderTypeDef hTxHeader;

    /* FDCAN发送数据区 */
    uint8_t Tx_Data_Buf[64];	            // FDCAN数据帧的数据段长度(0-8)
    
    /* FDCAN接收结构体 */
    FDCAN_RxHeaderTypeDef hRxHeader;
    
    /* FDCAN接收数据区 */
    uint8_t Rx_Data_Buf[64];			    
} 
FDCAN_data;

FDCAN_data 

提前定义好数据帧

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】:传统帧格式(兼容CAN2.0),正常工作模式,1Mpbs,发送区(普通FIFO),接收区(FIFO)

开发环境:

  • 芯片型号:STM32H743VGT6
  • 是否有OS:FreeRTOS
CubeMX配置:

后面的例程默认在这上面更改

Project程序开发:
效果:

【例程2】:传统帧格式(兼容CAN2.0),测试模式(内部环回模式),1Mpbs,发送区(普通FIFO),接收区(FIFO)

注意,此工作状态数据字段最大为8

工程:

📎241022_H743_RTOS_FDCAN.zip

效果:

3. FDCAN外设开发问题和调试

3.1. 开发问题

3.1.1.1. STM32H7双路CAN踩坑记录_stm32h7 can-CSDN博客
3.1.1.2. FDCAN发送数据包函数问题(和安富莱讲的不太一样的点)【数据长度不用右移16位】

错误:

正确:

3.1.1.3. 脑袋短路了,因为第一次测试的时候是写FDCAN兼容CAN,所以数据字段还是最大8字节的

3.1.1.4. 注意需要FDCAN兼容CAN的方式与传统CAN通信(CAN设备通信都要),波特率要一致
3.1.1.5. 如果参考安富莱的FDCAN工程,虽然全面,但是仅是为了实现can与fdcan兼容can通信,不用那么多;这也导致我在接收中断写错了一些

STM32H7 FDCAN兼容普通CAN使用 基于CubeMX配置_stm32cubeide h7配置fdcan作为普通can-CSDN博客

如果仅实现上面的目的,这些会导致无法解析数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值