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