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博客
开发和原理-挺全: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 / bxCAN
和 CAN-FD
很多情况下称 CAN2.0
为 bxCAN
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的功能如下所示:
- 符合CAN协议2.0版A,B和ISO 11898-1:2015,-4。
ISO 11898-1:2015,-4是classic CAN和CAN FD协议标准
综上,协议是向下兼容的
- 可访问的10 KB RAM内存,最多可分配2560个字。
- 改进了接收过滤。
- 两个可配置的接收FIFO。
- 多达64个专用接收缓冲区。
- 接收高优先级消息时的单独信令。
- 多达32个专用发送缓冲区。
- 可配置的发送FIFO和发送队列。
- 可配置的发送事件FIFO。
- 时钟校准单元。
- 收发器延迟补偿。
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个)
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通信原理最优化配置的规范,那我们配置波特率也按这个设置来。
- BRP尽量小:即Prescaler(预分频器)的值尽量要小。
- 不用管,因为经典CAN仲裁域和数据域BRP是一样的。
- SJW尽量大且尽量保持与TSEG2一致 :即Sync Jump Width和TimeSeg2两个参数尽量保证一致且较大。
- 我们配置的波特率一般为经典CAN模式支持的最大值即1Mbps 1000kbps>800kbps 所以我们选择采样率(SMP)为 75%左右的参数
- 不用管
- 不用管
所以我们选择参数的要求是 采样率(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以下列模式之一工作:
- 限制操作模式(Restricted-operation mode)
- 总线监控模式(Bus-monitoring mode)
- 外部环回模式(External loop-back mode)
- 内部环回模式(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博客
主要是在上面原理部分
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 Retransmission 与 Transmit 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 Prescaler、Data Sync Jump Width、Data Time Seg1、Data Time Seg2:(数据段 - 波特率参数)
-
- 根据波特率计算器得到的填入
仲裁域的波特率配置:
- Nominal Sync Jump Width (仲裁域的同步跳转宽度) :
-
- 根据波特率计算器得到的填入
- Data Prescaler、Data Sync Jump Width、Data Time Seg1、Data 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即可】
如果有目标接收需求可加
-
- 定义接收过滤器类型定义结构体(
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】:传统帧格式(兼容CAN2.0),正常工作模式,1Mpbs,发送区(普通FIFO),接收区(FIFO)
开发环境:
- 芯片型号:STM32H743VGT6
- 是否有OS:FreeRTOS
CubeMX配置:
后面的例程默认在这上面更改
Project程序开发:
效果:
【例程2】:传统帧格式(兼容CAN2.0),测试模式(内部环回模式),1Mpbs,发送区(普通FIFO),接收区(FIFO)
注意,此工作状态数据字段最大为8
工程:
效果:
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博客
如果仅实现上面的目的,这些会导致无法解析数据