STMCU小助手
发布时间:2021-12-22 14:00
|
一、介绍9 N- w/ u! v0 h! Y1 t7 o , T7 e+ K9 L; ^+ Z1 D FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括: 1、每帧数据段最大长度由8字节上升到64字节。1 p3 a4 L; O, `5 C$ {' O ; Q5 m- i$ @! D4 Y2 e 2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。9 t L4 M5 ?& a$ B# i 3、向下兼容CAN。 & F$ ~$ A! l$ z5 R- Q H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。5 ?8 ~2 {0 W: v 二、结构! c; @" u8 V7 g$ ? 8 f) G, X) s7 t9 b- q: m! }0 {- n 先看结构框图:
& y/ ]) O1 v7 @* v 1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。 4 U0 ]$ D8 z# e, @5 P+ X7 G
: I3 ^8 [. ~* A& i5 x 可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。/ }* C U7 R$ T! Z; | | 9 s) s! o4 s% F! J2 C 2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。: V* b5 t/ t. V" y Q0 z1 R 1 N+ _/ Z; a; A9 f! y 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。内存分配如下:7 @6 c$ i! U+ o5 t% e# h8 o9 U
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字。 0 B$ D; u% Q7 c2 \% n: B 1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。; Y, l, c/ M- C: T! t, u 9 [6 h3 K) {. D0 n 2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)/ c/ T+ V1 b2 E% g' @7 [$ a8 g, J/ b; X 3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。 ( q* I0 _- q6 K3 |) z3 x Z 4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。 % Q# R: t, l& A( ] 5)、RXBC.RBSA:接收buffer,最多可以有64个元素。 ( u7 S# H: Z8 e6 B 6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)% G0 J0 H9 M+ p' u 7)、TXBC.TBSA:发送buffer,最多可以有32个元素。$ F5 x7 c( v! B) a / A9 D; P+ ^! L7 }3 d4 d$ s 8)、TMC.TMSA:触发存储器,最多可以有64个元素。- H& w2 F* I# m7 C7 }* E 3 }( L- p- f+ {& w' D 《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》! v8 J0 K. {7 w' ?/ d 3 l1 f+ \6 F! Q! c- ^% v 下面来详细介绍这几个功能区域:: `8 ^& E% e: D S- _ 7 n9 L5 G9 K) f0 T" W1 W: D/ s3 m 1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:
SFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种: 00:范围过滤,过滤到SFID1到SFID2中的所有信息。 A: i" _" `- ^: ?+ l( A5 h8 w 01:双过滤,过滤到SFID1和SFID2中的信息。 10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。7 W2 T: E8 L4 U 11:禁止过滤器元素* H" v+ I3 T- C- M# {# v5 j SFEC[2:0](bit29:27):标准过滤配置 000:禁止过滤器元素4 d8 i( V- E% t3 R* u& M: y$ i 001:当过滤匹配以后存储在Rx FIFO0中。) @# N( G0 R& s 010:当过滤匹配以后存储在Rx FIFO1中。/ P" O" T, E+ K, W% h7 T' ? 011:当过滤匹配以后丢弃。8 h6 X4 d1 q( ~, H) f! F. _ 100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。' w+ I. q w4 w5 ]. x2 Y ` 101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。, U' b& t7 T3 [# v5 N9 y3 K 110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。 111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。 SFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。0 g* e! b: g+ j1 a SFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。 0 J8 F$ e* v- {' y! E SFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。5 J7 E1 p6 ]! J6 R, U+ G1 K 00:将消息存储在Rx Buffer中。 01:debug消息A。 ( d+ h1 v! e7 v+ n9 |3 R6 l0 L( o" S 10:debug消息B。8 E4 b1 q% w0 Y- u9 q & N) e, z: e. N& m- }# {6 K0 F 11:debug消息C。 SFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。 SFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。% u4 ~$ N4 f8 L: k/ z2 A 0 {# D$ S0 V; y+ {3 {9 v5 r 2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为: 5 w5 b5 v, T9 Q9 F. Y0 P
. `0 B+ v. e& ~6 q& Q9 O" _! h8 m F0 EFEC[2:0](bit31:29):扩展过滤配置2 ]8 Y U1 M9 h 000:禁止过滤器元素, J7 O3 ?- Z2 `" o 001:当过滤匹配以后存储在Rx FIFO0中。/ e! i$ b9 N i" b9 D 010:当过滤匹配以后存储在Rx FIFO1中( J5 O% x8 p# |' E; ? |5 G L- a, R 011:当过滤匹配以后丢弃。0 ^: i1 B9 y3 @ 100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。 101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。) a- z, ]9 O3 T/ Y 110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。* m! @! ]9 W; |, x I* } 111:存储到Rx buffer中,EFT[1:0]忽略。, i) u' D! ] @* }. |0 {3 S F0 EFID1[28:0](bit28:0):扩展过滤ID1。 F1 EFTI[1:0](bit31:30):扩展过滤类型 00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。 01:双过滤,过滤到EF1ID和EF2ID中的信息。2 z# t) F; H, E. z) h( `! q 10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。 11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码 F1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。 00将消息存储在Rx Buffer中。7 _& E" K) ]; \4 M 01:debug消息A。* I0 B7 R' C! Z& K$ U6 _5 c' `$ ~ p* j 10:debug消息B。+ k' }: d( G' C* T0 s; D. S6 V 11:debug消息C. F1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。' {- O5 |* f: o# y5 N 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字,它们的位定义相同: 9 I- j- O8 C8 @. v* x$ p- D
R0 ESI(bit31):错误状态标志) {+ U+ D4 p4 q8 k 0:传输节点显性错误。1 F* w A5 F7 {/ O4 v9 j0 v9 M2 B' o 1:传输节点隐形错误' r3 w: V Y( t5 e; Y" r: s2 F) U* Y R0 XTD(bit30):标记是标准ID还是扩展ID 0:11位标准ID 1:29位扩展ID R0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。 0:接收到的帧是数据帧 1:接收到的帧是遥控帧# D* [, b/ u0 _& {- c* c6 p6 w( X+ Y! J R0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。0 E" h1 M6 k" |$ G R1 ANMF(bit31):3 g4 I3 ~" c% N2 d" L 0:接收到的帧和FIDX中的过滤帧匹配+ Y2 L: z5 g" _9 H, a3 r $ C3 [5 `% K1 R6 j 1:接收到的帧和Rx滤波器中的任何元素都不匹配 R1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。 R1 FDF(bit21):帧格式 0:标准帧格式2 d( B( f h; E" R$ ]& a6 c$ B 1:FDCAN帧格式。8 U3 f0 S" {( t, Q: P4 I R1 BRS(bit20):比特率切换/ e# D3 j: `! \; K" T 0:接收到的帧没有比特率切换0 }( v8 C @+ r3 }; k% I- r 9 V( \) H p7 C" f4 P2 \ 1:接收到的帧带有比特率切换7 T9 S4 u/ v F/ \6 b, u R1 DLC[3:0](bit):数据长度。 0-8:传统CAN+CAN FD,接收帧长度为0~8字节。 9-15:如果是传统CAN,接收帧长度为8字节。* E4 W# z: c, E5 o, E 9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。 R1 RXTS[15:0](bit15:0):接收时间戳。7 v/ I- y7 _5 k9 {5 e$ H" I R2-Rn:接收到的数据。5 S4 Y5 S! K) C9 x. H3 F 3 q! I$ G1 h, x; _, Y: V 4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。7 g6 G @% x2 _: o9 z6 @ ( D3 q1 Q4 S1 ~: w, s7 g
E0 ESI (bit31): 错误状态标志 . _8 Y% e- b( w7 j$ i% A w 0:传输节点显性错误 1:传输节点隐形错误。 E0 XTD(bit30):标记是标准D还是扩展ID 0:11位标准ID. 1:29位扩展ID. @# @. k: M/ B5 V+ a, G ' b7 k; o0 [" l8 Y E0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。: W7 i$ z* |7 w i7 h 0:发送的帧是数据帧 1:发送到的帧是遥控帧 E0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。+ q+ t" o% r, L) P, L E1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。 E1 EFC(bit23:22):事件类型( t( ?5 M- J! k 00:保留。+ U% O) m/ S* z/ f* i 01:发送事件。 10:发送取消。( e. ?8 ~) n0 Y 11 :保留。" u8 q3 Z% \" s( u E1 EDL(bit21):扩展数据长度 0:标准帧格式" C7 C& e% O* w- u( M 1:FDCAN帧格式。3 B2 s5 b" c- m" |- O E1 BRS(bit20):比特率切换+ u8 d$ d- ~# l6 O 0:发送的帧没有比特率切换 1:发送的帧带有比特率切换 E1 DLC[19:16](bit):数据长度。 0-8:传统CAN+CANFD,长度为0~8字节。 9-15:长度为8字节。 E1 TXTS[15:0]bit15:0):时间戳。% f% m/ Q7 s& n: P1 C( s/ ^ ) S9 ?. L" q, { 5)、TXBC.TBSA:发送buffer,有32个元素。9 F! D/ N/ r5 Y* ?. O! _8 _8 | / H' G6 Q" x, u4 f
9 O3 }) N- g" _ T0 ESI(bit31):错误状态标志/ n& e- J' o$ T' `& z1 D9 L4 J 0:CANFD帧中取决于被动错误标志。( L0 E3 u! Q8 w! N* c( z" x 1:CANFD帧中隐形错误。2 L3 {0 W' z2 h3 a9 l$ T) m- F T0 XTD(bit30):标记是标准ID还是扩展ID 0:11位标准ID" c% C- b: s; y8 f 2 Y8 [6 E5 ?. Y' d 1:29位扩展ID, N" c' ~: M$ Z8 o2 r2 S* Y T0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。 0:发送的帧是数据帧 1:发送到的帧是遥控帧3 e" @: x9 z0 H6 r* f& M" A T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。2 L$ w# G0 X( \: A9 Z 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:发送的帧没有比特率切换/ v/ K- w* c L3 o 1:发送的帧带有比特率切换 T1 DLC[19:16](bit):数据长度。: a; Z* H& x7 |; I& V 0-8:传统CAN+CAN FD,接收帧长度为0~8字节。0 i; w6 d; G! L 9-15:传统CAN,接收帧长度为8字节。 9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。' J, K4 g- e- V5 K" _ T2-Tn:发送的数据。+ s2 g- |# D; J3 i$ K+ U 三、过滤器设置 标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式: : {& h/ T6 \+ k- K 1、指定范围过滤7 u; k% _. R0 A) u 通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:5 i; A; s0 g( l% J; ]
2、指定ID过滤6 t, h/ \$ s4 U1 Z; p. D 我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下: & K" ?5 F6 h. O5 e$ J& `! ]' i
3、传统的位过滤3 _, J' ~6 q4 y 第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下: V4 ~+ n0 X3 T$ v
其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。 四、CAN的收发过程 1 a! H( c3 \) [' j 1、CAN接收过程 ; K0 @' c2 D# M n CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。 0 ^% K. t" j9 r; c3 D 接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。% u1 o4 w4 \) X, A; L3 a. |" D. t
% s4 b# Q' C" L* x% R$ i 当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。 ! G0 f! v' H3 Q8 O1 f 5 W1 y* H& J7 W; s, O$ \
为了避免接收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中读):* [3 P0 O) d9 [# ]' c) b
1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。
- p, E& j! A! T8 M5 N5 L( e2 M [ 2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。 9 n6 b9 v' @4 ^1 Z) V9 q9 M" \ 3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同2 d$ G6 l% k5 l2 A, O4 [4 H5 e - P8 B1 C0 K( M5 | + a+ O& ]$ J! y' U, |2 I9 q) D# s7 M 如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。 如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。" m; \5 J5 ^1 X4 {( z. w; d- `' p7 G 计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:2 y! `4 ]8 |5 s2 m8 p h
2、CAN发送流程) r3 r4 ^* ]! [7 _4 [ 在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:0 N! q- c1 x6 O' o+ i 2 j8 k6 Z8 V" ?1 ?4 R' P& j& u
因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。 $ J$ ?+ M" I: Q6 G8 X6 O 下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况: , P) y' |/ W" |; N9 e3 ?4 V/ @2 B 1)、专用发送buffer 专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。 如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。
7 r7 Y8 A3 L' O5 |9 T2 P3 W6 l 专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:- A9 ?. E/ j4 d) h6 o/ m1 W! l' f 发送buffer索引 (0…31) *4+发送buffer起始地址。( k% C+ Y5 I2 I6 Z( u: T: T% T ) s! S5 |1 f6 R8 q 2 X. Z _% X7 d 2)、发送FIFO ! T" ?/ a$ e3 ~0 G( l 发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。 新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。: s' ]7 c, S: @( ~$ ~/ U$ |! H 5 P8 p7 S( M' Y/ U0 \5 j
( T% o5 [0 A k, K* v 发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。 如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。 C' h- \- c& S$ r) s( l2 l 如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。' z4 Q6 K5 ?2 ] 如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。 发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是: 放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。( b& e: C5 e( I; d- l 3)、发送队列+ A# Z+ P F/ Q 发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。% G/ {$ f) L6 {: _. [4 C* T8 @4 {/ v 新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。 应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。1 g. E" I& h, ]/ m1 t8 | 发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是: 发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。( Y' F/ S0 T2 g4 r# F W/ ?! j8 l 因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同: j" y: r3 O, } 1)、发送buffer全部配置为专用发送buffer' i6 s" `; G0 O' ]7 I' ~' B% z 每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。 8 Z7 [9 V$ a' F7 A7 P/ b k: G 2)、混合专用发送buffer和发送FIFO& M. t, S4 u7 d3 L ) |- h; H/ I: q* G$ C* m$ c 在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。8 u6 g6 Q0 o8 j# [" r
# z" I4 Q5 h7 t0 d) k" m 发送优先次序: A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区), q2 ^0 F; x5 j" L/ B/ R B、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据 % m: ]; U. Z7 l 3)、混合专用发送buffer和发送队列- x) B* J5 [+ ^ : k; Y* \, b- A0 F; O w) ^ ] 在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。 # e; |: p+ @2 S- j$ L7 h1 q$ W
发送优先级设置:. o( ~( F ]: L A、扫描所有激活了发送请求的发送缓冲区 B、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据 $ O6 ~6 [2 U9 O; t# w) I5 q1 d; q. K 再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。 FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。 发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。 发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。 n6 i; |( b6 t7 c+ I 为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。 6 U. l7 h7 C9 D) V8 J CAN发送消息的代码实现: 2 C* n! f) {% L% x4 Z# U& T 发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ: 9 p! t1 n6 ?- k1 [ x4 _
发送buffer请求寄存器FDCAN_TXBAR,描述为:8 ^" l, r; G: P ! G4 K% s: ]4 O1 U* I% H: d: H9 G 8 ^9 c: m4 V1 c) { 此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。 ]: C" ~7 a( R0 p" o9 ]! X ; D/ x- f3 k- J7 d. B1 I/ A7 n2 h 2 h! d$ R8 d8 M, f- X # y2 G5 u6 x2 Z, X 五、初始化0 u3 p6 z, }# W6 V# r, }$ ^3 \( _ 初始化可以直接使用HAL库,
9 m4 Q! U0 u( g: [; J* I 六、CAN发送示例(来自正点原子) % E5 n! C( r6 s% V
七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)( U7 w3 o/ C% _: C B
参考:《正点原子》 STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准): R. x' _ T( G3 d+ b |
【 逢7发帖赢大礼】1、利用CubeMX生成正点原子H7R7开发板的STM32CubeIDE工程
【STM32H735G-DK测评】ADC3+TIMER1+DMA 采样模拟信号
【STM32H735DK测评】Ethernet+lwip DHCP获取IP地址
STM32H743基于CubeMx5.0配置手册
【STM32H735G-DK测评】lvgl移植
【STM32H735G-DK 测评】STM32H735G-DK External Loder 编译和测试
【福利三:逢7发帖赢大礼】使用MPU6050模块、img_dsc、img_btn机制实现一些简单有趣的动画效果
【福利三:逢7发帖赢大礼】使用arc弧形拉条控件控制PWM舵机及使用SPI3接口操作NRF24L01模块进行无线通信
【福利三:逢7发帖赢大礼】调试nucleo-WBA65RI工程测试样板的一些心得
【福利三:逢7发帖赢大礼】lv_timer和BME680实现温度、湿度、气压数据读取
微信公众号
手机版