![shequ.stmicroelectronics.cn](./template/st_v1/static/img/logo2.png)
一、介绍 FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:/ Y1 ~2 h0 k) }; r5 q) c 3 z; I5 W/ M) _( E/ A9 q7 _ 1、每帧数据段最大长度由8字节上升到64字节。6 U; Y" b% P# I/ t 2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。+ y m3 f& Y# z, ^' w0 x* s 3、向下兼容CAN。 H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。 , r3 Q. K' f( U 二、结构 2 O! s' Z) T9 m5 Y 先看结构框图:$ }6 w3 d1 L5 y4 a% ` K2 b ![]() 9 f0 w+ ]1 U- B 1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。* Z0 s1 N. W, D1 \: {. r; q ![]() $ T( S( O# M+ p. w- s 可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。% f: z: o) P# _6 ]: A" P3 E 2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。/ I( z0 k' H7 F c8 ^1 u: F & @8 s4 X" J2 o/ K 3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。 / [ Y! v/ y! A$ f; g1 a# g& t 4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。 5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:5 ]4 Y& b9 `. @; j: |8 G5 }; R * o: G) T' l, h: s1 Y5 p ![]() - {: Z1 N' Q4 w& W' E9 A 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字。 " B2 w) \9 u' J9 ^& `% R ?" V 1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。 2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID), v* d5 O. P( Y- S 3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。 4 o) N& @; q5 Y: N+ ^2 f 4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。 9 u( g+ p* k ~1 e 5)、RXBC.RBSA:接收buffer,最多可以有64个元素。, P7 J2 X( i( q3 e - l# c3 d$ A$ ~1 @ 6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)" ] z R# u. E; t7 Y 7)、TXBC.TBSA:发送buffer,最多可以有32个元素。 8)、TMC.TMSA:触发存储器,最多可以有64个元素。6 D5 h4 g( f6 q! q2 P& o 7 v8 L: _' d% F9 T. ? 《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》 : {4 u+ n, I- J4 L0 |/ [- ^+ k 下面来详细介绍这几个功能区域: 1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:8 I5 d3 b ~6 k6 l7 L, j* L $ b: m8 J" T( @0 @7 `7 W, S2 C) r ![]() SFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种: 00:范围过滤,过滤到SFID1到SFID2中的所有信息。 01:双过滤,过滤到SFID1和SFID2中的信息。 10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。8 D& O, L! k6 B. F6 ?& k 11:禁止过滤器元素5 ]. l! B W1 F/ G 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。$ v5 e8 Z( H( N H) V# d4 J SFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。; t- l3 }& @8 \$ @8 ^ SFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。" @( q. k9 X8 n0 z9 c 00:将消息存储在Rx Buffer中。" |6 v0 _3 p! l* f& l" D y! m ]: Q+ i# x( B9 V* l6 U x 01:debug消息A。 1 @( U' K' m( m9 P0 Q 10:debug消息B。" R- h( y9 y9 N# X4 } 3 V g( f* _. ` D3 k1 l3 P: x1 _ 11:debug消息C。 ( U" E0 e5 x: j& L$ C9 ] SFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。 ) e% o/ r, w( I! x; @4 y7 ? SFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。/ ]+ d2 K, r# U$ T5 E1 y 2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为: ![]() F0 EFEC[2:0](bit31:29):扩展过滤配置& L& p- k* l/ v8 }; u. I. Y& c! ~3 h+ k 000:禁止过滤器元素" v7 Y! P* A: y% O; X. B" U! C 001:当过滤匹配以后存储在Rx FIFO0中。3 j% G+ D( p! a! u; K 010:当过滤匹配以后存储在Rx FIFO1中+ d" G' N- @/ p 011:当过滤匹配以后丢弃。) G! d9 w0 k& C 100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。 101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。 110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。) K8 c1 Q' D4 v6 p3 u- h, K 111:存储到Rx buffer中,EFT[1:0]忽略。 F0 EFID1[28:0](bit28:0):扩展过滤ID1。 F1 EFTI[1:0](bit31:30):扩展过滤类型 00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。7 y5 g* S- [% l, d y! F, D& _ 01:双过滤,过滤到EF1ID和EF2ID中的信息。, l+ H! s: Y5 t 10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。 11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码3 p! \& m& D" k F1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。+ Q6 q% U# h. B7 A; M! ~; S 00将消息存储在Rx Buffer中。5 e& ]: }1 u& \* n2 T; t 01:debug消息A。 10:debug消息B。( ~) G& l; T) l 11:debug消息C.1 T- x/ Z% N" t' T F1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。 F1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。7 P9 A) a8 b$ W" z3 P2 M 3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同: ![]() / F& N0 O6 Q. q8 T: a2 P0 k7 ] R0 ESI(bit31):错误状态标志2 U, a& x4 B0 c 0:传输节点显性错误。 1:传输节点隐形错误 R0 XTD(bit30):标记是标准ID还是扩展ID" c* y% c- y9 J; j+ f 0:11位标准ID* P: X6 m( |* P' @+ M2 O 1:29位扩展ID 6 {6 \1 S d: [+ d6 r% y; K. f R0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。4 @. x5 t, Y' U7 Y( y; V& v9 j8 |9 B 0:接收到的帧是数据帧0 a) @$ s6 A5 a4 a( X1 X7 N3 Y & H, ~& c5 [( K 1:接收到的帧是遥控帧 R0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。% @5 U% q1 v7 H& T) ` R1 ANMF(bit31):4 N; E5 v3 t H, U: ~ ' s% v2 V2 E: v, C 0:接收到的帧和FIDX中的过滤帧匹配 1:接收到的帧和Rx滤波器中的任何元素都不匹配 B4 y5 q! n7 c0 z. B3 c0 M+ i R1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。 R1 FDF(bit21):帧格式( M& {- S) A( d5 p 0:标准帧格式 1:FDCAN帧格式。7 i) M8 M# w% X R1 BRS(bit20):比特率切换 0:接收到的帧没有比特率切换 ( d9 C. P4 J' c- x! ~( K 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:接收到的数据。: j8 \& W6 j8 c9 I1 U% m. C- ~ ( y5 `( u" |) B4 w1 M 4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。 " l9 k [* c7 q# b2 N5 l ![]() E0 ESI (bit31): 错误状态标志 0:传输节点显性错误- A5 |& e/ D; K t+ f Z- `; F 1:传输节点隐形错误。' B! ]" q8 k6 o6 q* n0 Z2 [' o E0 XTD(bit30):标记是标准D还是扩展ID5 D5 L+ r; U# y. ~ 0:11位标准ID.( }# P0 V5 `& `1 X 1:29位扩展ID E0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。 0:发送的帧是数据帧4 J5 M* ^8 r+ Q 1:发送到的帧是遥控帧 E0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。( o# b5 g% G% z2 {! x E1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。 {! \5 \" S. Q, q( h9 m/ ~ E1 EFC(bit23:22):事件类型 00:保留。 01:发送事件。) M; r i0 i5 [* y* t! E4 ]5 Q 10:发送取消。 11 :保留。0 E5 r% @( o7 N4 C- `% V9 @- ? E1 EDL(bit21):扩展数据长度' O2 m9 ]& P, q# a" t* C( l 0:标准帧格式 1:FDCAN帧格式。) r4 \$ q; M! D b B5 O E1 BRS(bit20):比特率切换/ g6 H* d {3 N: d k9 Y 0:发送的帧没有比特率切换 + A: t, f1 } S- |2 c8 w 1:发送的帧带有比特率切换6 s! v: | o. { E1 DLC[19:16](bit):数据长度。* D9 w5 l2 m$ c 0-8:传统CAN+CANFD,长度为0~8字节。 9-15:长度为8字节。 E1 TXTS[15:0]bit15:0):时间戳。 5)、TXBC.TBSA:发送buffer,有32个元素。 ![]() ) Q$ E! N9 @- ] T0 ESI(bit31):错误状态标志& P! O/ n% E2 {6 z 0:CANFD帧中取决于被动错误标志。/ Q% i* g5 o$ ~; D9 r 1:CANFD帧中隐形错误。; v2 }* R! I% P3 f T0 XTD(bit30):标记是标准ID还是扩展ID: ^; |% w7 @/ h: H8 i# ~ `+ c7 }9 c5 A 0:11位标准ID- N$ ^0 j \1 u7 l+ k6 z' p4 L' q # D% l( _5 v+ [( U0 G: @6 R 1:29位扩展ID% a. j2 p h# B8 ^. X! j+ U ' n, n+ q& g- W' X T0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。6 r: u7 [8 y- }2 l# M5 f 0:发送的帧是数据帧& A$ c9 D0 n8 W& x) h 1:发送到的帧是遥控帧 T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。7 i( k T7 x: j* X0 v; k T1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。 T1 EFC(bit23):事件FIFO控制 0:不存储发送事件。 1:存储发送事件。 T1 FDF(bit1):帧格式 0:标准帧格式 1:FDCAN帧格式。1 I' w1 V# F. a9 M T1 BRS(bit20):比特率切换 0:发送的帧没有比特率切换 4 G9 g1 c( `) H( o9 g 1:发送的帧带有比特率切换- ~2 ?* D# b; h) R# b, ?- K T1 DLC[19:16](bit):数据长度。8 r, A- S( ~! g. U! j 0-8:传统CAN+CAN FD,接收帧长度为0~8字节。4 {. p% n! ?% i: o( \/ K1 U 9-15:传统CAN,接收帧长度为8字节。% d3 G! s S- V0 | q$ m" D 9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。3 r, w8 {1 j0 R2 R: O6 T! P n T2-Tn:发送的数据。7 m/ f+ p. f- G/ z; C* D 0 A1 L; v/ {" m) W 三、过滤器设置 标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:' y+ y7 T! [9 x* F. k ; u: B. H2 Q. T- d' V 1、指定范围过滤 通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:) m1 _& K% M D, B7 k. F3 ?) l
2、指定ID过滤 % n# u5 \4 z" j9 l7 { 我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下: % H* x6 p U& y; x
3、传统的位过滤. C A2 i4 Z" } 第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:2 y$ a2 U1 v, O8 ~( e: L* a
其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。+ Z/ B; w: n# {, P% s$ q ) W! n C) t0 {! Z/ d: E/ f 四、CAN的收发过程/ Y, O+ h ?, |$ ?3 |- z7 R; O" U+ N 1、CAN接收过程, L" e U- a- |8 s$ G CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。 0 X! o H3 t A6 g0 ~) n* s 接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。, [" [6 H0 z1 g. ^0 W- l( u9 L/ r0 f ! c; m2 p B1 d. h ![]() 当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。 - V5 e2 r0 Z; }1 Y ![]() ' i3 t: X3 D( G- J0 l( [ 为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。0 Z* J0 d1 e* n2 H; p; ^ 读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):* U5 n/ C: H z. ]( V/ ]) O 1 b' T/ [6 ~+ g, }7 f7 [$ K
1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。 . e! w z9 {2 j4 y* D0 o ![]() 2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。 + }+ p" W0 e: H% k 3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同& F* j" ]2 ^2 m$ p( z2 a ( H( f: j% J% Q% P0 V$ E 如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。. K r2 i: U* D. }, K% L 如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。/ p2 R6 d$ l* N" L4 {! a, e 6 G7 g# d9 K5 x3 n- c0 i% c 计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:
2、CAN发送流程 在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定: ![]() & t+ o. M+ M. E& y* V1 l 因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。, n% A* z2 A2 B4 h8 { # R6 \: ~# n" |( V9 |2 n; P 下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:, Y6 m+ h5 `8 s, Z4 y/ F/ o 1)、专用发送buffer3 O0 p1 t M2 ~( \# E+ K+ z; x 专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。 如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。' ]- O) [: i$ G. `" q; I* w- b Y) n) w 7 g2 o' c0 f9 u# a% h ![]() 专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:3 f' |2 Y6 d' I8 O8 V9 s* @8 b 发送buffer索引 (0…31) *4+发送buffer起始地址。 . H" I! U% F! y9 l! E! O/ z , ^5 p4 F# c; D- H: i: g" t- m. { 2)、发送FIFO1 y' k+ g9 Y# f8 Y5 M 发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。7 J# c5 s: ?. d c1 S) v 新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。 ![]() ; \" T" ~# q0 _0 s 发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。 5 B4 A4 ` Y+ A' C* E 如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。 如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。 如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。 发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是: ) a6 P8 n! {" b4 J4 \ S 放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。 0 G/ A9 z: Q1 K5 q9 H4 x 3)、发送队列3 u/ |9 c1 U/ G1 e( `& Y |7 R4 D 发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。/ H5 q' v9 k) L8 k9 a- Z% O 新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。 应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。3 f! w+ M q6 a4 @6 v) g, D 发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是: 发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。: n1 }- t" t8 _. m3 K7 @" o4 M4 w : w! D2 \7 O" ]% \ 因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:8 o1 `5 E( x2 J+ H: j1 a ; R. b3 m$ z; U* m 1)、发送buffer全部配置为专用发送buffer" t9 H3 \+ ^* a( ]- y $ P& E: U- G% ^0 l+ i 每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。 % ]7 W5 H/ \/ Q3 s- y! q7 C; S+ M4 C 2)、混合专用发送buffer和发送FIFO/ G+ i: _* [+ |) y * U. a0 L! X m8 ~3 a, @ 在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。 ![]() 发送优先次序: A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区) B、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据; U# h4 }4 g! w \ 3)、混合专用发送buffer和发送队列 在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。 / L) B) t% J q6 u ![]() 发送优先级设置:' K) z: O: Y6 W/ C8 x) h A、扫描所有激活了发送请求的发送缓冲区 B、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据$ D* x! F; {0 j- D2 d+ d5 m ! K1 p9 Z" n( N* X1 N+ e& z 再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。 q1 `7 D5 s% j: w/ X7 b* f FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。1 ?8 l/ r! @9 Z3 k 发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。 发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。 , B8 p8 U/ G8 i0 V9 [4 s 为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。, D2 z) \: Q' E2 A# k D CAN发送消息的代码实现:. U# s6 I q9 Z2 T / z8 A K4 J n 发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:/ H7 B6 D/ ?3 @6 B9 G1 y ) h8 A+ |1 O- t+ r6 H6 }3 ~8 e
发送buffer请求寄存器FDCAN_TXBAR,描述为: ( Y, |# t8 X" O# x$ o# N$ ~* O 此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。 - Q# B4 Q! T6 ?2 d' s) k9 j 五、初始化 - D% M+ h: l# d5 a1 Q+ W 初始化可以直接使用HAL库,
六、CAN发送示例(来自正点原子)
七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0) 7 F! q! Q" K5 ]5 C% _
参考:《正点原子》 STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准) / L+ R, S. l! d6 U. }( Y |
基于STM32H7 SPI NSS 功能的灵活应用经验分享
线下培训 与ST专家面对面 | STM32H7R/S系列高性能微控制器-拥有MPU般高性能与安全性的MCU
【管管推荐】STM32经验分享篇
【经验分享】STM32_H7_ADC
STM32H7R/S高性能MCU:安全性,大存储和优异图显赋能更多应用创新
Stm32H7XX GCC下分散加载实现
【银杏科技ARM+FPGA双核心应用】STM32H7系列10——ADC
DIY-STM32H750核心板
[nucleo-H7A3ZI-Q]1-点亮一个皮皮灯
DIY-STM32H743核心板