
一、介绍 / B$ M* k/ o1 t5 o9 M! o( m7 Z FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括: 1、每帧数据段最大长度由8字节上升到64字节。 2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。7 D% p6 ~. a4 h0 f 9 ?1 g- |7 ?3 I& u% B" C: H6 Z 3、向下兼容CAN。: T7 y g" {- M9 R, x6 y7 c6 O H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。 二、结构! Y$ a5 v+ t% i/ Q$ V' s) l/ e! U: Z 先看结构框图: - Q2 m1 p- h4 A( `" y4 K7 m ![]() & V: @, y3 Y6 d# h0 Q 1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。# A1 b. A, N9 [' |( S# L2 ] [: J $ A+ _* Q% z+ M% g: [ b ] ![]() ^ T! i8 D6 g: Z8 x 可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。 2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。 " N( p I# u: c, w 3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。8 H& k! ?( A- a. H Y) l 4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。. @+ j9 l# u7 Y# [3 W, u . }) U/ H* j- i) S! B N% t 5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:7 w( R% }* K$ U2 Q ! q' ?" S& |: `- a' Y8 I ![]() 2 h: ]9 L/ b i* D* k3 o5 } 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字。& G4 R4 W/ f, I* E- U0 }! a + j/ Y8 j2 u" w# D; [( k: M5 w 1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。% G% S! {' d3 S/ l& B - {& q) Y% C Z! Q1 ` 2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)8 ~6 ?9 P6 m9 q+ ?+ A# |0 Q $ J9 @/ K9 g: V0 o% Y 3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。 4 L5 w6 X# j( g8 I 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个元素。 ' l' R0 `$ a6 L7 G( ? 《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》2 i' y4 R% K' {. ?. g8 r0 a3 [ 8 N; Y+ W" k1 T 下面来详细介绍这几个功能区域: 1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为: & x Z# Y# s; ~; L ![]() SFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:" p+ r* g' ~# D3 h" E7 u 00:范围过滤,过滤到SFID1到SFID2中的所有信息。 01:双过滤,过滤到SFID1和SFID2中的信息。) B4 Q; X0 ^, `8 V 10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。9 u; I" t+ k/ ? 11:禁止过滤器元素3 K. V# [1 U$ J9 t2 \8 _ SFEC[2:0](bit29:27):标准过滤配置 000:禁止过滤器元素 001:当过滤匹配以后存储在Rx FIFO0中。 010:当过滤匹配以后存储在Rx FIFO1中。 011:当过滤匹配以后丢弃。 100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。( [, n4 J" D* M1 _ 101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。' m; ]$ k/ i1 U 110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。 111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。" I5 L0 y5 R- }( S, S) _. M0 j SFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。- S" E: `0 |& B, o/ H m SFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。9 h: w/ }" z& I# B 3 Q2 @9 w) |- C! K. @2 J7 z! U. j2 ~2 { SFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。 00:将消息存储在Rx Buffer中。- G0 T( U$ B1 Y8 b % W: x$ d3 G# N' d 01:debug消息A。 10:debug消息B。) ~0 J4 [7 e6 G% V4 o. Q ( r. e4 G+ K2 j8 q( t3 R 11:debug消息C。 J1 C. E/ O, |# ]* Z2 g# F# J SFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。 $ a% J1 l0 T: I, l SFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。 ( a* G+ n1 |6 T 2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:6 {4 Y4 u# F7 v0 T2 i7 G ![]() 7 Y7 X+ s) v8 I9 p2 d" r F0 EFEC[2:0](bit31:29):扩展过滤配置 000:禁止过滤器元素 001:当过滤匹配以后存储在Rx FIFO0中。 010:当过滤匹配以后存储在Rx FIFO1中 011:当过滤匹配以后丢弃。 100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。9 c2 n2 e' Y! t1 |. g L/ j/ k 101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。 110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。! n& N1 C5 y# v0 V; [1 k g! L( Z 111:存储到Rx buffer中,EFT[1:0]忽略。 X6 X& T3 R3 A& l: P, Y* [1 y F0 EFID1[28:0](bit28:0):扩展过滤ID1。 F1 EFTI[1:0](bit31:30):扩展过滤类型$ ^; p) P+ D# _ 00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。 01:双过滤,过滤到EF1ID和EF2ID中的信息。7 P& v# b/ Z/ l) L9 P 10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。 11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码5 g- B- F% q i1 G* C/ ] F1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。 00将消息存储在Rx Buffer中。- w2 e F: _* T+ ^+ O* ^& N 01:debug消息A。 10:debug消息B。' u7 B: A( ~ I6 ^* S; M9 W' Q 11:debug消息C.2 n3 s1 R G7 j" P8 k F1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。( ?- Q% a" ?, r9 G8 K+ R F1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。) S7 T( J' e& S* W ' @& G# R$ T& R; X. B 3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:5 K6 Y) g- |; u' H5 `1 y0 d ! E9 k8 u. @ z3 b ![]() 6 H/ C! E( b; g$ x# O( {' Q9 e R0 ESI(bit31):错误状态标志 0:传输节点显性错误。0 P, V$ ?) L$ R+ M 1:传输节点隐形错误2 s7 Q% k% N8 e% t, f R0 XTD(bit30):标记是标准ID还是扩展ID 0:11位标准ID 1:29位扩展ID- C% C: R) }& E: _5 z1 b R0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。6 o* \6 {( B# M$ @; u 0:接收到的帧是数据帧# m, D% x( r5 m 9 q' l. e/ [" J# u% J; E( m4 ` 1:接收到的帧是遥控帧 R0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。2 R$ j. c$ R* g3 b0 |1 W R1 ANMF(bit31): - Q* `8 T7 \; ]' u% o1 b2 B 0:接收到的帧和FIDX中的过滤帧匹配 ( E) k9 T/ l: d' D4 E5 R3 M 1:接收到的帧和Rx滤波器中的任何元素都不匹配 R1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。6 C" h. q$ \- I( K- n R1 FDF(bit21):帧格式+ X) S3 X# ^0 Z3 ? 0:标准帧格式 1:FDCAN帧格式。" O+ U$ \ T1 Y& L5 O! | R1 BRS(bit20):比特率切换 0:接收到的帧没有比特率切换 " ~7 J1 T8 A( [0 @' L) G 1:接收到的帧带有比特率切换 R1 DLC[3:0](bit):数据长度。 0-8:传统CAN+CAN FD,接收帧长度为0~8字节。 9-15:如果是传统CAN,接收帧长度为8字节。4 z% j* F0 t3 o! t* e6 v" F 9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。 R1 RXTS[15:0](bit15:0):接收时间戳。- y. B; e8 x( ?9 M7 F R2-Rn:接收到的数据。 4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。% g9 L7 U' [ e( U! ? ![]() 3 @0 I. y; y9 y% f. f E0 ESI (bit31): 错误状态标志$ o: M; j# F; Z7 R1 ^ , s" y1 H) h1 l- N I; t 0:传输节点显性错误0 ^6 G+ z" }" P 1:传输节点隐形错误。4 I- Q' G4 N% y: j6 V+ U( h7 X E0 XTD(bit30):标记是标准D还是扩展ID 0:11位标准ID.$ ~5 x& z. o" g. {% m; u1 S 1:29位扩展ID! x" x+ c* A2 ~+ n# {8 U5 _ E0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。6 ?# `8 i3 w$ |& P- o0 x- L 0:发送的帧是数据帧: K+ x* @% k5 a 1:发送到的帧是遥控帧5 O6 T; z+ F; I$ ] E0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。" }, G0 E8 m4 ]% j/ {! h! a! ^ E1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。 E1 EFC(bit23:22):事件类型5 S( _ Z- `& w9 i; s3 N9 m( p 00:保留。' L2 ^( T& R5 }! O) q; {( z, P 01:发送事件。 10:发送取消。, Q6 a# f* H" k1 w! K 11 :保留。 E1 EDL(bit21):扩展数据长度8 { I* C5 r/ s3 ? 0:标准帧格式 1:FDCAN帧格式。 E1 BRS(bit20):比特率切换: n: B' W0 H# }( }, l 0:发送的帧没有比特率切换& K1 t% l* d8 F x9 e& M- M 3 W* o; z7 k: Z( [/ U- k1 t7 b, O0 D. x 1:发送的帧带有比特率切换1 a* {7 d* O: d3 Y2 u E1 DLC[19:16](bit):数据长度。 0-8:传统CAN+CANFD,长度为0~8字节。$ X, p" I- t. Y( Z, ~ 9-15:长度为8字节。 E1 TXTS[15:0]bit15:0):时间戳。) @/ G5 ^ `9 k 5)、TXBC.TBSA:发送buffer,有32个元素。# e1 s- Y$ j& B4 f- s- H " M5 k9 `5 g2 e, z7 ] ![]() % L0 |$ M% d/ l8 @- v: H T0 ESI(bit31):错误状态标志6 y) ~, K9 R+ k 0:CANFD帧中取决于被动错误标志。 1:CANFD帧中隐形错误。: A- }! F1 O+ p2 h: O# U T0 XTD(bit30):标记是标准ID还是扩展ID& }: O( j F: N ] 0:11位标准ID 7 H1 t- p' G/ F' c& d 1:29位扩展ID T0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。 0:发送的帧是数据帧 1:发送到的帧是遥控帧/ E/ {. L `7 t, H! ^: K6 P T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。 T1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。/ V( x6 V* w- W! r5 D T1 EFC(bit23):事件FIFO控制 0:不存储发送事件。3 l! C( K( J d% [& \* \3 C: T3 B 1:存储发送事件。# g1 H V8 [* a: t T1 FDF(bit1):帧格式8 c- P+ w, u1 ?; H! C 0:标准帧格式( o N7 x7 R1 h* Z 1:FDCAN帧格式。( l+ g; y- N% j, S S! c5 U1 M4 z8 y T1 BRS(bit20):比特率切换: S7 W: Y& w4 R" H- r) a* \ 0:发送的帧没有比特率切换 1:发送的帧带有比特率切换 T1 DLC[19:16](bit):数据长度。 0-8:传统CAN+CAN FD,接收帧长度为0~8字节。+ D0 I J! ?$ k0 |; h 9-15:传统CAN,接收帧长度为8字节。# [# b( g0 y6 \* F" e: x 9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。, f) H$ o+ M# c2 x, b# y T2-Tn:发送的数据。- ]. l7 y9 i0 }6 [+ t; V1 A, R5 z& J : j9 j7 {, y4 X- `; {% @& @0 H8 o+ p 三、过滤器设置 0 X0 {& l' `# |# T. q 标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:+ Q1 u, {! @0 Y, }* b $ j! O8 L. B5 e9 i8 { 1、指定范围过滤 通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:
2、指定ID过滤7 e+ Z( P. Q: |9 u( e8 X 我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:, a' E) v; x3 p5 b | o! L ; J' g& Y9 z3 y, u# H$ ^
3、传统的位过滤: r* |! Q+ H9 Q; p7 D 第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:
其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。7 b: ^; |, r& P8 U9 k$ q2 p- f( S( i( ] 四、CAN的收发过程 / }$ @: z/ Y# i; } 1、CAN接收过程4 g) b2 ?. I% Q+ q CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。 接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。. C$ _& R$ i; V0 g/ I+ Z1 a ![]() 当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。5 l7 y0 _+ N* P4 T6 \$ S. V3 P ' B8 E! x# e: O( s% ?7 O 1 T- e+ q$ A7 k E' l" L3 z$ f' j! J ![]() 5 j1 Y1 v( l, E7 w 为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。! O7 K/ r) W8 D2 I7 i* e0 b( K; i 读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):, F/ ?- W* a% ]# v; m
1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。 ) j2 ~- |5 A9 r* G! i2 j& R ![]() . {& x4 [/ `6 H1 H5 t3 E 2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。: M$ h* u* j( n( c 3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同5 Y, n, V" ~1 Y6 M 2 ]* I" ]# Q5 B( K2 P% ~ : M$ H. n9 [9 q: R2 v! U 如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。 % E$ z S1 X" a$ M 如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。8 c8 J6 t* I5 |. P4 R 计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:
2、CAN发送流程( u& x- s# R1 q& a# g, ^0 h2 E! [, ^ * E3 Q5 T; |* o- P# N# X# } 在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定: ![]() 因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。 下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:5 J9 U8 c3 Y. ^$ y; @& K 5 Y- i& G3 A; p) B+ h 1)、专用发送buffer' P. v7 P p9 y- s- h' q - |$ [; z. H# g7 I- u, X& ] 专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。 如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。8 H q- U# s3 X# A( N D ![]() 专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是: 发送buffer索引 (0…31) *4+发送buffer起始地址。8 }0 k# t A6 y: v , Q" s( G) k2 f$ i7 t 2)、发送FIFO8 [+ h$ m. w( G4 x ' ]% T$ v2 A4 q! t) h+ `/ H 发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。 新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。 6 i' q- W! l0 `5 c2 e7 B ![]() 发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。" Z5 n0 O/ m$ U- s 如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。 如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。 如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。 发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:3 l# Q8 V7 \+ I8 \# L) }4 j1 b# [ : i' _1 V* ?/ }* D. y. p" @ 放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。" `( a# J0 C( \; E& H 5 ?" c: p2 P; M% g 3)、发送队列0 f6 g' s3 J+ n, `" w! A0 K 发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。 新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。 应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。! [* k. O( G) C1 A; j- I9 x 发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是: " f1 C6 e! n" Y5 I/ v3 x 发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。 因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:3 [" [8 ~ g$ q0 S; ? R% q / x7 y0 f/ F& N# k* o 1)、发送buffer全部配置为专用发送buffer ( C$ l4 {( R8 s 每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。# q* _- V: _. A2 Q. O - a/ `, V8 k/ `: } 2)、混合专用发送buffer和发送FIFO5 x. X! ?/ H0 a, X 在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。' d6 d8 _+ H, R3 d 5 W- `" u5 G3 ]; c! T/ o9 k ![]() 2 G0 R9 o; M9 S. h 发送优先次序:% q, ~8 @4 b! W( ]) |' @ A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)0 p! y- } I4 R! [5 ?% @ B、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据) |+ C0 t @# L: W : s2 _- F) }) f- i9 u/ H5 U 3)、混合专用发送buffer和发送队列 在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。 ![]() 6 _: a; y+ g; K9 N! P Y3 Q+ d; ] 发送优先级设置: A、扫描所有激活了发送请求的发送缓冲区" w& f: W1 ~7 P# N& n* v; N8 ]1 l B、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据 再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。* x! z) N- o9 y FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。 发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。5 \/ }1 S( v6 ]4 b" x5 N 发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。2 l0 h6 Q' L8 M9 s1 p 8 z$ B& L- V) V% j 为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。 : d7 d {" g$ A CAN发送消息的代码实现: e7 @- i- W. x- y$ L 发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ: 0 L% W+ _) `; E1 g7 R
发送buffer请求寄存器FDCAN_TXBAR,描述为: $ t7 N5 C. B8 k4 m! b. z 1 W; P- }: q: ^& p7 m 此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。 ' Y/ M, J9 Y/ Q* g / }; C: T" `5 J- H; x2 _2 t0 [2 p 五、初始化" {8 q4 ^0 L% Z" A 9 z7 \! C2 L: @1 n( |7 ` 初始化可以直接使用HAL库, % s9 F( U- K+ q6 X
2 `% y2 q( Z% }2 k+ _- { 六、CAN发送示例(来自正点原子)1 O1 w' \/ G/ ^) V
七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0) . Z# W, `# `" ?& v6 s. n. K
参考:《正点原子》 STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准) ) G$ e) e c7 d ) _ y) C: e2 c9 L |
STM32H7的TCM,SRAM等五块内存基础知识
STM32H7的TCM,SRAM等五块内存基础知识
简单了解一下STM32H7的BDMA
有奖预约 | STM32H7R7基于RT-Thread RTOS的智能终端GUI解决方案
【STM32H745I-DISCO】基于TouchGFX的工业控制器界面设计
STM32H745I-DISCO串口打印
【STM32H745I-DISCO】TouchGFX探索——3、触屏滑屏操作与中文显示
TouchGFX软件下载及使用
【STM32H745I-DISCO】TouchGFX探索——2、照葫芦画瓢
STM32H745I-DISCO硬件信号为空,M4、M7对于硬件信号的优先性判断