一、介绍

FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:

1、每帧数据段最大长度由8字节上升到64字节。

2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。

3、向下兼容CAN。

H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。

二、结构

先看结构框图:

1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。

可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。

2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。

3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。

4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。

5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:

10KB的消息RAM中,是以32位为最小单位来操作的(因此消息RAM的起始地址要32位对齐),即字(word),因此10KB共有10*1024/4=2560个字。2560个字被分成了8个功能区,从上到下为:SIDFC.FLSSA、XIDFC.FLESA、RXF0C.F0SA......每个功能区中的条目又定义为元素(element),每个元素可以占用多个字,但是同一个功能区中的元素大小是相同的。比如Rx FIFO0是接收FIFO0,消息通过过滤器后被保存到这里,它最多占用1152个字的空间,有64个元素,也就是可以保存64条消息,这64个元素的大小都相同,每个元素的大小最大为1152/64=18字。

1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。

2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)

3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。

4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。

5)、RXBC.RBSA:接收buffer,最多可以有64个元素。

6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)

7)、TXBC.TBSA:发送buffer,最多可以有32个元素。

8)、TMC.TMSA:触发存储器,最多可以有64个元素。

《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》

下面来详细介绍这几个功能区域:

1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:

SFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:
    00:范围过滤,过滤到SFID1到SFID2中的所有信息。
    01:双过滤,过滤到SFID1和SFID2中的信息。
    10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。
    11:禁止过滤器元素
SFEC[2:0](bit29:27):标准过滤配置
    000:禁止过滤器元素
    001:当过滤匹配以后存储在Rx FIFO0中。
    010:当过滤匹配以后存储在Rx FIFO1中。
    011:当过滤匹配以后丢弃。
    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
    111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。
SFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。
SFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。

SFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。
    00:将消息存储在Rx Buffer中。

01:debug消息A。

10:debug消息B。

11:debug消息C。

SFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。

SFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。

2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:

F0 EFEC[2:0](bit31:29):扩展过滤配置
    000:禁止过滤器元素
    001:当过滤匹配以后存储在Rx FIFO0中。
    010:当过滤匹配以后存储在Rx FIFO1中
    011:当过滤匹配以后丢弃。
    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
    111:存储到Rx buffer中,EFT[1:0]忽略。
F0 EFID1[28:0](bit28:0):扩展过滤ID1。
F1 EFTI[1:0](bit31:30):扩展过滤类型
    00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。
    01:双过滤,过滤到EF1ID和EF2ID中的信息。
    10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。
    11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码
F1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。
    00将消息存储在Rx Buffer中。
    01:debug消息A。
    10:debug消息B。
    11:debug消息C.
F1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。
F1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。

3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:

R0 ESI(bit31):错误状态标志
    0:传输节点显性错误。
    1:传输节点隐形错误
R0 XTD(bit30):标记是标准ID还是扩展ID
    0:11位标准ID

1:29位扩展ID

R0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
    0:接收到的帧是数据帧

1:接收到的帧是遥控帧
R0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
R1 ANMF(bit31)

0:接收到的帧和FIDX中的过滤帧匹配

1:接收到的帧和Rx滤波器中的任何元素都不匹配
R1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。
R1 FDF(bit21):帧格式
    0:标准帧格式
    1:FDCAN帧格式。
R1 BRS(bit20):比特率切换
    0:接收到的帧没有比特率切换

1:接收到的帧带有比特率切换
R1 DLC[3:0](bit):数据长度。
    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。
    9-15:如果是传统CAN,接收帧长度为8字节。
    9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。
R1 RXTS[15:0](bit15:0):接收时间戳。
R2-Rn:接收到的数据。

4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。

E0 ESI (bit31): 错误状态标志

0:传输节点显性错误
    1:传输节点隐形错误。
E0 XTD(bit30):标记是标准D还是扩展ID
    0:11位标准ID.
    1:29位扩展ID

E0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
    0:发送的帧是数据帧
    1:发送到的帧是遥控帧
E0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。
E1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。
E1 EFC(bit23:22):事件类型
    00:保留。
    01:发送事件。
    10:发送取消。
    11 :保留。
E1 EDL(bit21):扩展数据长度
    0:标准帧格式
    1:FDCAN帧格式。
E1 BRS(bit20):比特率切换
    0:发送的帧没有比特率切换

1:发送的帧带有比特率切换
E1 DLC[19:16](bit):数据长度。
    0-8:传统CAN+CANFD,长度为0~8字节。
    9-15:长度为8字节。
E1 TXTS[15:0]bit15:0):时间戳。

5)、TXBC.TBSA:发送buffer,有32个元素。

T0 ESI(bit31):错误状态标志
    0:CANFD帧中取决于被动错误标志。
    1:CANFD帧中隐形错误。
T0 XTD(bit30):标记是标准ID还是扩展ID
    0:11位标准ID

1:29位扩展ID

T0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
    0:发送的帧是数据帧
    1:发送到的帧是遥控帧
T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
T1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。
T1 EFC(bit23):事件FIFO控制
    0:不存储发送事件。
    1:存储发送事件。
T1 FDF(bit1):帧格式
    0:标准帧格式
    1:FDCAN帧格式。
T1 BRS(bit20):比特率切换
    0:发送的帧没有比特率切换

1:发送的帧带有比特率切换
T1 DLC[19:16](bit):数据长度。
    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。
    9-15:传统CAN,接收帧长度为8字节。
    9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。
T2-Tn:发送的数据。

三、过滤器设置

标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:

1、指定范围过滤
        通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:

SIDFC.FLSSAn.SFT=0        //范围滤波
SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到RxFIFO中
SDIFC.FLSSAn.SFID1=0x123  //ID1
SDIFC.FLSSAn.SFID2=0X321  //ID2

2、指定ID过滤

我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:

SIDFC.FLSSAn.SFT=1        //指定D过滤
SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到Rx FIFO中
SDIFC.FLSSAn.SFID1=0x123  //ID1
SDIFC.FLSSAn.SFID2=0X123  //ID2

3、传统的位过滤
        第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:

SIDFC.FLSSAn.SFT=2          //传统位过滤
SDIFC.FLSSAn.SFEC=1         //如果滤波匹配成功的话将消息保存到Rx FIFO中
SDIFC.FL SSAn.SFID1=0xFF00  //ID1
SDIFC.FLSSAn.SFID2=0xF00    //掩码

其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。

四、CAN的收发过程

1、CAN接收过程

CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。

接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。

当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。

为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。
        读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):

/* Calculate Rx FIFO 0 element address */
GetIndex = ((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8);
RxAddress = (uint32_t *)(hfdcan->msgRam.RxFIFO0SA + (GetIndex * hfdcan->Init.RxFifo0ElmtSize * 4));

1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。

2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。

3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同

如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。

如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。

计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:

pRxHeader->IdType = *RxAddress & FDCAN_ELEMENT_MASK_XTD;
pRxHeader->Identifier = ((*RxAddress & FDCAN_ELEMENT_MASK_STDID) >> 18);
pRxHeader->RxFrameType = (*RxAddress & FDCAN_ELEMENT_MASK_RTR);
pRxHeader->ErrorStateIndicator = (*RxAddress++ & FDCAN_ELEMENT_MASK_ESI);  //这里地址自加了一次
pRxHeader->RxTimestamp = (*RxAddress & FDCAN_ELEMENT_MASK_TS);
pRxHeader->DataLength = (*RxAddress & FDCAN_ELEMENT_MASK_DLC);
pRxHeader->IsFilterMatchingFrame = ((*RxAddress++ & FDCAN_ELEMENT_MASK_ANMF) >> 31); //这里地址又自加了一次
......
pData = (uint8_t *)RxAddress;
for(ByteCounter = 0; ByteCounter < DLCtoBytes[pRxHeader->DataLength >> 16]; ByteCounter++)
{*pRxData++ = *pData++;
}

2、CAN发送流程

在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:

因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。

下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:

1)、专用发送buffer

专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息
        如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。

专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:

发送buffer索引 (0…31) *4+发送buffer起始地址。

2)、发送FIFO

发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。
        新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。

发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。

如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。
        如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。
        如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。
        发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:

放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。

3)、发送队列
        发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区
        新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。
        应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。
        发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是:

发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。

因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:

1)、发送buffer全部配置为专用发送buffer

每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。

2)、混合专用发送buffer和发送FIFO

在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。

发送优先次序:
A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)
B、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据

3)、混合专用发送buffer和发送队列

在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。

发送优先级设置:
A、扫描所有激活了发送请求的发送缓冲区
B、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据

再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。

FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。
        发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。
        发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。

        为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。

CAN发送消息的代码实现:

发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:

PutIndex = ((hfdcan->Instance->TXFQS & FDCAN_TXFQS_TFQPI) >> 16); //获取元素编号
FDCAN_CopyMessageToRAM(hfdcan, pTxHeader, pTxData, PutIndex); //复制消息到Tx buffer
hfdcan->Instance->TXBAR = (1 << PutIndex); //发出传输请求

发送buffer请求寄存器FDCAN_TXBAR,描述为:

此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。

五、初始化

初始化可以直接使用HAL库,

u8 FDCAN1_Mode_Init(void)
{FDCAN_FilterTypeDef FDCAN1_RXFilter;HAL_FDCAN_DeInit(&FDCAN1_Handler);                              //先清除以前的设置FDCAN1_Handler.Instance=FDCAN1;FDCAN1_Handler.Init.FrameFormat=FDCAN_FRAME_CLASSIC;            //传统模式FDCAN1_Handler.Init.Mode=FDCAN_MODE_NORMAL;                     //正常模式FDCAN1_Handler.Init.AutoRetransmission=DISABLE;                 //关闭自动重传FDCAN1_Handler.Init.TransmitPause=DISABLE;                      //关闭传输暂停           FDCAN1_Handler.Init.ProtocolException=DISABLE;                  //关闭协议异常处理//时钟为200M,baudrate=200M/(NominalTimeSeg1+NominalTimeSeg2+1)/NominalPrescaler,这里配置为1MFDCAN1_Handler.Init.NominalPrescaler=10;                        //分频系数FDCAN1_Handler.Init.NominalSyncJumpWidth=8;                     //重新同步跳跃宽度FDCAN1_Handler.Init.NominalTimeSeg1=11;                         //tsg1范围:2~256FDCAN1_Handler.Init.NominalTimeSeg2=8;                          //tsg2范围:2~128FDCAN1_Handler.Init.MessageRAMOffset=0;                         //信息RAM偏移,10KB消息RAM共有2560字,故可以偏移0~2560//使用了多少个滤波器就要设置为多少FDCAN1_Handler.Init.StdFiltersNbr=3;                            //标准帧滤波器个数,0~128FDCAN1_Handler.Init.ExtFiltersNbr=2;                            //扩展帧滤波器个数,0~64//接收FIFO0、FIFO1和buffer配置,此处没有使用FIFO1故个数设置为0FDCAN1_Handler.Init.RxFifo0ElmtsNbr=64;                         //设置接收FIFO0元素个数,0-64FDCAN1_Handler.Init.RxFifo0ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO0元素的数据域大小:8字节    FDCAN1_Handler.Init.RxFifo1ElmtsNbr=0;                          //设置接收FIFO1元素个数,0-64FDCAN1_Handler.Init.RxFifo1ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO1元素的数据域大小:8字节      FDCAN1_Handler.Init.RxBuffersNbr=64;                            //接收buffer元素个数,0~64FDCAN1_Handler.Init.RxBufferSize=FDCAN_DATA_BYTES_8;            //接收buffer元素的数据域大小:8字节  //没有使用发送事件FIFO功能,故TxEventsNbr设置为0。把发送buffer全部作为专用发送buffer使用,故TxFifoQueueElmtsNbr设为0.FDCAN1_Handler.Init.TxEventsNbr=0;                              //发送事件FIFO元素个数,0~32FDCAN1_Handler.Init.TxBuffersNbr=32;                            //发送buffer元素个数,0~32FDCAN1_Handler.Init.TxFifoQueueElmtsNbr=0;                      //发送Buffer被用作发送FIFO/队列的元素个数,0~32FDCAN1_Handler.Init.TxFifoQueueMode=FDCAN_TX_FIFO_OPERATION;    //发送FIFO模式选择,可以选择FIFO模式或队列模式FDCAN1_Handler.Init.TxElmtSize=FDCAN_DATA_BYTES_8;              //发送元素的数据域大小:8字节if(HAL_FDCAN_Init(&FDCAN1_Handler)!=HAL_OK) return 1;          //初始化FDCAN//配置RX滤波器,标准帧   FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准IDFDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                   FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  FDCAN1_RXFilter.FilterID1=0x112;                                //11位IDFDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准IDFDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                   FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  FDCAN1_RXFilter.FilterID1=0x113;                                //11位IDFDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准IDFDCAN1_RXFilter.FilterIndex=2;                                  //滤波器索引                   FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  FDCAN1_RXFilter.FilterID1=0x114;                                //11位IDFDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化//配置RX滤波器,扩展帧。标准帧和扩展帧的滤波器索引是分开的   FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展IDFDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                   FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  FDCAN1_RXFilter.FilterID1=(1 << 20)|(2 << 12);                  //32位IDFDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展IDFDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                   FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  FDCAN1_RXFilter.FilterID1=(1 << 20)|(3 << 12);                  //32位IDFDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;    //滤波器初始化//滤除的消息直接丢弃HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler,FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);  //设置被滤除掉的消息的处理方式HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);   //使能新消息接收中断HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_TX_COMPLETE,0xffffffff);   //使能消息发送中断,0xffffffff表示所有的发送buffer都触发中断    HAL_FDCAN_Start(&FDCAN1_Handler);                               //开启FDCANreturn 0;
}

六、CAN发送示例(来自正点原子)

//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
//len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
//       其他,失败;
u8 FDCAN1_Send_Msg(u8* msg,u32 len)
{   FDCAN1_TxHeader.Identifier=0x12;                           //32位IDFDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID;                  //标准IDFDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME;              //数据帧FDCAN1_TxHeader.DataLength=len;                            //数据长度FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;            FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF;               //关闭速率切换FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN;                //传统的CAN模式FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS;     //无发送事件FDCAN1_TxHeader.MessageMarker=0;                           if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送return 0;
}

七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)

//can口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到;
//       其他,接收的数据长度;
u8 FDCAN1_Receive_Msg(u8 *buf)
{   if(HAL_FDCAN_GetRxMessage(&FDCAN1_Handler,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf)!=HAL_OK)return 0;//接收数据return FDCAN1_RxHeader.DataLength>>16;
}

参考:《正点原子》  STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准)

STM32H7的FDCAN相关推荐

  1. 【STM32H7教程】第91章 STM32H7的FDCAN总线基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第91章       STM32H7的FDCAN总线基础知识和 ...

  2. 【STM32H7教程】第92章 STM32H7的FDCAN总线应用之双FDCAN实现(支持经典CAN)

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第92章       STM32H7的FDCAN总线应用之双F ...

  3. STM32H743+CubeMX-解决FDCAN控制器无法接收远程帧

    文章目录 一. 前言 二. STM32H743编程参考手册 三. HAL_FDCAN_ConfigGlobalFilter( ) 一. 前言 第一次接触STM32H7的FDCAN控制器时,觉得要使用它 ...

  4. ST NXP Infineon 常用MCU的汇总说明

    文章目录 ST NXP Infineon 主要是 ST, NXP, Infineon 的芯片: 基本都有免费的IDE 图形化的配置, 生成初始化代码 便宜的调试工具(RMB几十 ~几百) 都有移植好的 ...

  5. 《安富莱嵌入式周报》第237期:2021.10.25--2021.10.31

    往期周报汇总地址:链接 目录 1.Cortex-M23和Cortex-M33权威指南上线 2.KEIL Studio教程文档上线,同时增加411块板卡 3.基于TensorFlow Lite的语音识别 ...

  6. 浅析STM32H7 FDCAN(二)

    一,认识 Message RAM 消息 RAM 是 FDCAN 里面非常重要的一个点,这也是和之前的 STM32 bxCAN 最大的不同. STM32H7 自带了 10K 的消息 RAM,消息 RAM ...

  7. 浅析STM32H7 FDCAN(一)

    一,写在前面 目前STM32H7系列全部支持CAN_FD,现在互联网上的关于STM32 CAN_FD的内容基本没有,寥寥几篇也都是关于滤波器的,所以就决定来研究一下,这篇博客就抛砖引玉了,希望大家看完 ...

  8. STM32H743+CubeMX-计算FDCAN2上的MessageRAMOffset(使两路FDCAN正常工作)

    文章目录 一. 前言 二. 消息RAM 三. "错误"配置之同时使用两路FDCAN 3.1 FDCAN1 3.2 FDCAN2 3.3 消息RAM的分布 四. "正确&q ...

  9. STM32H743+CubeMX-学习FDCAN控制器上的TxFIFO

    文章目录 一.TxFIFO设为1所带来的问题 1.1 代码 1.2 CAN分析仪 二. 解决问题的方法 2.1 STM32CubeMX 2.2 CAN分析仪 一.TxFIFO设为1所带来的问题 在ST ...

最新文章

  1. python可以从事什么工作-学完Python开发可以从事哪些行业?
  2. python dash库_让你事半功倍的小众 Python 库
  3. 性能指标TP99之我解
  4. mybatis报错invalid types () or values ()解决方法
  5. 谷歌浏览器安装Postman插件 亲测有效!!!
  6. python 暂停程序 等待用户输入_Python-基础02-程序与用户交互
  7. G7在实时计算的探索与实践
  8. 蓝宝石rx470d原版bios_AMD RX470/570强刷RX580完整图文教程(附文件下载及查BIOS攻略)...
  9. 【Python精华】100个Python练手小程序
  10. win10系统用cadance画封装软件卡死问题
  11. 基金定投应该什么时候终止?
  12. 船讯网|爬虫COOKIE重定向反爬处理
  13. IGBT静态参数测试系统可测项目有哪些?
  14. 知识点四 图论:dijkstra (HDU 2544 +HDU 1874)
  15. Linux下数据库表结构导入导出
  16. CF1225D Power Products (数论)
  17. c语言生日快乐爱心,C语言 生日快乐
  18. Java学习笔记———选择结构
  19. MySQL SSL安全解读
  20. 自己做量化交易软件(3)通通量化分析框架构成1

热门文章

  1. 手写logback_springboot logback调整mybatis日志级别无效的解决
  2. blocked java线程_Java线程状态:BLOCKED与WAITING的区别
  3. 可以通过无线充电的软脑植入物来控制大脑中的脑细胞
  4. CMU贺斌教授团队提出:冥想可以增强对脑机接口的控制
  5. 我玩《王者荣耀》、斗地主、打麻将,但我是正经搞AI的北大教授
  6. 全球大半网络瘫痪,背后原因竟来自这家无名小公司
  7. 阿里达摩院数学竞赛考题曝光!4道题限时48小时,网友:题目能看懂但就是不会做...
  8. Momenta完全无人驾驶首次曝光!城区道路混行无接管,遭遇逆行也不怕,特斯拉Waymo路线二合一...
  9. 数据科学中的6个基本算法,掌握它们要学习哪些知识
  10. Unable to get the CMake version located at