MCU:H743野火挑战者
其他文章:
STM32H743 FDCAN通信 接收中断如何实现
如何计算并设置CAN外设的波特率(基于STM32H7和HAL库)
STM32H743 FDCAN 发送管理分析(HAL库)
前言
H743具备两个接收FIFO,分别是FIFO 0和FIFO 1,功能相同;H743提供的是FDCAN,FDCAN扩展帧允许单个消息中发送64个数据字节,而CAN 2.0有效负载数据最多可以发送8个字节。
当FDCAN接收到报文,经过过滤器过滤后,会将报文存储到FIFO或RX buffer中(可选,本文仅分析进入FIFO)。对于标准帧,H7提供了128个过滤器;对于扩展帧,H7提供了64个过滤器,每个过滤器都有一个自己的编号。各个过滤器处于并联关系,即只要通过一个过滤器就可以进入FIFO。那进入FIFO后,H7是如何保存这些报文,又是如何读取这些报文,并辨析报文对应何内容?这是本文想要探索的问题。
RAM过滤区
FDCAN外设可以配置两套验收滤波器:一套用于标准标识符,一种是扩展标识符,用于存储或拒绝接收到的消息。针对标准帧有128个元素,扩展帧则有64个元素。而滤波器的元素保存的是滤波器的基本设定,比如过滤的模式、ID等信息。
滤波器相当于审核的大门,这个大门可以设定在某个接收区域,比如FIFO 0或FIFO 1或RX buffer。如下图的代码, 则将标准滤波器0号安排给RXFIFO0守大门。
每个接收区域都可能不止一个滤波器,一般会从编号比较小的开始审核,如果通过了,就不再进行后面的审核。我们可以设置每个过滤器都只通过一个对应ID,也可以设置每个过滤器通过多个ID。
IdType:主要表示此滤波器针对的是标准帧还是扩展帧,针对标准帧有128个滤波器,扩展帧则有64个滤波器。FDCAN_STANDARD_ID是标准帧,FDCAN_EXTENDED_ID是扩展帧。
FilterIndex:过滤器索引,标准帧有0-127,扩展帧有0-64。
FilterType:滤波器模式。范围模式、专用ID模式、掩码模式、扩展支持范围模式。
范围模式:FilterID1 至 FilterID2范围内的ID都可以通过;
专用ID模式:只有FilterID1 或 FilterID2可以通过。
掩码模式:ID1是标准ID,ID2是掩码。比如ID1=0x111 ID2=001,那么ID2作为掩码位是1,那么ID1对应位所在的位必须一模一样,即最后一位必须是1 。所以0x112不可以通过,0x113不可以通过,0x211可以通过,0x351可以通过。
最后一个模式比较少用,不太了解。
这里用的是掩码模式,ID1为111,ID2为000,000代表没有任何一位必须满足,所以其实相当于ID1没用,所以的ID都可以通过此过滤器。
FilterConfig :过滤后的报文放在哪个区域。可选填如下。
FDCAN_FILTER_TO_RXFIFO0代表过滤后放进FIFO0,其他选项包括放进FIFO1、放进RxBUFFER、如果ID匹配则拒绝等选项。
接收区
下图是FDCAN外设对应消息RAM。图上显示FIFO0区域最多64个元素。在FDCAN中,消息的接收和发送意味着在RAM级别存储“元素”。 这个“元素”仅包含标识符,DLC,控制位(ESI,XTD,RTR,BRS,FDF),数据字段和特定的发送/接收位字段进行控制。就是一些信息+数据段。
一个FIFO元素,包含前面2个字(32位)的标识符内容,还有后面部分真正的数据段。
介绍部分标识符:
XTD:标准帧还是扩展帧;
RTR:数据帧还是扩展帧;
ID:标准帧的话11位ID,扩展帧的话29位ID;
FIDX:过滤器索引,代表这条报文是经过了哪条过滤器审核才进来的。加入一个过滤器只通过一个ID,那么我们就可以通过读取这个过滤器索引得知这个报文属于哪个ID。虽然说读索引效率比读ID效率高那么一丢丢,但是直接读ID不香吗?
每个元素的数据段都是可以设置的,最多64字节。前面两个固定2个字,后面数据x个字,x主要取决于接收的数据到底有多长。如果数据0-8个字节,则每个元素为数据分配2个字,再加上前面2个字,一共就是4个字,对应下表第一行。下表详细介绍了必要的“元素”大小,具体取决于数据字段范围。
而FIFO 0 包含64个这样的元素,理论上是可以同时保存64个报文。收到的报文通过滤波器后就会被整合成一个元素,放在FIFO 0这片区域。如果Rx FIFO已满(即传入超过64个报文且不读取),则可以根据两种不同模式来处理新到达的元素:
(1)阻塞模式:这是Rx FIFO的默认操作模式,其他元素不能写入RxFIFO,直到至少一个元素已被读出。
(2)覆盖模式:Rx FIFO中接受的新元素将覆盖Rx FIFO中最旧(最先接收的数据)的元素并且FIFO的put和get索引加1。
所以只要报文接收的速度没那么快,滤波器设置严格一点,读的勤快一点,FIFO还是没那么容易溢出的,毕竟有64个。
要从Rx FIFO读取元素,CPU必须执行以下步骤:
(1)读取寄存器FDCAN_RXF0S(FDCAN 接收 FIFO 0 状态寄存器)以了解FIFO 0的状态。
位24 F0F:0代表接收FIFO 0已满;1代表接收FIFO 0未满;
位21:16 F0PI 接收 FIFO 0 写入索引指针,范围为 0 到 63,即新的报文放在哪个位置;
位13: 8 F0GI 接收 FIFO 0 读取索引指针,范围为 0 到 63,即读取下一个报文从哪里开始读起;
位 6: 0 F0FL 接收 FIFO 0 中存储的元素数,范围为 0 到 64,即装了多少个未读的元素了。
(2)按照以下公式计算RAM中最旧的元素的地址:
Oldest element address = CAN_message_RAM_base_address + FDCAN_RXF0C.F0SA (start address) + FDCAN_RXF0S.F0GI (get Index) x Rx FIFO_element_size.
解释:CAN 消息RAM起始地址+FIFO的起始地址+读取索引指针*元素大小
(3)从计算出的地址中读取元素。
CPU从Rx FIFO读取一个元素或一系列元素后,它必须确认读取。确认后,FDCAN可以将相应的Rx FIFO缓冲区重新用于新元素。为了确认一个或多个元素,则CPU必须将从Rx FIFO读取的最后一个元素的缓冲区索引写入FDCAN_RXF0A(FDCAN 接收 FIFO 0 确认寄存器)。结果,FDCAN更新了FIFO填充级别和get索引。
这个寄存器位5:0为F0AI:主机从接收 FIFO 0 读取消息或消息序列后,必须将从接收 FIFO 0 读取的最后一个元素的
缓冲区索引写入 F0AI 中。此操作会将接收 FIFO 0 获取索引 RXF0S[F0GI] 设为 F0AI + 1,并会更新 FIFO 0 填充级别 RXF0S[F0FL]。通俗点讲,这个寄存器的值会传给上面那个状态寄存器,包括新的获取索引和填充级别。比如读完第4个元素,那获取索引应该是5。
看了这么多,觉得很复杂的朋友不用慌。上述过程HAL库已经整合成了一个函数。需要的时候引用就好,至于获取索引,填充级别之类的,函数内部已经设置好了。
虽说FIFO 0有64个元素,但不是默认就是使用64个的。需要自己设定使用多少个,多于设定值那就叫满溢。比如下图设置了3,所以当FIFO0接收到了3个报文,就认为是满了,再多就叫满溢。
下面还可以设置每个元素的数据段多少字节,还有FIFO 1的一些参数。这个在STM32 CubeMX就可以设置,生成的工程自带这些代码。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210714175028353.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1d2VuYmluMTI=,size_16,color_FFFFFF,t_70
通过这篇文章,我们可以了解到FIFO是如何把通过滤波器的报文保存下来的,又是如何读取的。FIFO 1和RX BUFFER是同样的道理。在报文传递速度低的情况下, 只要函数内部保证读取的速度,其实就能保证FIFO不溢出。如果存在溢出,那么就得考虑滤波器的设置和满溢中断函数了。
如果要了解CAN是如何进入中断的,可以看STM32H743 FDCAN通信 接收中断如何实现。