一、介绍 FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括: * J4 U3 ]7 g7 F4 m/ i6 C# a 1、每帧数据段最大长度由8字节上升到64字节。3 K) y# E$ U6 r: `) [0 o$ j 2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。 3、向下兼容CAN。 H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。$ T2 a' \! {# z9 _ ' y' H2 ~7 m# B* K+ S% {3 G: o 二、结构3 Y4 v! i7 ?% |) j* h9 C+ B4 f% {& A 先看结构框图: 1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。 , n: H0 q) m2 g; r+ _1 J }$ i" v 可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。 2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。+ w$ W7 l# U% Q, [2 b" _ 3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。 " _: v& R/ t- u8 P, |2 C 4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。 5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下: # ?/ F1 V2 i9 i! ?' A2 @( o: W, i 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) O5 v$ Y* X2 I/ ?0 R/ T 3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。 : x7 ]3 [; X% W 4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。 5)、RXBC.RBSA:接收buffer,最多可以有64个元素。 ' J9 ]. d# X) }( H 6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO) % e2 Z5 q! L* g$ @; o& l7 n 7)、TXBC.TBSA:发送buffer,最多可以有32个元素。3 l% T) b% ?; k0 V. |8 q & a. k$ n+ |4 C+ R( f1 { @- M S1 Z 8)、TMC.TMSA:触发存储器,最多可以有64个元素。! w$ Z2 E* g0 ?, N* T5 O 《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》- k" y+ T# Z8 n, B % R4 V. Y" R) L 下面来详细介绍这几个功能区域:" H/ X' v" ?* I$ X ! E! O" c: Q2 U$ m6 ~ 1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为: & J: h1 W: b! b" K4 y SFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:- L, E: A4 r& R- }0 z2 D# O 00:范围过滤,过滤到SFID1到SFID2中的所有信息。- r5 n7 K( J7 K) w 01:双过滤,过滤到SFID1和SFID2中的信息。; e: u3 t+ S5 w* L 10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。! B, ^: E5 N# k 11:禁止过滤器元素 SFEC[2:0](bit29:27):标准过滤配置 000:禁止过滤器元素 001:当过滤匹配以后存储在Rx FIFO0中。 010:当过滤匹配以后存储在Rx FIFO1中。: G0 G( Z0 p: t+ Q1 S 011:当过滤匹配以后丢弃。0 b0 A. u5 @) X: p 100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。+ a* ~' B, k1 S7 f( F0 f' x' b% O* H 101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。) H G$ V. S- b& S, B6 l. n5 P 110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。3 U4 ^' V4 m q3 t9 U4 x 111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。3 b" s( @7 @, d: R$ F SFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。 SFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。$ h$ M1 n5 v, ^ ; ^ N. ^# ?/ ? SFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。) j* z2 e$ r. f: [1 @; l 00:将消息存储在Rx Buffer中。 01:debug消息A。 + d) W5 z' b5 N4 F2 t/ r5 M3 S " j3 j$ O/ M4 q( _3 U/ f' H b4 { 10:debug消息B。3 W( ?: j% U+ [ 9 F" b1 M6 |/ @% u* Y5 [ y 11:debug消息C。* @3 w% }$ [& X+ u, V4 Y 3 r2 D6 h1 V3 G8 f: ~, Z! {' ]2 r SFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。 SFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。 2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:2 B4 G4 u: M9 @7 o# M! q : ?4 e& n, o; f. k. ^ F0 EFEC[2:0](bit31:29):扩展过滤配置, Z( K' F7 z( b/ E& @* H 000:禁止过滤器元素. [3 h9 _: ?6 ~( J4 I" ` 001:当过滤匹配以后存储在Rx FIFO0中。 010:当过滤匹配以后存储在Rx FIFO1中 011:当过滤匹配以后丢弃。* p: K& ]* V4 W S9 r# d 100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。* L4 P4 l( H* Q 101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。! ? W) o" t) S/ M( [: i- _ 110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。+ |, Q2 f, C& u1 f* z2 | 111:存储到Rx buffer中,EFT[1:0]忽略。 F0 EFID1[28:0](bit28:0):扩展过滤ID1。 F1 EFTI[1:0](bit31:30):扩展过滤类型: D3 }# s$ @4 M1 z$ d' g/ I 00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。 01:双过滤,过滤到EF1ID和EF2ID中的信息。 10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。 11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码1 L1 Z' X; m- ` Y/ a F1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。0 r. ? u4 e) o7 A& M- B 00将消息存储在Rx Buffer中。/ G( @3 X& }0 D H( J" }' v" E 01:debug消息A。 10:debug消息B。' g n' q3 K/ K' V% j3 q& Y 11:debug消息C.' o S. } T& ?. |* S F1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。 F1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。 % c' _6 H7 n/ o6 w% U) G 3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:$ H/ j+ G" j8 m" `- E+ G( k , N( A2 u% W. u* T- w$ i! ? % z! P3 a0 s$ c& h- D. Q R0 ESI(bit31):错误状态标志 0:传输节点显性错误。# z8 z1 Y; d- o9 q 1:传输节点隐形错误& ^- F; u" Y) Z$ ]) A" d R0 XTD(bit30):标记是标准ID还是扩展ID/ K; d, g/ }+ V% \( J0 u' B' z 0:11位标准ID8 ]" {- o, {3 |/ D+ J 1:29位扩展ID * H4 V' ?" I! K2 s# h' e. B% k- k R0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。# ^+ [0 _- o5 ~ 0:接收到的帧是数据帧8 H$ l. c/ @& u$ z+ F) M 1:接收到的帧是遥控帧 R0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。5 t+ ]/ J4 R( l% c' D% T R1 ANMF(bit31):, ?9 b Q6 N5 Y& u 0:接收到的帧和FIDX中的过滤帧匹配 7 V c/ K4 B& Z; B' E6 C* d' w 1:接收到的帧和Rx滤波器中的任何元素都不匹配' N% k6 C5 m K3 N! b$ r R1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。* {: ]' V5 g9 `7 j" Y R1 FDF(bit21):帧格式9 ^1 U. J$ J" t 0:标准帧格式4 x" _& d8 H7 v' \, @. y: d 1:FDCAN帧格式。- U0 w2 E" n7 }' h2 T0 d1 Z R1 BRS(bit20):比特率切换 0:接收到的帧没有比特率切换 1:接收到的帧带有比特率切换 R1 DLC[3:0](bit):数据长度。, H* t! [" M/ ?' P- c8 o8 P, w& S 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):接收时间戳。( @: \0 r' C, F2 h R2-Rn:接收到的数据。& r( Z1 b0 h3 B$ M5 X1 ^- V7 j9 b 4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。6 s- R2 A% o6 V 2 P- O. ]3 E- ]/ P2 h# \$ X" N* | / ?( Z2 V! k4 {! D$ k E0 ESI (bit31): 错误状态标志' v: o7 F7 j- f$ u) H9 V1 G, F 0:传输节点显性错误& j1 |4 o% t- v' A* U 1:传输节点隐形错误。 E0 XTD(bit30):标记是标准D还是扩展ID) X0 {! M! v* h( [ ^7 c1 G 0:11位标准ID.% R; J5 V7 [& B& y( m 1:29位扩展ID E0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。6 I5 G/ z7 S/ Q2 V 0:发送的帧是数据帧' R' M C+ y9 r; r/ [" S 1:发送到的帧是遥控帧 E0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。 E1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。" V; P* W5 E2 v7 F$ X' Y! y5 n E1 EFC(bit23:22):事件类型$ m& }, c* Q4 J. v7 w' h9 B 00:保留。 01:发送事件。 10:发送取消。: ^" ~! Y( G5 @3 J, v6 l2 K' o8 N 11 :保留。- D1 |; x' ]; X+ R1 ^ E1 EDL(bit21):扩展数据长度 [. q# `2 O' \/ A' E/ R 0:标准帧格式, C4 \+ c5 j0 k 1:FDCAN帧格式。# k0 A: o0 w2 f- Q( _ E1 BRS(bit20):比特率切换 0:发送的帧没有比特率切换# r% b* C& I( w' U. B. J ; P2 X# V* o1 q( [# `6 f 1:发送的帧带有比特率切换+ c+ |# E" ?+ w* y. ~/ ^ E1 DLC[19:16](bit):数据长度。- h; P! f9 u! Z 0-8:传统CAN+CANFD,长度为0~8字节。( y' s3 w" s9 L- A0 D. }6 |1 {9 y 9-15:长度为8字节。9 X9 p1 }3 P1 a- F: C' A; o E1 TXTS[15:0]bit15:0):时间戳。 5)、TXBC.TBSA:发送buffer,有32个元素。 T0 ESI(bit31):错误状态标志 \" g8 i8 O& B0 p. N 0:CANFD帧中取决于被动错误标志。4 b1 J7 ?+ }6 m8 R2 p 1:CANFD帧中隐形错误。- ~' S: }% P: o# | T0 XTD(bit30):标记是标准ID还是扩展ID 0:11位标准ID 1:29位扩展ID8 {' k% s9 N, j, g T0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。3 V1 P( P' s+ J5 k/ U# g 0:发送的帧是数据帧3 m$ i7 X: ^+ K3 V6 ]( k& X) v 1:发送到的帧是遥控帧 T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。9 R# N* z3 `: ?# b$ f( C# o @ T1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。 T1 EFC(bit23):事件FIFO控制# ]' ^/ | e, G4 [& I# [7 F2 k 0:不存储发送事件。% F6 f' G4 \% k: [7 X4 [# o, q 1:存储发送事件。) G0 B$ \0 [% L" ? T1 FDF(bit1):帧格式$ ?6 r6 C0 R( u+ c 0:标准帧格式# F. I+ r: T' X/ w: _- P 1:FDCAN帧格式。2 g4 `; W/ |: h T1 BRS(bit20):比特率切换 0:发送的帧没有比特率切换 ' B5 j. w' z' o" z3 {7 b 1:发送的帧带有比特率切换 T1 DLC[19:16](bit):数据长度。1 I. f# {1 e8 x 0-8:传统CAN+CAN FD,接收帧长度为0~8字节。 9-15:传统CAN,接收帧长度为8字节。 9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。 T2-Tn:发送的数据。 - M2 [( `& J/ r" ], ? 三、过滤器设置 # ~% k% j/ m+ y0 N! S 标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:5 g0 H% Y. i+ K; r+ I8 m) Z ) ]: F- c- m. `0 g/ A 1、指定范围过滤2 u( S2 @% I6 R8 B+ n# k7 R/ K7 y 通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:8 W" K7 s5 G3 p) ^ {
2、指定ID过滤 我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:, V4 u& x7 i# n u1 ?4 N 4 Y; D5 g5 @0 W9 f G" i+ M. J+ f
3、传统的位过滤! X0 d6 [4 Q6 x- ?) a; A 第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下: ' d; T& P0 w* y; o0 `3 u T
其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。' B+ c" ?2 t$ s2 x 四、CAN的收发过程 1、CAN接收过程 5 z4 I& v2 k' w9 ^7 E% C2 l CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。 接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。 ; b+ _* F$ q2 Q) K6 X0 b5 b 当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。$ f. R' U* b& a# B/ D j # T' e0 i( z& w" y" x 为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。3 \" ~; w4 G% f' U i 读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):
1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。/ ~ t4 j' Y. L. x/ i0 M % v& t v1 @9 N 2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。 3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同 2 r: P9 s) V8 h7 T, E# W% Z: z 如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。6 F1 }$ W# d# W2 l% d8 i o$ s # ^+ x9 @$ Y0 h- i7 c 如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。3 d$ ^5 g* p, ]& E7 S' E& T; X 计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了: 4 o4 k- ]6 a7 K1 V0 J
2、CAN发送流程) T' N) T$ U8 c1 f! G2 F ( P1 G$ @ }, s! _ 在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:' n, r- x/ i' E( s" o$ T, X * Q5 A7 J0 t" Y S& N0 P) g 因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。 . C4 {# I+ {( ~' R2 I2 B5 W4 a4 O 下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:, ?& v7 @9 e8 m7 C( P* N3 } 1)、专用发送buffer9 ^- T' q- s: v( u 专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。/ [1 ]; I0 @2 o' V8 b4 H ` 如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。 : H0 z( ^) _# D: t7 P 1 h0 H, H6 [5 M* E1 C6 `5 C O: \6 Y 专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是: 1 ?8 ]: [- B( H; v# o 发送buffer索引 (0…31) *4+发送buffer起始地址。9 }6 H5 F6 ~! k, l7 ^7 j , D, Q$ X2 B4 m( A& Q' t + [# i2 L# P9 H# ^1 I4 v0 \, s4 ] 2)、发送FIFO4 N+ }# L4 E2 e+ c5 P# W 发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。( p0 f; ^$ |/ ~! B) }3 m 新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。" b5 q2 Y$ j- H% t3 V+ d' a , K3 [, ~% Z2 ? C3 b* y" V4 M, v 发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。 如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。 如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。 如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。3 _$ t; ] a2 @ C; @ 发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:7 e. A6 ^5 e% h) h; l& n9 D 放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。 , x: V% [, u$ q- g0 _& C1 e. o 3)、发送队列 发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。 新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。 应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。7 {6 `1 R5 h6 x; J6 Y4 y) S' U+ l 发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是: 发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。+ D h5 Q0 @" ?% O, M. ?) ~! j 因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:8 V7 ]0 \# B* ` 1)、发送buffer全部配置为专用发送buffer, |9 C' C: o5 i7 s( m5 D 1 p# r. [ U) s" Y! j5 O# l: E" q 每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。2 x; O" Y3 G7 e/ C1 y9 _7 Q% | 2)、混合专用发送buffer和发送FIFO 在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。: B0 k3 w8 v1 d1 Z( O / ]' u, T7 l- P1 Z2 x+ G! @! z 发送优先次序:. W* H: H/ U2 E9 q( l A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区) B、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据, x' u' d6 o% o 3)、混合专用发送buffer和发送队列 # m7 @6 t. N5 G- L7 `" `2 h2 u 在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。 & {# U9 Q+ t% Z( I 发送优先级设置: A、扫描所有激活了发送请求的发送缓冲区2 c6 E! u. k" k3 ~% q; g* A B、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据6 V" d- h7 L" F 再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。 ] l* g* M& z/ i8 `, F: {/ v FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。 发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。 发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。 为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。 F3 y6 m) m, c+ A1 t! j# ^ CAN发送消息的代码实现: 发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:+ v7 E7 u4 M9 h : |$ X" o1 {# X, y
发送buffer请求寄存器FDCAN_TXBAR,描述为: 3 f- }/ U' _( K 此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。. v. V* G6 O1 b4 ]' R W$ h# I( } n3 d# q* N1 w% k; o ( S8 h( }9 s$ s; h 五、初始化 7 J0 T1 Q! c+ H+ ]" z+ S& s 初始化可以直接使用HAL库,
六、CAN发送示例(来自正点原子)5 v0 U! q% U; g9 O2 Y9 E+ e$ A 2 U3 i2 f: K! ?4 K( [. Q& G: I
七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)
参考:《正点原子》 STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准) - G1 n% Y; r* {7 I |
【STM32H7S78-DK】汽车仪表系统
【STM32H7S78-DK】基于 rtthread 适配 lcd 驱动移植 lvgl
【STM32H7S78-DK评测】TouchGFX (QR Code)二维码生成器
【STM32H7S78-DK】rtthread 增加 psram 内存管理
【STM32H7S78-DK】开箱与rtthread工程初体验
【STM32H7S78-DK评测】-5 LVGL&DMA2D DEMO测试
【STM32H7S78-DK评测】-4 LTDC&DMA2D 基本测试
【STM32H7S78-DK评测】CoreMark移植和优化--兼记printf重定向实现方法及常见问题
【STM32H7S78-DK评测】移植AI框架TensorFlow【DSP指令加速篇】
【STM32H7S78-DK评测】移植AI框架TensorFlow【下篇】