一、介绍$ d: K7 b, [6 `
% F& l0 C/ a3 ?0 v! E FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:- x4 R" a" p4 K: p% }+ V v
; p8 w+ m; g, |, @' ]9 }* O 1、每帧数据段最大长度由8字节上升到64字节。8 D. S4 s- ?3 t G# _; S& k
3 r0 X$ ]# |$ y# B: w8 e! t7 P
2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。7 Y- g6 o* Z! N F: [3 A. h2 Z: Q+ o* r
4 F9 ^' J- n9 B, L. m, a* G2 D
3、向下兼容CAN。, Y& k4 T" Q5 s4 L$ R
1 @6 o, K& I1 B0 W, [5 f H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。4 [5 w( i/ P2 @7 |7 R) g
+ E j6 ^. S4 n! c) r1 l二、结构
" I0 Y7 O$ y4 f j9 I* J l* q4 b9 Z1 U$ Y+ T: Y
先看结构框图:( z) W: }* _; `3 z& c
5 M) j2 i) u" _) K5 [6 M) d5 _9 v
2 t: d- c" L) ^0 V) Q7 L. h& h& L- {5 b6 Z, r- ~2 B
1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。
( O t0 g7 v# J6 A( ]& c
0 C& V5 g8 R$ r2 M" n. T$ Y- H( \9 _
$ c) F5 O( u% p
: P8 m" l: @. t9 o& D8 w. D 可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。+ N8 o1 w& h; U% \* d6 h% D& G% e
# I! b1 S( ?3 ~2 u
2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。
/ n4 T5 Z8 J8 c" L( ?
. t% p2 F& G4 A* E! b3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。
- W: b' \9 W. {% p+ F- A3 E2 {/ e% r* z( W/ y1 C
4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。
9 s3 x# ]4 Y5 t2 p
; H# Z! y' ~6 K5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:$ d1 o7 J9 V; N/ s) e4 x
, \* D* e3 s* [$ {5 A/ V) C2 t% `' s& P3 W3 G# |
8 ?: e8 c0 @# v7 z- H; ]
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 j: I/ Z+ }+ p
6 g* r" E0 Z+ O# ^: g7 f& C 1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。
2 R$ M& R$ ^) N. u* U
$ w& k. W0 d2 H* N" q6 [. N/ S; m; T/ ~ 2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)
( w3 I' v% H9 p7 q( |6 v( l; u; Z
' a b5 [5 E: Y s& @1 W 3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。
& E% _% F ^! L# p5 } X6 a& d x0 E4 g& ]- C
4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。5 E% d% F/ i# T! p
( \: m7 u3 g6 K1 n* @3 T/ t, H
5)、RXBC.RBSA:接收buffer,最多可以有64个元素。 c# _6 U5 P6 v* U3 l {
0 G7 }( o9 V3 r1 F6 ]$ f: m" m& Y& P 6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)9 D8 I" Z: J' V6 j* _7 Q8 O
2 E) O" O- W2 V! o g 7)、TXBC.TBSA:发送buffer,最多可以有32个元素。
; v: K* t! m t7 `8 C+ W7 {) I- f6 l3 J1 s. b3 C' ]5 h
8)、TMC.TMSA:触发存储器,最多可以有64个元素。
2 i4 @" @6 |! n" R2 D+ } c$ h. `1 |0 v2 i, K m! p
《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》
' J; Y6 p: a" Y- X7 M2 J- v
' e+ v3 Q) ]* X9 q1 n p 下面来详细介绍这几个功能区域:
" [0 g7 U5 U: I) V7 U% S. x; o! H1 b) m# F
1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:2 D9 R+ L0 F& a( o _* o$ {
; C$ X$ z: B$ S% H+ n2 @# a5 k1 `3 j' A: V8 h) Q
$ O) n+ s; _* c
SFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:/ G4 G/ v+ T' z- ]$ V) N8 p" ]
00:范围过滤,过滤到SFID1到SFID2中的所有信息。
3 c# g, s/ N ^) [! D$ d l5 i 01:双过滤,过滤到SFID1和SFID2中的信息。
& p# X) ?( P2 q. w, G5 G 10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。
) P* a6 d' J5 h- g/ J 11:禁止过滤器元素+ B& h* V# U' @9 P1 n" M/ C
SFEC[2:0](bit29:27):标准过滤配置
* m9 W4 [; S0 p8 k' y/ T 000:禁止过滤器元素
( M) Q7 ^% @/ j) T: B. j 001:当过滤匹配以后存储在Rx FIFO0中。8 _" W7 k: O4 ^3 a
010:当过滤匹配以后存储在Rx FIFO1中。
7 A0 r' a( {7 c- ~+ I. M% t 011:当过滤匹配以后丢弃。( t$ v+ A* X# Q6 g
100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。' W1 W3 Z7 [% b3 @
101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
7 p; R( R1 h8 d6 w; y* V9 @3 U% ~ 110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。8 d) @8 Y8 ` E$ Y! V& }7 @2 p
111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。
+ ^6 {* H+ e ?# I8 YSFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。0 d$ N2 N$ T* a# j: t3 x! [: z
SFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。
7 O! Z0 }9 V- g! g/ g" u7 z- Z7 w9 `5 H5 D( Q! I. `, Y: z- M
SFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。
7 E1 d1 V. d0 O9 N* [6 M6 D3 [ 00:将消息存储在Rx Buffer中。 b+ X6 S$ E/ a1 E9 r( Y6 \0 l
1 i! `- Y6 e6 M: P 01:debug消息A。 , l) C9 U3 w2 B( N% \6 W* |. J8 r
$ G% N+ G, Q7 l# O- [; h5 y 10:debug消息B。
) [$ m6 O! R, f* }6 x) e i! n% u4 c# `0 S. z9 o
11:debug消息C。- E3 [0 u3 l4 E$ U9 U* p" e
0 K5 M/ t) { y9 b" b1 [SFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。
( t3 ?6 s; u1 x$ t: S& z! z. O
5 M! @' c/ o4 KSFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。2 ~' ~9 }/ q& n! k) S
) _: e* V/ h# B- ~2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:' S0 S) E. A) y! U+ I0 Y# E
# B9 D+ { A1 e+ \. K2 ]3 _% v
7 j0 F6 @; V4 `! m: f2 `' P" g ?9 H
- s% @: @6 O1 w z7 w2 {F0 EFEC[2:0](bit31:29):扩展过滤配置; t$ g6 o' ]6 K2 N# f
000:禁止过滤器元素
) X% c! K* A; b/ D 001:当过滤匹配以后存储在Rx FIFO0中。
* s8 F' o( }3 r9 a- V5 r1 _ 010:当过滤匹配以后存储在Rx FIFO1中1 u- x/ u. h+ L+ R
011:当过滤匹配以后丢弃。
! U! X: R: O8 C6 Y+ v6 M 100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。; A/ _2 A1 b7 k8 L. q" j# Y
101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
) _2 Q F; D. r. @# T" W5 ]. t 110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
2 n. A5 G) V5 b: n9 u8 _3 c 111:存储到Rx buffer中,EFT[1:0]忽略。
$ ~+ ^3 t% q- N3 I$ lF0 EFID1[28:0](bit28:0):扩展过滤ID1。3 O. u S6 d# v, @% i9 Y
F1 EFTI[1:0](bit31:30):扩展过滤类型0 D' u$ }* I Y
00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。
& S, v2 ]9 l( Z8 y% { 01:双过滤,过滤到EF1ID和EF2ID中的信息。
8 v3 P8 }' Q' H' u0 w1 z 10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。
3 r4 @7 E& C* o7 G- ?) e6 g 11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码2 Z' c: @) G0 u5 ?4 F8 `
F1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。 q* g5 t% l. E# w
00将消息存储在Rx Buffer中。$ t. R; K6 }! U; V9 |
01:debug消息A。# d) e% m' L3 q% a+ g
10:debug消息B。
( s' n* k2 P: u1 _6 y2 ` 11:debug消息C.* e& _3 w$ h1 l2 ?( |, N J
F1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。
; h* G; B* C* W) x' K# \! T) [! IF1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。
$ [ Y+ J B7 u/ J# o9 T; @7 c5 i$ m. F* J7 W
3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:" M) ~. K0 q& R/ u) `
) n4 ]; w9 A& @3 i1 m+ G
0 v$ u, X0 w; _! i) A' ~% @
7 J6 O5 h+ @# c
R0 ESI(bit31):错误状态标志: O" J$ Y' W! n4 S9 [
0:传输节点显性错误。
8 s" p$ i' f+ n& R2 F1 }4 f# G 1:传输节点隐形错误! `2 h( S0 [: d- d& i
R0 XTD(bit30):标记是标准ID还是扩展ID7 c. u" |" [$ t) ?
0:11位标准ID. Z& H( l" v! I9 y9 C, [3 W) l
4 j& A M% R% h9 R9 y3 {; f 1:29位扩展ID
6 D5 R6 M( \+ }' Q$ H) Z9 R( v3 ~' {2 l& n& \1 A3 f6 Z+ a, S7 @" Q
R0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。3 {' W' a% L! h9 |4 P
0:接收到的帧是数据帧. Q' H: j* b( H- X# {
! z, @8 `) y1 g7 O. G7 Z9 }: H
1:接收到的帧是遥控帧
5 e5 m9 M* X+ t$ X1 dR0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
4 t& l5 ?2 L' ~! ZR1 ANMF(bit31):( Y; l1 `' V* M3 ~2 ^/ g, u5 A
- _% O& n6 w! p2 L! Y$ l. I 0:接收到的帧和FIDX中的过滤帧匹配
- O5 d3 e2 [4 ^
8 W2 T( ?2 y' y% B 1:接收到的帧和Rx滤波器中的任何元素都不匹配
3 d0 s; x8 t5 y7 H/ ER1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。
. Z/ X. w" a1 Y9 O6 Y# ?R1 FDF(bit21):帧格式4 s8 N" K5 j3 m% t2 y
0:标准帧格式
* o% T4 ?- }# z4 }8 a 1:FDCAN帧格式。3 I7 _# j: B) e6 k
R1 BRS(bit20):比特率切换, Y0 q9 { i" j/ n) z
0:接收到的帧没有比特率切换
% q$ ?7 h5 f K5 L! u2 ?
% }0 J( E7 s9 j) u. ] 1:接收到的帧带有比特率切换
C6 q+ |& }# T& }# M$ K: @R1 DLC[3:0](bit):数据长度。
3 x3 \5 T, G7 c; L8 Y; s 0-8:传统CAN+CAN FD,接收帧长度为0~8字节。
; [; d/ Z/ L/ [1 V 9-15:如果是传统CAN,接收帧长度为8字节。6 I8 w% o0 N6 ~7 d; f% l
9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。
+ l$ {7 q' L6 W! [R1 RXTS[15:0](bit15:0):接收时间戳。% i! P0 M0 N0 X# l4 p" O+ G
R2-Rn:接收到的数据。 [0 C5 D2 V, k, K W9 U1 j3 L1 E
2 @& Q% E' M9 A, W1 h g4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。" J) }% }( x+ L) c- x
$ |& J2 g; u, e3 z. B$ d% b9 v/ n8 G/ x7 [; t& A$ o$ @
- ~+ ]& L9 d$ [/ ?
E0 ESI (bit31): 错误状态标志+ P/ H1 `( @' h: B- _: l) s, N
% F& m `8 x* Q/ _ 0:传输节点显性错误
% b. z( [" Q) l# ~7 d# F 1:传输节点隐形错误。
1 p! |. }, k1 N) G8 `4 UE0 XTD(bit30):标记是标准D还是扩展ID, ~2 q3 J, _- ]. |9 z+ I7 X, Z# J$ o
0:11位标准ID.
* F. X: z" t8 M1 u# B9 _2 i 1:29位扩展ID1 d/ E) g4 I1 q$ ^( s, b, t
0 }7 y" c1 T _% a+ a. UE0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
. z8 [$ |3 y5 X7 N 0:发送的帧是数据帧4 ^# |+ `: `. p" t7 Y, _
1:发送到的帧是遥控帧
/ Q7 h0 k$ k8 s* _( gE0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。
6 ]2 y( `9 ]+ z+ A" jE1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。
1 ]; U$ x) l% \# X, e' O. w5 C. [5 rE1 EFC(bit23:22):事件类型
1 J4 ~, v* Z: C 00:保留。
" c7 z1 D- p# D 01:发送事件。
; I* }' b: p8 L+ s/ B; x, W, Y 10:发送取消。
. q, @$ X* d I2 }. n 11 :保留。
?8 I8 a0 T* H- j4 Y! h1 bE1 EDL(bit21):扩展数据长度+ X4 T3 H m# D+ M& ~2 a2 Y3 `' q; r
0:标准帧格式$ Q. [3 Z/ V# m& s( x4 \. w
1:FDCAN帧格式。
0 J1 Z6 @" c" a- ]' @4 yE1 BRS(bit20):比特率切换
% a( f" ^" p! { g 0:发送的帧没有比特率切换. ]* a. h" W' W! Q
y1 K6 q, E+ m( h) Y
1:发送的帧带有比特率切换
- [, x0 m& x$ I1 R, G% i+ K6 @E1 DLC[19:16](bit):数据长度。9 D. Q6 q& a! f" n# t7 [
0-8:传统CAN+CANFD,长度为0~8字节。
0 Y8 P" D0 e8 o+ @$ } 9-15:长度为8字节。
3 J/ F5 W% Z( BE1 TXTS[15:0]bit15:0):时间戳。
6 ?; |* n; E# l3 s" T- w/ E: A' Q
! i6 \2 M. k& [- a5 Q5 b5)、TXBC.TBSA:发送buffer,有32个元素。 [: ]5 z# u9 h) n
: ~, }. r9 W" C* U* {9 n, Y1 {. Z6 P* a. {
- `9 i8 f+ i4 k# _$ D
T0 ESI(bit31):错误状态标志( X) p/ m# Q9 J- U d% y7 V
0:CANFD帧中取决于被动错误标志。; H/ S* \, r3 f5 q& o1 u
1:CANFD帧中隐形错误。
: B1 \: t4 j) I& }/ FT0 XTD(bit30):标记是标准ID还是扩展ID) Y. t) ]8 j% X- ?6 ]* t2 b
0:11位标准ID# @; u% V9 \3 T/ X; P
- L6 f, S1 }- H: U
1:29位扩展ID- _7 u! y) m" k( C8 s
3 z/ ]7 g; s% i: S- l$ X5 KT0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。$ y- q, d6 [, r' i7 `$ i
0:发送的帧是数据帧( f. `5 F+ A! v( ^% D
1:发送到的帧是遥控帧
7 q! e' b1 g8 h+ Y- ~+ yT0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。6 V, K j9 p& ]! Y2 ]
T1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。
% i# C9 J5 x5 |( }T1 EFC(bit23):事件FIFO控制6 U: A: s, o l- z- \
0:不存储发送事件。9 M0 P( ?! p" j `
1:存储发送事件。
3 @6 D- z/ T; gT1 FDF(bit1):帧格式
/ A/ m/ B4 t) ~* r% L5 r' n2 }+ \ 0:标准帧格式
2 k: R8 o, y0 E 1:FDCAN帧格式。6 K" n+ M+ Q/ C* X4 h, [- B
T1 BRS(bit20):比特率切换+ T7 |0 J4 h+ _; E
0:发送的帧没有比特率切换# D( I) M; {- r$ j2 _
; M0 D: S5 _1 Q n3 q& T3 s 1:发送的帧带有比特率切换7 ^% s/ J7 J( n7 C R0 t5 J9 E- r k
T1 DLC[19:16](bit):数据长度。
& v. O9 w% [) ]0 e( E 0-8:传统CAN+CAN FD,接收帧长度为0~8字节。
; J1 _$ i" w4 O 9-15:传统CAN,接收帧长度为8字节。
5 f1 E5 P$ m6 A3 s, F% k. B/ D 9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。
" s7 o6 c% v* |1 O5 {' _, CT2-Tn:发送的数据。; L8 h6 {0 {+ w# s
$ V6 x+ p6 N7 _9 w/ y. `3 i5 _- `
三、过滤器设置7 n0 w' k7 k0 N. S$ q u$ [
+ t; C, L7 |3 u" g7 G
标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:
' Y5 @! C4 Z3 Z0 `5 p$ g( @& C: r! ~; Q! O6 V8 w' S# M+ F. x
1、指定范围过滤% b' e! Y$ k/ h
通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:
, J2 k- N: w( t# t" v4 e0 T4 L! _8 H" B0 s
- SIDFC.FLSSAn.SFT=0 //范围滤波
; t8 l, v, j; K, Y3 e" O. C - SDIFC.FLSSAn.SFEC=1 //如果滤波匹配成功的话将消息保存到RxFIFO中
3 A" H) F' ~1 l' }! Q* i( K, R - SDIFC.FLSSAn.SFID1=0x123 //ID12 X) Z- z. Q9 Y2 C5 O
- SDIFC.FLSSAn.SFID2=0X321 //ID2
复制代码
- o A2 G c) }" C M+ F9 b1 X 2、指定ID过滤! J- z# i. I) c0 |( W2 `" ~) M
( e" `+ Y/ y" e, ?, D
我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:
4 N3 n6 j. ?4 R+ Z- t+ P/ }8 o( f" {& h5 h3 @
- SIDFC.FLSSAn.SFT=1 //指定D过滤
0 u/ ]2 T' O; y6 Q; C - SDIFC.FLSSAn.SFEC=1 //如果滤波匹配成功的话将消息保存到Rx FIFO中
" S( O/ u1 W( a9 }8 h - SDIFC.FLSSAn.SFID1=0x123 //ID1
3 C. ?/ e$ v/ l - SDIFC.FLSSAn.SFID2=0X123 //ID2
复制代码
8 S' X P: g/ s( f7 U8 o" M' V2 B 3、传统的位过滤
+ t0 ?4 H. I5 }8 H" O 第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:3 Q* |8 z" T0 P i
% X1 X1 d& F, }9 ~6 C4 V/ v- SIDFC.FLSSAn.SFT=2 //传统位过滤
( g5 I) h# Q i. A* f - SDIFC.FLSSAn.SFEC=1 //如果滤波匹配成功的话将消息保存到Rx FIFO中
: c$ E. A5 v. Y4 R - SDIFC.FL SSAn.SFID1=0xFF00 //ID10 P" j- r4 j- ?
- SDIFC.FLSSAn.SFID2=0xF00 //掩码
复制代码 3 R4 p$ L+ d# P4 }) V
其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。* t; q N* i+ l% f
( z, R* s2 @) [/ \8 u2 e5 x四、CAN的收发过程5 E. k; d c" {0 |- m9 H
3 z7 a$ f3 L1 a/ N( o% O: [% j& A3 K
1、CAN接收过程5 @2 v! P! v2 ?3 S+ u. n( Y/ Z
0 m5 X8 K7 h4 p8 h( o5 ^9 V0 `, b
CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。
( F M" p @# S, H9 Q- A9 Z4 P+ J6 F9 O: y, l& }* B2 d; }+ O, _! b
接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。
+ z8 ^1 E1 q, G: y( x' T, e% I4 O. Y1 H9 P$ O" o. }; u" u. `( c
! L. }) u5 A8 k8 J; S
/ n! U/ j+ H+ g! H
当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。2 Z9 U; t- Y1 L
$ h$ S" R6 B0 `2 ~2 P5 j: z* S6 }
6 [5 F* P; q4 B0 p, k
$ [. {0 Z/ V- l3 g: I# ` 为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。
. f" L+ V: j+ W; V9 ?1 k% H 读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):
# ]& P( j o" L) {; F. A: Z0 \5 F' y. u
- /* Calculate Rx FIFO 0 element address */. ~4 A+ |- T" O$ v: x! n
- GetIndex = ((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8);" {3 m# k1 f: p+ W5 a6 Z
- RxAddress = (uint32_t *)(hfdcan->msgRam.RxFIFO0SA + (GetIndex * hfdcan->Init.RxFifo0ElmtSize * 4));
复制代码 % n2 a7 V/ f5 P/ b/ m* S w' E: Q6 [
1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。
6 R& b. J3 Y2 N
+ d& E" X) f8 z- _2 N$ K0 x" f; l7 O! u4 G
& H) }. y7 O# f3 s% s* M
2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。
[! E- |5 ~" c! R0 q+ }
( v; ~0 C7 C) c3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同
" ]/ j. B5 e: ?+ D/ G
; v1 J# D% k, I) [/ u6 Q3 r# P+ b0 ?/ y! @* z) E1 I9 D! N
% {' p( G9 L1 Z$ |$ y
如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。
+ Q& z5 h- ]1 W, \# ?
- |0 Y4 D/ [0 N: l- q' ~如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。
- U) D# j" W' A* w0 J- d2 p k0 Y+ N' x) p4 {: A% L: k* _% V
计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:
" {. \7 q( Q: V$ d5 ]* J/ j
6 \6 D. l3 F* Q' A- pRxHeader->IdType = *RxAddress & FDCAN_ELEMENT_MASK_XTD;2 ^' A2 ~3 x) N+ t U
- pRxHeader->Identifier = ((*RxAddress & FDCAN_ELEMENT_MASK_STDID) >> 18);6 s! U+ R2 L+ q. N8 W
- pRxHeader->RxFrameType = (*RxAddress & FDCAN_ELEMENT_MASK_RTR);- F; R8 N$ z# B. }5 b' V9 i
- pRxHeader->ErrorStateIndicator = (*RxAddress++ & FDCAN_ELEMENT_MASK_ESI); //这里地址自加了一次6 U* c: u7 E6 A3 I1 F& F+ U5 \, ~" i
- pRxHeader->RxTimestamp = (*RxAddress & FDCAN_ELEMENT_MASK_TS);9 p; j) C- o! o/ c2 d0 n$ r4 X s
- pRxHeader->DataLength = (*RxAddress & FDCAN_ELEMENT_MASK_DLC);
0 A8 ~. f9 M2 O& \& z - pRxHeader->IsFilterMatchingFrame = ((*RxAddress++ & FDCAN_ELEMENT_MASK_ANMF) >> 31); //这里地址又自加了一次
2 w8 A2 T. D7 `# C) C- o - ......
9 q& D$ f( Y' Y - pData = (uint8_t *)RxAddress;
9 n, s+ L$ H& \: Z - for(ByteCounter = 0; ByteCounter < DLCtoBytes[pRxHeader->DataLength >> 16]; ByteCounter++)0 h8 l) ]1 p5 i+ D( S3 e3 u2 K
- {
% m8 E/ U% y1 V' C/ H - *pRxData++ = *pData++;! n; I3 u/ _9 L H
- }
复制代码 " @% U, D6 E0 F5 g/ o( e2 O- {# b( ~
2、CAN发送流程
! B. P# B2 m+ A& Y: i0 y0 Y
9 v% y; E+ _5 h* x0 t 在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:
" Q$ z- ~6 |# Q$ I( i/ @. H5 d$ [+ V# V4 h. n/ ?4 t
4 t( @. p7 {' D8 {
# b4 b; Y6 N0 s 因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。2 @; R$ w+ o* q, W9 ]1 W' b! l
* _8 [6 C b7 s3 n" V 下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:
% g5 P, a. T/ c% z& J1 @3 j! m* Y, ~" c& Q' |0 |% \) P
1)、专用发送buffer
+ Y6 P: ^0 _& }, F3 V
# [. o# X4 L% N3 @$ x* J 专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。) z) g" _+ G5 _ ]1 |9 Y
如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。
* k+ b. p* u2 k/ I) r$ T7 d1 B. L# ] X! f$ r, k2 g
- ?' O _6 d" u1 M
- M8 G# s. B3 u {# A5 r5 _$ D! \
专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:; {6 n- F: H0 T; x# F J
1 Z- P/ R, C% r3 R* ^' p发送buffer索引 (0…31) *4+发送buffer起始地址。- b- v, ]" m: U0 E4 [6 C1 \8 r
# r) W, G8 R) Z
' I* U) ]6 q. c& L c
$ v1 |8 l$ t, h 2)、发送FIFO* D: z7 ^" o+ z+ o- z2 C2 B7 ^
. h8 N1 c8 t* U8 l4 C8 u 发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。
6 G' Y2 |" H7 |4 u" j5 h 新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。
5 `4 l' V& k: I7 b/ `1 k7 z
6 \! z: Q r9 F, @1 J' s0 u4 ~9 `( M# E- {; {1 u
8 y' y M m( j) P i8 ~8 s4 D H
发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。
" k9 j& B' w; U2 \ ~/ h7 g6 D8 L. K! r( o9 f( d9 l/ g2 e
如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。
3 `, i6 x- k( H3 _1 ~. B1 v 如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。6 q+ X- T" {3 t" r* }% S2 E
如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。
0 }+ }: x! B) a 发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:3 a& l9 y h" u L/ f
/ p! t+ v) U8 V7 h7 m+ m0 p! a
放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。/ S% i/ o: e, t- G# ~' o/ e; R" V% p0 n& e
( K) S; h* X8 r5 L 3)、发送队列
/ E$ X$ l6 x+ m2 m- S- m- m 发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。# o% u( ?5 w( {. ^; n
新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。# f4 L# V _) H+ r
应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。- u! |, g6 {7 z! W" e1 e2 p ?
发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是: S. W) t& w5 G) w
r& G0 y* k! X6 w6 {
发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。* [, f2 M" Z/ w! f, B5 ~
5 D% k- F6 w) @
因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:2 h; A; a8 m; S/ k8 B# [1 f
/ S7 X3 [ t/ x9 x6 ]- j; O 1)、发送buffer全部配置为专用发送buffer
' S! V- o( r& [
4 T# l& W2 w) a. n; T2 {8 P 每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。
. {& B; ]( _/ |- d9 a; V7 b9 P" a. }9 _0 `# y
2)、混合专用发送buffer和发送FIFO* S3 a6 h! t) c! E
0 i u8 V- r( |
在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。
. }! j* d4 L2 n$ H% ^ I! c# ?
5 G0 h, Y: U& e1 s) T# `0 S8 \
- p: G1 g, O+ c1 N# p3 d! g' a6 r$ W: n
# a# B4 i: B( ?, m# f/ u' o$ n 发送优先次序:1 M. f. l& @9 p. M! z
A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)$ K, T6 b& k$ Y" t! {% m
B、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据. l" h) V$ t0 F
3 Z/ Z9 m8 v9 u( v 3)、混合专用发送buffer和发送队列8 w0 w; i6 ?2 b" c
& |% ~5 l! G: w u2 r 在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。
5 H$ v3 [# @& i. ~% d$ H1 |1 f T9 t1 r1 {
, A' i: G6 b- B' ^9 E
" \- }! j- j1 w3 \6 k 发送优先级设置:
6 O" @3 k5 j- m/ H3 m x; EA、扫描所有激活了发送请求的发送缓冲区4 i: Q$ }3 J' W, t* H: B4 y
B、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据. K" T; t& u x! Y1 f1 Z [
/ e6 x. j8 [6 [+ ^& W
再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。; w! Q3 l( M& s# N; `+ j1 g o R$ \
* R3 h, l2 H. {, l9 L7 P* A FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。3 n' u( O& Q! Z E8 g2 F; C0 }. y
发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。
; L) _9 ~5 v) C% p2 k 发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。
8 G9 S" ]1 i% p! J9 ^7 c) q
" Q$ j; }4 e+ v% N% J3 C* E 为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。
( g) z* X. b" W9 z$ E9 N& x3 a; i+ A. n
CAN发送消息的代码实现:
2 n* [- v9 E% n. Y" ^ ]2 D' [( w. J2 Y
发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:
5 ]( R& N- @" W4 D2 K3 Z6 z) U2 U& S' F' j
- PutIndex = ((hfdcan->Instance->TXFQS & FDCAN_TXFQS_TFQPI) >> 16); //获取元素编号
8 r' x! K" `" [- J - FDCAN_CopyMessageToRAM(hfdcan, pTxHeader, pTxData, PutIndex); //复制消息到Tx buffer
6 U! v) H3 H/ c0 }3 ^ - hfdcan->Instance->TXBAR = (1 << PutIndex); //发出传输请求
复制代码
- N) k1 R; \! X$ h 发送buffer请求寄存器FDCAN_TXBAR,描述为:% {9 D% c4 V; J. v6 L/ u
n3 {: p0 I( }4 S9 Q
K8 M3 V9 {2 l$ R! K2 w: r& q
0 }* M& j) J7 m% `6 ?: @1 h 此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。
" J- t, d& D. K" }/ t& Q% E+ S% b/ S+ s, C
# [7 G5 G/ ^; P0 Z
0 s/ N7 E" \$ h7 O" p! V; |0 f五、初始化& F9 c' m/ A( v4 g0 o" N% u
: k, Q$ \- o4 T( t: i 初始化可以直接使用HAL库,6 D& l( l4 g5 Z; x$ L
. w8 A; m, O) ]; j- u8 FDCAN1_Mode_Init(void)) N6 Y7 l1 p- `! T. S
- {
0 v% m) i3 b8 z) H, S' a - FDCAN_FilterTypeDef FDCAN1_RXFilter;
' l" Y! m1 I3 y( P - ! k" C& D3 O- j$ }/ J4 Q
- HAL_FDCAN_DeInit(&FDCAN1_Handler); //先清除以前的设置
- h2 ^& V( F: r% p - FDCAN1_Handler.Instance=FDCAN1; j7 |# n9 m2 d; i1 t
- FDCAN1_Handler.Init.FrameFormat=FDCAN_FRAME_CLASSIC; //传统模式- h! m! w' {* T# b0 Y0 M! c
- FDCAN1_Handler.Init.Mode=FDCAN_MODE_NORMAL; //正常模式, t( e% Y" ~+ |3 e' Y6 z, m5 H
- FDCAN1_Handler.Init.AutoRetransmission=DISABLE; //关闭自动重传: ^$ m$ g4 k) g2 x
- FDCAN1_Handler.Init.TransmitPause=DISABLE; //关闭传输暂停
/ F# e: O: h3 s$ Y - FDCAN1_Handler.Init.ProtocolException=DISABLE; //关闭协议异常处理: q F0 i: {' A4 R! e+ f
- * d4 s& Q: L! f5 L3 S5 B) \* J: l
- //时钟为200M,baudrate=200M/(NominalTimeSeg1+NominalTimeSeg2+1)/NominalPrescaler,这里配置为1M
y' @& W$ B& C - FDCAN1_Handler.Init.NominalPrescaler=10; //分频系数( O X, w/ d# E; |
- FDCAN1_Handler.Init.NominalSyncJumpWidth=8; //重新同步跳跃宽度, [+ i& C3 u; m
- FDCAN1_Handler.Init.NominalTimeSeg1=11; //tsg1范围:2~256
4 T6 J( b- L8 w2 w5 ? - FDCAN1_Handler.Init.NominalTimeSeg2=8; //tsg2范围:2~128* Q; ? O7 l7 l# U N9 m
- , A H! _# m* X
- FDCAN1_Handler.Init.MessageRAMOffset=0; //信息RAM偏移,10KB消息RAM共有2560字,故可以偏移0~25609 H% s6 H% s/ Q+ {* H" [
- //使用了多少个滤波器就要设置为多少% n; S; [% y$ [0 e1 i# Y5 {
- FDCAN1_Handler.Init.StdFiltersNbr=3; //标准帧滤波器个数,0~128
M0 b4 ~4 ^9 G- z" w: ^3 O - FDCAN1_Handler.Init.ExtFiltersNbr=2; //扩展帧滤波器个数,0~64& Q7 q( x* V/ f& r. n ]
- ! a! T" a+ p% a% O8 P% ~
- //接收FIFO0、FIFO1和buffer配置,此处没有使用FIFO1故个数设置为0- S. V0 N% Y$ x3 B7 f8 v0 ?% o
- FDCAN1_Handler.Init.RxFifo0ElmtsNbr=64; //设置接收FIFO0元素个数,0-64
2 b8 G% i: a( P- c5 w - FDCAN1_Handler.Init.RxFifo0ElmtSize=FDCAN_DATA_BYTES_8; //接收FIFO0元素的数据域大小:8字节
+ M+ r! Z0 ]6 M- @ - FDCAN1_Handler.Init.RxFifo1ElmtsNbr=0; //设置接收FIFO1元素个数,0-64
3 n) l0 }9 o4 ^+ R- n& |# { - FDCAN1_Handler.Init.RxFifo1ElmtSize=FDCAN_DATA_BYTES_8; //接收FIFO1元素的数据域大小:8字节
7 X* Z0 A. }& J7 g - FDCAN1_Handler.Init.RxBuffersNbr=64; //接收buffer元素个数,0~64, c9 T4 _! s- e4 J* h4 w
- FDCAN1_Handler.Init.RxBufferSize=FDCAN_DATA_BYTES_8; //接收buffer元素的数据域大小:8字节 5 }$ ^4 O0 o! Z; ]/ K/ ?; H, m
- $ ]2 [3 f/ q& }9 A& i B
- //没有使用发送事件FIFO功能,故TxEventsNbr设置为0。把发送buffer全部作为专用发送buffer使用,故TxFifoQueueElmtsNbr设为0.9 }+ V1 ?7 K( E L* `
- FDCAN1_Handler.Init.TxEventsNbr=0; //发送事件FIFO元素个数,0~32 L7 Q! a z# ?3 P& o' Y% t
- FDCAN1_Handler.Init.TxBuffersNbr=32; //发送buffer元素个数,0~32. m! v5 z9 E3 U X& A' U
- FDCAN1_Handler.Init.TxFifoQueueElmtsNbr=0; //发送Buffer被用作发送FIFO/队列的元素个数,0~32: X+ A3 f) U# u3 v
- FDCAN1_Handler.Init.TxFifoQueueMode=FDCAN_TX_FIFO_OPERATION; //发送FIFO模式选择,可以选择FIFO模式或队列模式' o7 h+ D1 C' q$ ]
- FDCAN1_Handler.Init.TxElmtSize=FDCAN_DATA_BYTES_8; //发送元素的数据域大小:8字节. E) o% P6 A* o4 X. P* a2 A
- 0 R/ F }6 N6 K( j: S% D( w
- if(HAL_FDCAN_Init(&FDCAN1_Handler)!=HAL_OK) return 1; //初始化FDCAN- I+ }. Z6 e f/ o( Z0 R! a y
( E8 ^; V3 ]* @% S5 r" K( E2 h- //配置RX滤波器,标准帧
* b5 S" k3 g0 Z - FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID; //标准ID' Q! j/ E) J, Y
- FDCAN1_RXFilter.FilterIndex=0; //滤波器索引 0 p. ~, ~( ?# \( O
- FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK; //滤波器类型
2 ]$ R W. s4 g; f b; f" [$ T; i7 e - FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0; //过滤器0关联到FIFO0 6 m- T0 r/ ]; ^" v2 ]$ ?* _
- FDCAN1_RXFilter.FilterID1=0x112; //11位ID1 l2 Z3 Q# `1 X, g* B8 `
- FDCAN1_RXFilter.FilterID2=0x7FF; //11位掩码: h5 D& c4 W; X2 u, D
- if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化% a7 a$ b6 L# _$ A. l% H, ^
- 7 V$ N1 \$ ]* S! x3 @' l
- FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID; //标准ID' T, n8 {2 R( N! f. D- ]9 f- l* z
- FDCAN1_RXFilter.FilterIndex=1; //滤波器索引 2 @. {2 k7 Y' p
- FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK; //滤波器类型/ Z6 R% F, _, p) `) Z
- FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0; //过滤器0关联到FIFO0
2 @& k+ S- ?3 o( ] - FDCAN1_RXFilter.FilterID1=0x113; //11位ID( w7 A( b& [( m7 ?$ n: U" _
- FDCAN1_RXFilter.FilterID2=0x7FF; //11位掩码' [+ j# l; Q; c% i: ^% |$ L
- if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
0 g3 {1 b- A! N0 v6 | - $ j" @9 }" @/ D, V
- FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID; //标准ID! s4 z! X O. k
- FDCAN1_RXFilter.FilterIndex=2; //滤波器索引
9 u* o% P2 V4 P2 b1 q* L) U - FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK; //滤波器类型
+ o w8 I- V. J( R - FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0; //过滤器0关联到FIFO0 ( K2 P% g& N2 z! b, B# q$ i4 O
- FDCAN1_RXFilter.FilterID1=0x114; //11位ID
1 G: I w( X) w. Z7 K - FDCAN1_RXFilter.FilterID2=0x7FF; //11位掩码
; Q' f/ I7 I" c$ j" O, @, u3 U1 \ - if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
7 i6 z8 {6 N5 \9 H( ^4 V - . u" P' b8 f, R( ^( P, r' ]
- //配置RX滤波器,扩展帧。标准帧和扩展帧的滤波器索引是分开的
/ V+ B4 U. q1 Y5 E) _ - FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID; //扩展ID" a. K- P. w9 {' z% u; g4 i/ L
- FDCAN1_RXFilter.FilterIndex=0; //滤波器索引 % m8 U4 \ \9 r1 p
- FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK; //滤波器类型2 T4 {1 [5 |# t- F* L8 z
- FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0; //过滤器0关联到FIFO0
, w2 E6 v7 d$ P- J+ O; a2 y - FDCAN1_RXFilter.FilterID1=(1 << 20)|(2 << 12); //32位ID1 |0 g/ Y c1 ^, p4 s0 q+ m0 P/ G* s
- FDCAN1_RXFilter.FilterID2=0x1FFFF000; //32位掩码8 {- b9 ]* g% P: d$ m( m
- if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化* F2 ?! [7 h$ U; o; D- g
- " y, _, b( `& `
- FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID; //扩展ID2 L, I( X& p6 j7 n" ~3 `# P
- FDCAN1_RXFilter.FilterIndex=1; //滤波器索引 6 K! m$ @6 l# _1 v& {" Y' d
- FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK; //滤波器类型
u; x# w. Z# }/ [# W' m - FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0; //过滤器0关联到FIFO0 ^5 [$ i0 B7 p3 }) W! d' u [
- FDCAN1_RXFilter.FilterID1=(1 << 20)|(3 << 12); //32位ID/ S0 _* w3 P% M7 Y/ O
- FDCAN1_RXFilter.FilterID2=0x1FFFF000; //32位掩码
8 h3 s1 M% }2 g5 [/ |: l2 o8 B - if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2; //滤波器初始化
: L# ?5 m) J; s, o; T2 |* ? - $ u# v; I( d* v! Q+ T
- //滤除的消息直接丢弃
& m: M7 P4 H/ O/ y0 n - HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler,FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE); //设置被滤除掉的消息的处理方式
6 P! d. H& j( B2 f -
+ W- U# Q$ e6 ^0 t& m - HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0); //使能新消息接收中断- B9 M V7 M9 _# u' A! T/ g
- HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_TX_COMPLETE,0xffffffff); //使能消息发送中断,0xffffffff表示所有的发送buffer都触发中断 # O9 e9 w X. q, C7 h% d0 H. W0 {8 M
- 6 U3 R2 @9 @- y# O1 W% L0 b
- HAL_FDCAN_Start(&FDCAN1_Handler); //开启FDCAN/ \7 Z9 W" E( u5 i; L* p6 V
- return 0;
3 I, |+ C+ {+ E6 ?1 a9 |! } - }
复制代码 1 k" @+ f; x2 m5 W
/ F; O" V: s' D7 s# B; ?
六、CAN发送示例(来自正点原子)$ y) V; d% K, i& k* g
j% W9 `$ q2 V% t. |9 ]- //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
( s$ T0 E6 p& ^: r' D, H7 c - //len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8 ; `& y3 e4 c6 X5 p
- //msg:数据指针,最大为8个字节./ ` G* C s: \+ ~' U
- //返回值:0,成功;# D9 d& b h" G% @" Q
- // 其他,失败;
4 Y) X, T& T( p8 {/ q& e& b& G - u8 FDCAN1_Send_Msg(u8* msg,u32 len)+ F% M- A m, }4 ]0 ^0 a0 ~
- {
' I$ K" W5 ~) y5 A: {2 b0 A) _ - FDCAN1_TxHeader.Identifier=0x12; //32位ID- Y# {" U- x' x0 Z6 K# M
- FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID; //标准ID3 A0 e( v: R5 y
- FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME; //数据帧
5 E& S7 Q5 [: ? - FDCAN1_TxHeader.DataLength=len; //数据长度" X3 z+ j5 B, L1 {2 n/ Y2 K4 |! y6 P
- FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE; & _- Q8 b; b u6 Z# D9 O
- FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF; //关闭速率切换8 F( f! ~' t; K; U1 t9 w
- FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN; //传统的CAN模式; D- `# X- u c# O/ F6 O
- FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS; //无发送事件4 K; V9 Q9 \. W/ A! P& U4 z
- FDCAN1_TxHeader.MessageMarker=0; / C* i/ @# ]- a
- 7 w! u+ B- y {& n( Y& x
- if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送3 Q5 `+ i6 G, t% o) Z0 M! k
- return 0;
# w( F- S( _( @ - }
复制代码
3 z6 o7 q; _- c; c" u七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)4 _+ n/ k8 F; g6 Y" O
# _, y, m1 D6 g' O' M; x- //can口接收数据查询2 k5 k& w3 ?& U D6 F
- //buf:数据缓存区; ! H7 N K1 X' ~8 P, R; F: J/ y
- //返回值:0,无数据被收到;
6 r& H0 z6 w/ T- g - // 其他,接收的数据长度;" @9 g2 O1 `$ D' v u3 W
- u8 FDCAN1_Receive_Msg(u8 *buf)5 t5 Q1 J/ }' q& C4 W& O5 C
- {
. }% t4 U0 h) D y# J) X0 Z - if(HAL_FDCAN_GetRxMessage(&FDCAN1_Handler,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf)!=HAL_OK)return 0;//接收数据
: c6 I& O- M; a( J - return FDCAN1_RxHeader.DataLength>>16;
7 d2 Z2 B5 @7 T - }
复制代码
4 j. X9 | R4 w- D+ s$ g6 l参考:《正点原子》 STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准)5 G/ M9 Q: [. d) l: k! Q d
Z& ~6 ], L0 y& B3 l
. C5 W; A+ {% o, ` |