Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
CANFD_fdcan
一文读懂CANFD的发送模式
发布于 2022-01-09 21:05:42 浏览:3481
订阅该版
[tocm] # 前言 本文主要讲解 CANFD 的发送模式,CANFD 支持的发送模式有 3 种:TXBUFF, TXQUEUE, TXFIFO,另外发送模式还支持发送事件。对 CANFD 的接收模式有兴趣的可以参看我之前的文章《[一文读懂CANFD的接收模式](https://club.rt-thread.org/ask/article/3282.html)》。本文以 STM32H7 为为例,将对以下的关键词进行详细的讲解。 - TXBUFF - TXQUEUE - TXFIFO - 发送事件 - 事件戳 ## 一, CANFD 的三种发送模式 CANFD 支持三种发送模式,再配置过程中可以选择其中一种或者两种(TXQUEUE 和 TXFIFO 不能同时使用),使用两种发送方式的的组合可以是:TXBUFF + TXQUEUE 和 TXBUFF + TXFIFO。 三者的区别如下: ![image-20220109161152340.png](https://oss-club.rt-thread.org/uploads/20220109/55b111dde4b1d383f42ad741adf38e9b.png.webp) ### 1. TXBUFF 发送模式 TXBUFF 的发送方式为专用发送发送缓存区,每个换缓存区仅能保存一帧报文,根据对 Message RAM 的配置可以选择将报文放入指定编号的发送缓存区, 最大支持 32 个专用发送缓存区。 使用 TXBUFF 发送模式时,需要 Message RAM 的配置方式为: ``` hfdcan1.Init.TxBuffersNbr = 10; // 设置 TXBUFF 的数量为 10 ``` 使用 TXBUFF 的发送方式可以使能发送完成中断,开启方法为: ``` /* 开启 TXBUFF0 发送完成中断,注意第三个参数使用宏的方式 */ HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_TX_COMPLETE , FDCAN_TX_BUFFER0); /* 发送完成中断回调函数 */ void HAL_FDCAN_TxBufferCompleteCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t BufferIndexes) { if (hfdcan == &hfdcan1) { /* TODO */ HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_TX_COMPLETE, BufferIndexes); } } ``` 使用 TXBUFF 发送一帧报文的示例: ``` FDCAN1_TxHeader.Identifier = 0x103; FDCAN1_TxHeader.IdType = FDCAN_STANDARD_ID; FDCAN1_TxHeader.TxFrameType = FDCAN_DATA_FRAME; FDCAN1_TxHeader.DataLength = FDCAN_DLC_BYTES_8; FDCAN1_TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE; FDCAN1_TxHeader.BitRateSwitch = FDCAN_BRS_ON; FDCAN1_TxHeader.FDFormat = FDCAN_FD_CAN; FDCAN1_TxHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS; FDCAN1_TxHeader.MessageMarker = 22; /* 将要发送的报文放入发送缓存区 */ HAL_FDCAN_AddMessageToTxBuffer(&hfdcan1, &FDCAN1_TxHeader, rxdata1, FDCAN_TX_BUFFER0); FDCAN1_TxHeader.Identifier = 0x102; HAL_FDCAN_AddMessageToTxBuffer(&hfdcan1, &FDCAN1_TxHeader, rxdata1, FDCAN_TX_BUFFER1); FDCAN1_TxHeader.Identifier = 0x101; HAL_FDCAN_AddMessageToTxBuffer(&hfdcan1, &FDCAN1_TxHeader, rxdata1, FDCAN_TX_BUFFER2); FDCAN1_TxHeader.Identifier = 0x100; HAL_FDCAN_AddMessageToTxBuffer(&hfdcan1, &FDCAN1_TxHeader, rxdata1, FDCAN_TX_BUFFER3); HAL_FDCAN_EnableTxBufferRequest(&hfdcan1, FDCAN_TX_BUFFER0 | FDCAN_TX_BUFFER1 | FDCAN_TX_BUFFER2 | FDCAN_TX_BUFFER3); ``` 将发送消息的请求给到 CANFD 控制之后,CANFD 会自动将 TXBUFF 的消息发送,会根据保存在 TXBUFF 中的消息的帧 ID 的大小来发送 ,帧 ID 数值越小,优先级越高。 ![image-20220109162033407.png](https://oss-club.rt-thread.org/uploads/20220109/3146e43fcde01ce69d6f8195d8abb178.png) ### 2. TXQUEUE 发送模式 TXQUEUE 的发送方式为专用发送队列,最大支持 32 个深度的发送队列,将需要放的发送的数据放入发送队列之后,硬件会根据报文的帧 ID 不同,按照帧 ID 数值越小的先发送,例如压了2帧数据,帧 ID 分别是 0x101 和 0X102,先压入 0x102 在压入 0x101 ,那么先发出去的消息是 0x101 的报文。 使用 TXQUEUE 发送模式时,需要 Message RAM 的配置方式为: ``` hfdcan1.Init.TxFifoQueueElmtsNbr = 10; /* 使能 QUEUE 模式 */ hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_QUEUE_OPERATION; ``` 使用 TXBUFF 发送报文的示例: ``` FDCAN1_TxHeader.Identifier = 0x102; FDCAN1_TxHeader.IdType = FDCAN_STANDARD_ID; FDCAN1_TxHeader.TxFrameType = FDCAN_DATA_FRAME; FDCAN1_TxHeader.DataLength = FDCAN_DLC_BYTES_8; FDCAN1_TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE; FDCAN1_TxHeader.BitRateSwitch = FDCAN_BRS_ON; FDCAN1_TxHeader.FDFormat = FDCAN_FD_CAN; FDCAN1_TxHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS; FDCAN1_TxHeader.MessageMarker = 0; FDCAN1_TxHeader1.Identifier = 0x101; FDCAN1_TxHeader1.IdType = FDCAN_STANDARD_ID; FDCAN1_TxHeader1.TxFrameType = FDCAN_DATA_FRAME; FDCAN1_TxHeader1.DataLength = FDCAN_DLC_BYTES_8; FDCAN1_TxHeader1.ErrorStateIndicator = FDCAN_ESI_ACTIVE; FDCAN1_TxHeader1.BitRateSwitch = FDCAN_BRS_ON; FDCAN1_TxHeader1.FDFormat = FDCAN_FD_CAN; FDCAN1_TxHeader1.TxEventFifoControl = FDCAN_STORE_TX_EVENTS; FDCAN1_TxHeader1.MessageMarker = 1; /* 压入 QUEUE 的帧 ID 顺序为:0x102, 0x102, 0x102, 0x101 */ HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &FDCAN1_TxHeader, rxdata1); HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &FDCAN1_TxHeader, rxdata1); HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &FDCAN1_TxHeader, rxdata1); HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &FDCAN1_TxHeader1, rxdata1); ``` 依次收到的报文的顺序是: ![image-20220109155650682.png](https://oss-club.rt-thread.org/uploads/20220109/6a57bf312f3d7cced980de5be0023aee.png) 在发送第二帧的报文的时候检测到有多个帧 ID 不同的报文,QUEUE 模式会优先发送帧 ID 较小(优先级更高的消息)。 ### 3. TXFIFO 发送模式 TXFIFO 的发送方式为先入先出,最大支持 32 个深度的发送 FIFO,将需要放的发送的数据放入发送 FIFO 之后,硬件会根据报文的放入的顺序,依次发送。 使用 TXFIFO 发送模式时,需要 Message RAM 的配置方式为: ``` hfdcan1.Init.TxFifoQueueElmtsNbr = 10; /* 使能 QUEUE 模式 */ hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; ``` 使用 TXFIFO 的发送方式可以使能 FIFO 空中断,开启方法为: ``` /* 开启 TXFIFO 空中断 */ HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_TX_FIFO_EMPTY , 0); /* FIFO 空中断回调函数 */ void HAL_FDCAN_TxFifoEmptyCallback(FDCAN_HandleTypeDef *hfdcan) { if (hfdcan == &hfdcan1) { /* TODO */ HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_TX_FIFO_EMPTY , 0); } } ``` 将发送模式修改为 TXFIFO 之后,使用 TXQUEUE 的发送代码,接收到报文的顺序为: ![image-20220109160659883.png](https://oss-club.rt-thread.org/uploads/20220109/8b925f295b1f5c7814ec1f87d0ea0149.png) 按照压入 FIFO 的顺序,发送了报文。 ### 4. TXBUFF + TXQUEUE 的混合发送模式 使用 TXBUFF + TXQUEUE 模式,准备好的报文如下图所示的顺序放入了 Message RAM. ![image-20220109161257115.png](https://oss-club.rt-thread.org/uploads/20220109/ad6ac16105ad4ae0ff806aef44d1da37.png.webp) 在混合模式下,会依次检索 TXBUFF 和 TXQUEUE 中的优先级最高的消息进行发送,发送的顺序为: 1. TXBUFF3 (ID = 1) 2. TXQUEU8 (ID = 2) 3. TXBUFF0 (ID = 3) 4. TXQUEUE7 (ID = 4) 5. TXBUFF4 (ID = 8) 6. TXBUFF2 (ID = 12) 7. TXBUFF1 (ID = 15) 8. TXBUFF5 (ID = 24) ### 5. TXBUFF + TXFIFO 的混合发送模式 使用 TXBUFF + TXFIFO 模式,准备好的报文如下图所示的顺序放入了 Message RAM. ![image-20220109162619931.png](https://oss-club.rt-thread.org/uploads/20220109/f50c841d8dac72f451e36f11c1bd4f75.png) 在混合模式下,会依次检索 TXBUFF 中优先级最高的消息与 TXFIFO 中第一条的消息进行比较,从他们二者中选择优先级更高的报文进行发送,发送的顺序为: 1. TXBUFF3 (ID = 1),TXBUFF 中优先级最高的是 ID =1 ,TXFIFO 中第一条消息的优先级 ID = 4; 2. TXBUFF0 (ID = 3),TXBUFF 中优先级最高的是 ID =3 ,TXFIFO 中第一条消息的优先级 ID = 4; 3. TXFIFO7 (ID = 4), 前两条消息发送之后,TXBUFF 中优先级最高的 ID = 8, TXFIFO 第一条消息 ID = 4; 4. TXFIFO8 (ID = 2), 前三条消息发送之后,TXBUFF 中优先级最高的 ID = 8, 剩余 TXFIFO 第一条消息 ID = 2; 5. TXBUFF4 (ID = 8) 6. TXBUFF2 (ID = 12) 7. TXBUFF1 (ID = 15) 8. TXBUFF5 (ID = 24) ## 二, CANFD 的发送事件 CANFD 中增加了发送事件的功能,即在发送成功之后可以记录下发送的信息,发送事件的使用场景可以是,发送某个紧急报文时,可以将其记录下来。开启发送事件记录功能需要在 Message RAM 中开启, CANFD 中支持最大记录事件的个数为 32. ``` /* 设置记录发送事件的个数为 10 */ hfdcan1.Init.TxEventsNbr = 10; ``` 根据 Message RAM 的配置,可以设置新事件到来的中断回调以及事件的水位中断,用来通知到用户发送事件的存储已经快满了。 ``` /* 设置发送事件的水位为 4 */ HAL_FDCAN_ConfigFifoWatermark(&hfdcan1, FDCAN_CFG_TX_EVENT_FIFO, 4); /* 设置响应的中断类型 */ HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_TX_EVT_FIFO_WATERMARK | FDCAN_IT_TX_EVT_FIFO_NEW_DATA |, 0); ``` 设置好中断之后,可以在中断中捕获对应的事件 ``` /* 发送事件的回调函数 */ void HAL_FDCAN_TxEventFifoCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t TxEventFifoITs) { if (hfdcan == &hfdcan1) { if ((TxEventFifoITs & FDCAN_IT_TX_EVT_FIFO_NEW_DATA) != RESET) { /* TODO */ HAL_FDCAN_ActivateNotification(hfdcan, FDCAN_IT_TX_EVT_FIFO_NEW_DATA, 0); } if ((TxEventFifoITs & FDCAN_IT_TX_EVT_FIFO_WATERMARK) != RESET) { /* TODO */ HAL_FDCAN_ActivateNotification(hfdcan, FDCAN_IT_TX_EVT_FIFO_WATERMARK, 0); } } } ``` 当捕获到对应的时间后,用户需要使用获取事件的 API 来获取事件,该 API 在裸机下可以直接在中断中读取,在 RTOS 中可以使用发送一个信号量的方式在线程中读取。 ``` HAL_FDCAN_GetTxEvent(&hfdcan1, &tx_event); ``` 以下是发送一帧报文,然后保存这个发送事件: ``` FDCAN1_TxHeader.Identifier = 0x102; FDCAN1_TxHeader.IdType = FDCAN_STANDARD_ID; FDCAN1_TxHeader.TxFrameType = FDCAN_DATA_FRAME; FDCAN1_TxHeader.DataLength = FDCAN_DLC_BYTES_8; FDCAN1_TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE; FDCAN1_TxHeader.BitRateSwitch = FDCAN_BRS_ON; FDCAN1_TxHeader.FDFormat = FDCAN_FD_CAN; /* 设置保存发送事件 */ FDCAN1_TxHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS; FDCAN1_TxHeader.MessageMarker = 0; HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &FDCAN1_TxHeader, rxdata); ``` `MessageMarker` 是消息标签,可以在发送消息的时候给每个报文打上这个标签,用来在获取事件的时候方便用户知道是发送的哪个标签的报文。 获取事件可以得到的信息,用结构体 `FDCAN_TxEventFifoTypeDef` 来记录,换句话说能获取到的信息,都在这个结构体里面 ``` typedef struct { uint32_t Identifier; // 帧 ID uint32_t IdType; // 帧 ID 类型 uint32_t TxFrameType; // 发送帧类型 uint32_t DataLength; // 发送数据长度 uint32_t ErrorStateIndicator; // 错误状态描述 uint32_t BitRateSwitch; // 波特率切换 uint32_t FDFormat; // 是不是 CANFD uint32_t TxTimestamp; // 时间戳 uint32_t MessageMarker; // 消息编号 uint32_t EventType; // 事件类型 } FDCAN_TxEventFifoTypeDef; ``` 大部分都可以发送报文的结构体一样。 - TxTimestamp : CANFD 外设有一个 16 位的定时器,用来记录发送的时间 - EventType : 事件类型,可以是发送事件,也可以取消了但仍发送的事件。 ## 三,CANFD 时间戳 CANFD 提供一个 16 位回卷计数器来生成时间戳。预分频器 TSCC.TCP 可配置为以 CAN位时间的倍数 (1…16) 为计数器提供时钟。计数器值可通过 TSCV[TCV] 读取。对寄存器 TSCV 进行写访问会将计数器复位为 0。时间戳计数器回卷时,中断标志 IR[TSW] 会置 1。 开始接收/发送帧时,会捕获计数器值,并将该值存储在接收缓冲区/接收 FIFO (RXTS[15:0])或发送事件 FIFO (TXTS[15:0]) 元素的时间戳部分。 开启时间戳功能 ``` /* 设置分频系数为 1 */ HAL_FDCAN_ConfigTimestampCounter(&hfdcan1, FDCAN_TIMESTAMP_PRESC_1); /* 使用内部时钟源 */ HAL_FDCAN_EnableTimestampCounter(&hfdcan1, FDCAN_TIMESTAMP_INTERNAL); ``` 在开启时间戳功能之后,就能在 获取发送事件的时候知道发送报文的时间戳。 ## 四, 总结 CANFD 是在 CAN 的基础上改进而来,增加了很多新的特性,这些新的特性赋予了 CANFD 更大的功能,根据这些特性来适配项目的需求,有利于设计出更加健壮的程序,对一些未提到的细节做以补充: - TXBUFF 和 TXFIFO/TXQUEUE 总是从 TXBUFF 开始查询 - CANFD 的发送模式非常灵活,没有特别需求直接选用 TXFIFO 的方式,最简便 - 注意激活多种中断的时候,使用或的方式 - 激活 TXBUFF 时,建议使用宏的方式,32个 TXBUFF 每个占用一个 bit,根据这个特性用户自己去设计需要关系的 TXBUFF 的标号。 - 参考文档《RM0433》《AN5348》。 - 调试设备 ZLG-USBCANFD-200U
3
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
whj467467222
开源,分享,交流,共同进步
文章
32
回答
1222
被采纳
148
关注TA
发私信
相关文章
1
ARTPi 的FDCAN使用官方工程如何发送数据?
2
STM32H750 FDCAN通信异常
3
ART-PI 的FDCAN功能使用
4
ART-PI FDCAN使用卡在rt_completion_wait处
5
ART PI FDCAN 死在rt_device_write
6
Art Pi FDCAN 到底能不能用呀?
7
CAN.c 与drv_fdcan.c兼容么?
8
出现这样的异常如何解决
9
能否有偿指导一下art-pi的hdcan使用
10
FDCAN报错 FDCAN报错
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
cubemx
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
rt_mq_消息队列_msg_queue
SFUD
keil_MDK
msh
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
812
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
2
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
2
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部