你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32H7的FDCAN

[复制链接]
STMCU小助手 发布时间:2021-12-22 14:00
一、介绍
1 {# y6 }# W: B/ d; ]: @$ s2 v" b& ^
4 D' I" x3 J" H# N4 k* V" f        FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:* D, q( w2 z: K2 I$ u5 a
' u7 E/ {9 _# J8 |" C! [
       1、每帧数据段最大长度由8字节上升到64字节。4 {3 @. ^. a2 I! Z

: r3 C( U# Q) }7 E! W        2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。: z7 [6 J: W# D& T, V

4 A" ~4 L9 R: h: B' I9 l        3、向下兼容CAN。6 ^; G( R% R  ?
' U& g3 O0 I1 t' ~# K
        H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。
# t' ]0 S" d/ [! q* _4 B0 l" A/ b) n1 a& j
二、结构
1 @% @" ?; l( a6 Z6 Y. @" h: a" d3 l! t4 X& D4 Q) y$ V
        先看结构框图:
9 p) h$ }. Q% n3 H+ z1 E% c+ d, V8 F
- r$ y7 z" c  O1 @/ ~% b6 v
20191121162856859.png

8 M; b! O- h4 `+ [9 Z& X$ ?9 {' ]3 G1 ^& E9 w' [
1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。/ w. O6 t, s4 M
4 r$ E- N. S: S/ Q: R9 C
20191127102816991.png

" P( L3 h8 P' z8 `" h( Q" ]" b& g! }
        可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。9 ~6 T3 Z5 |9 [) j$ E8 P/ h  P
8 }7 |  _5 ]3 ]
2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。- f: U4 R4 E# G6 {9 X: x' p8 d

4 a  n2 s) L3 `3 ]7 B7 q7 e3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。% e- ]6 A, G( }/ J- I0 F+ C

$ V6 u/ Z1 m- q1 `, b$ o$ L( g4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。$ G/ a9 ^) ?' q6 x  u, r
6 q4 ~. E% I1 J
5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:! p8 N: T; d2 T  C0 R

' t3 S' d. I- L3 b
20191121164211703.png

% U: t3 x: P! Z; L) k- ?
! r* u# c, q7 d. o        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字。
  G- ?# ]# P, {* Z8 j' v6 E
! J" l1 h( S8 q1 j8 D! C3 R$ z! P        1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。" U( S& p' U7 e7 ^. W% ?! k& P
2 A2 ~2 Z5 H1 g6 W/ J; z
        2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)/ X+ }- d7 x; ?7 m. K4 }
! S& @& I7 T, w) g. `% D
        3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。
4 j8 T9 r8 v7 U7 u& s6 r
( j! I+ k' g. ?/ [        4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。
" Z, o" {3 u# W2 p6 N
7 I: X- _! {' k. d        5)、RXBC.RBSA:接收buffer,最多可以有64个元素。0 P! |$ w+ R9 P6 m+ M5 q

9 \9 A$ g+ g# N- a4 b# f' \        6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)
8 m' S9 r4 {# c6 c2 M. ?
: p2 y" ?6 E( o. m$ W5 D        7)、TXBC.TBSA:发送buffer,最多可以有32个元素。1 T: ?0 u' P) c6 W. T0 v
4 v. x- ^0 E6 [, y2 Q4 L" R
        8)、TMC.TMSA:触发存储器,最多可以有64个元素。
8 e9 }2 V1 ]6 ?/ g4 }3 v, f) ]7 e" K1 u7 ~* U/ z. K  B" I* W( P
《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》
5 |% q0 P% I0 E6 T& c' d
- \1 p, J7 h+ u# Y+ a1 j: |        下面来详细介绍这几个功能区域:8 t* x, m1 e' V8 g. T5 H. ^+ A
8 w, B$ j$ z" Y
1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:9 C. M$ D- [3 w  \3 B

7 L$ O3 P4 f' c8 r! |/ a/ j  s
20191121170625565.png

3 Q9 Z' ^' ]7 Q) c# F
  b0 W+ N+ K9 E. n7 k. t3 x1 QSFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:
3 Y- v( A. w7 w" _) f: V4 e    00:范围过滤,过滤到SFID1到SFID2中的所有信息。
" R& r, y3 P$ a* j    01:双过滤,过滤到SFID1和SFID2中的信息。4 w& u. _" C5 s! q! g3 J$ A
    10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。8 C" q+ J4 _; T2 G
    11:禁止过滤器元素% J, G7 F  o7 W) Z4 d+ U- y
SFEC[2:0](bit29:27):标准过滤配置" {6 X, _1 h; }
    000:禁止过滤器元素
% k# c& h/ K0 _    001:当过滤匹配以后存储在Rx FIFO0中。( o$ r! v7 F* _4 ?; ]
    010:当过滤匹配以后存储在Rx FIFO1中。
7 P$ F7 q) N0 E/ _, P    011:当过滤匹配以后丢弃。1 P9 c9 d# b9 T) s, \
    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。# w# v# T' n0 ^
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
4 e% ?% V4 K3 w# V3 \    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。' o: x" l2 b& Z+ L9 M
    111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。9 c- v) A1 {! i6 A2 {2 h5 o1 M
SFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。0 c8 H2 Z# {) Y9 a6 i/ U
SFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。
3 }$ F7 \9 V) I
: _& D: e! {; Z. QSFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。: W0 P* x* W1 U
    00:将消息存储在Rx Buffer中。( W* s( C  {- ?/ }; Q) c( A; W1 h
# D( t7 E5 |# A7 u, Z6 Q7 N$ o# G* F3 ]
    01:debug消息A。    ! m* ^, G' a, l; n+ E$ S) J

. w' C" B* i% H) \1 s" K! k4 B    10:debug消息B。
3 ~* }# l8 O' @2 L3 ?9 O
: {# N+ X4 Q. [; A( S    11:debug消息C。5 p4 J! [2 b1 o) |+ ~% e
# w; L# l8 D7 F; M' P7 R* F) V
SFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。
* c$ E) t# Y$ J$ A& I  h% D. s
' v" F8 f3 P. i" ?8 L* LSFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。& Q# W3 l. n6 i- z. Y
6 A3 L( S* J1 U
2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:: P: {; n1 a) H5 J/ ~

) q" v/ W; l# G# N% t
20191121171836710.png
; H" j  O; q" R8 Y: k! ]% Q9 P

1 O3 C4 t( f2 `$ BF0 EFEC[2:0](bit31:29):扩展过滤配置
) l$ P, e& y8 ?5 d    000:禁止过滤器元素. A. s) G1 ]& e9 Y) m% f6 D8 {
    001:当过滤匹配以后存储在Rx FIFO0中。5 l( s2 A) N4 b7 W2 ], Q( M
    010:当过滤匹配以后存储在Rx FIFO1中
6 g) i7 v1 j0 [    011:当过滤匹配以后丢弃。
" \, n) q4 v+ v& B9 C! d  j$ j! o    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。
1 M* V: x- _% O4 O# V    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
5 M4 S2 j8 a4 p    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。7 W3 _' u; c% Y! H1 A# r9 h
    111:存储到Rx buffer中,EFT[1:0]忽略。
, z0 ~8 B0 y0 {0 M9 nF0 EFID1[28:0](bit28:0):扩展过滤ID1。8 G9 W, g; O+ |6 |4 c$ {
F1 EFTI[1:0](bit31:30):扩展过滤类型9 `; S+ ~. S6 ~$ V
    00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。
. a9 ]; a; z. v* Q2 N    01:双过滤,过滤到EF1ID和EF2ID中的信息。  ]& c1 `4 q( T8 s) ]) k
    10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。
2 w/ l! m( f  Q- n" W+ r5 x7 @    11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码! d0 I! g6 f) m5 }, b, J% s5 G, m. B
F1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。9 b& v( X* V3 o' p6 I5 k0 ^
    00将消息存储在Rx Buffer中。1 M: b! i6 F: n4 T( n
    01:debug消息A。$ g7 c4 e3 e9 A! _& E
    10:debug消息B。# q6 ]/ S, Y0 v5 ?* x+ x
    11:debug消息C.3 {5 C" h, M- ^
F1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。1 Y+ [5 A* T) [  s9 X" o, _
F1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。
2 l; X8 p$ Q% i- l3 p3 \
& N6 }# s3 Q/ Z" H: s1 h0 O3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:
$ I  v1 {  V, T# B) K  }: M* L! x3 g' z" c. q
20191121172451477.png
  N% Z9 Q! A* T; N- L
; p& G; s1 h; |0 ~3 v% d# q
R0 ESI(bit31):错误状态标志5 R9 d, W8 b% f# L# n+ u8 U
    0:传输节点显性错误。9 O7 q; d4 h( g6 E
    1:传输节点隐形错误) R( B- V  q& E. S
R0 XTD(bit30):标记是标准ID还是扩展ID! m! U! `+ [# L7 b
    0:11位标准ID0 y! e: r( \% S3 T8 u2 x6 k# X+ P2 w2 n2 |
& `" T4 H# ?; q  X
    1:29位扩展ID
4 j, d/ d2 ^9 @. ]$ c5 L% Q, [0 f) |: ?: z! N: B: o
R0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。. [  P7 J3 l6 J- R% ?& z; v0 c
    0:接收到的帧是数据帧
0 Q% o2 f$ Z3 u2 R  k* b6 @$ z. c; y; ^8 H8 V6 u1 r: \5 _4 x
    1:接收到的帧是遥控帧! p: R9 i4 c. }) Y* |1 Z
R0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
+ P3 _, Q/ I- ^+ Y( [, J7 nR1 ANMF(bit31):
6 V6 R+ `& c8 Q' L# E) I# r7 B4 C) L" Z8 q" G* {
    0:接收到的帧和FIDX中的过滤帧匹配
4 m. o1 ?: D/ H0 ^, N
( ^- D. _: D. w1 }" l2 m# ^    1:接收到的帧和Rx滤波器中的任何元素都不匹配/ [1 Q. W2 c+ z* V0 @+ z+ O
R1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。+ s1 o  {8 O- F. v4 |: j- i
R1 FDF(bit21):帧格式
1 S' d5 J1 F& t0 b7 g, K( b, u0 `$ U# K    0:标准帧格式
0 a: m% y4 P6 B- w! C' o    1:FDCAN帧格式。
7 x& x4 q5 N1 \# N! N2 nR1 BRS(bit20):比特率切换
( B6 {/ J  C/ C* j  Z) |* c" b    0:接收到的帧没有比特率切换
6 k1 p& @- |" ^* T0 [" J3 u" ~
0 {1 R. g$ u! G4 c7 Z$ l    1:接收到的帧带有比特率切换1 M  T/ S5 D& p: l
R1 DLC[3:0](bit):数据长度。" r8 |& j( `% |6 V) Z
    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。
7 R, C$ R; Q# l7 r$ c; Q! U    9-15:如果是传统CAN,接收帧长度为8字节。7 K( r1 b! U* A' f  N6 L' \  ]2 q: H
    9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。, y: D! v, m: _4 Z7 g/ w% i) n
R1 RXTS[15:0](bit15:0):接收时间戳。7 D) G% J4 N1 D/ T
R2-Rn:接收到的数据。
9 |4 h5 t9 \3 }0 Q, s1 m- i4 J* ]7 H) K5 ]! p
4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。, N* ]. U& ~+ W+ e& |

8 z9 Z* C& P1 s  h
20191121174147402.png

2 S! j. t! ?$ P$ Y7 G* |( [( }# D1 U$ K" R- l) c+ y: l" O: v$ }
E0 ESI (bit31): 错误状态标志
( \6 a3 [, L# p# }+ C' W3 V. V* e/ s- U  N: F0 ^: h% t' }
    0:传输节点显性错误
3 y" l2 ?8 w- x    1:传输节点隐形错误。2 |1 l5 d* Z4 @
E0 XTD(bit30):标记是标准D还是扩展ID
- d& T2 a! \8 @! [/ N7 A  m    0:11位标准ID.* k: O3 F& u& D) N
    1:29位扩展ID6 g% p9 {) |+ Z/ T2 W

+ @# t/ a) e$ A4 E  SE0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
: [$ ]2 P5 g  E! g: b    0:发送的帧是数据帧
4 Y4 b7 d5 z# D    1:发送到的帧是遥控帧
- O" q$ ~" j( D, p% B) D+ D) XE0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。" m" u9 T2 c; ?0 s6 ]7 ]  N
E1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。
# b, R, Q2 Z. F$ n9 H; W/ x0 |E1 EFC(bit23:22):事件类型
' i5 l1 V$ F4 F' ?8 R    00:保留。
6 U0 B# }0 @$ a2 l    01:发送事件。8 v& _1 l; R3 u. W- z
    10:发送取消。- Q/ l2 W  w. Y# E# {
    11 :保留。
4 o& r" r2 ?* M9 h- p9 H7 Z& XE1 EDL(bit21):扩展数据长度
! v5 Y+ R. I4 z9 U! i" U    0:标准帧格式
2 v2 [; W3 U+ x2 l; X* r3 k- J$ X    1:FDCAN帧格式。
! s' ]4 Z+ I7 ^" TE1 BRS(bit20):比特率切换
2 R( X+ B; ]+ m2 R7 P  E% q    0:发送的帧没有比特率切换4 t# M$ T0 l- v: g5 [. Y

" C% k5 d, K$ R* p% C    1:发送的帧带有比特率切换
( c2 J% u" D" V$ Q$ lE1 DLC[19:16](bit):数据长度。! O9 p( o- M. z6 d: E/ c' \+ o
    0-8:传统CAN+CANFD,长度为0~8字节。
2 G9 M* K: m7 d    9-15:长度为8字节。" R- T# O  n- d+ R
E1 TXTS[15:0]bit15:0):时间戳。1 L: ^7 t" g) `' h" ^
! H4 g; u1 M6 R" L7 `
5)、TXBC.TBSA:发送buffer,有32个元素。
6 T9 E9 Y9 B3 @7 R5 M$ N4 |
4 y+ c0 D! I$ Q, z' O+ |
20191121175121493.png

) p5 m; Y) [" r& E( g% w- Z
% Q3 [9 T0 f% U' X7 W; r8 {T0 ESI(bit31):错误状态标志
! H* |: ^. R+ j7 g* a    0:CANFD帧中取决于被动错误标志。
) |$ G# |, H9 v    1:CANFD帧中隐形错误。
. Y9 j/ {/ x2 U3 w- MT0 XTD(bit30):标记是标准ID还是扩展ID
7 S8 L; D. z  _3 _  n: B    0:11位标准ID
$ y3 A2 Q+ z! L8 P9 S
9 S8 e7 O( W* ?& M: R    1:29位扩展ID  @8 D+ d/ u7 N# G, q" D

/ g0 {6 P4 o/ G3 `T0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
# Q& U* w* t3 G' L( w6 u    0:发送的帧是数据帧
5 z* \% m: ]2 l  o    1:发送到的帧是遥控帧
8 a. q% v: h, s' n# H+ T+ s/ ~T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
1 l2 v/ K5 ?( f( ET1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。
" V( c& v/ V# l5 |* n/ x, FT1 EFC(bit23):事件FIFO控制" `, i9 c% w( r- K  B* V
    0:不存储发送事件。
# N9 C# f5 ^4 B% Y. o5 J    1:存储发送事件。" G4 ~' O9 s4 l1 f, @8 }* T& n
T1 FDF(bit1):帧格式
7 d( O$ ?( `6 f# T    0:标准帧格式
, S6 @3 x6 Z2 J2 L9 v    1:FDCAN帧格式。) \0 N: q5 l& U2 f
T1 BRS(bit20):比特率切换
0 H- M6 D1 w0 z6 Q$ e    0:发送的帧没有比特率切换9 _, a/ w2 H: [

, F! w3 l7 {4 s& _1 q7 h    1:发送的帧带有比特率切换+ H; o/ ?5 B$ n; P
T1 DLC[19:16](bit):数据长度。4 p. A7 f0 m( n, B
    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。( B  \' l. d' }7 |3 E6 |
    9-15:传统CAN,接收帧长度为8字节。' s( u7 o- ]  L+ r
    9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。
' M; I! X. l* O: RT2-Tn:发送的数据。
" \0 Q7 x7 l1 o/ h1 r6 ~/ t- L
# f& v) g7 y0 s; w+ u! X2 }* P) R三、过滤器设置2 W6 v' N6 v8 d+ y# f4 o& i

/ P8 u6 l9 l4 C2 X& L        标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:; t% \/ Q% y4 F  D; q0 p7 J

$ R1 |* Q3 J9 ]# R        1、指定范围过滤
! [3 W5 Z: w' |        通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:5 p, H' G7 t3 l! E5 z7 ~  Q
/ z8 U  W: B! F
  1. SIDFC.FLSSAn.SFT=0        //范围滤波
    7 `  ]: u4 {; O( y
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到RxFIFO中- \7 G* e# T- A8 c8 ]3 H
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
      n5 Q" ^5 g; B8 u. Q
  4. SDIFC.FLSSAn.SFID2=0X321  //ID2
复制代码
, y2 W1 V" K: O7 Q
        2、指定ID过滤
# E4 L2 K. }" ^4 F6 i4 h1 Q5 z' H  D+ \
        我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:
* }1 C" O% @% C$ r1 \) W
- }1 |" F6 {0 Q* c5 T3 U8 V& q+ G$ b
  1. SIDFC.FLSSAn.SFT=1        //指定D过滤  C9 ]( ^" V! ^5 ^9 J$ e# m/ J
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到Rx FIFO中- b, i: |9 U0 K
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    5 z. p& _3 l* b; ^1 }: C& C8 p) _( K
  4. SDIFC.FLSSAn.SFID2=0X123  //ID2
复制代码

2 Q$ y7 s  X5 _; k        3、传统的位过滤+ ~2 ^7 d: x2 ]% D
        第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:5 x; M% m* v3 `

/ ~1 ]8 n6 |9 j7 ?
  1. SIDFC.FLSSAn.SFT=2          //传统位过滤( F, _$ a6 p9 y/ S0 I3 Z
  2. SDIFC.FLSSAn.SFEC=1         //如果滤波匹配成功的话将消息保存到Rx FIFO中
      t4 M, h6 H4 D" l2 r9 Q5 |
  3. SDIFC.FL SSAn.SFID1=0xFF00  //ID15 v) @/ B* P6 E" R2 S/ j$ p
  4. SDIFC.FLSSAn.SFID2=0xF00    //掩码
复制代码

+ T* p1 c5 D% [' |/ h* E        其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。  D% _! ^  O7 ^" ?
0 D; [8 d. y# I; K# c
四、CAN的收发过程
4 ^: p, i. H) x2 p9 ]7 Z. q, K) T
/ H# ], T# v% U( S1 m* s, x$ ~+ j% @- O        1、CAN接收过程
, D; ~' j- w" v. Y! [$ L7 e) x% V% p% F: B9 ~5 Z* N
        CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。3 {5 s- Q* N& C  F# W; q% g
! ?. \6 T, V  Q( P
        接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。
( {! H. x, P( U( W* e# l1 A) f! [; ~. Q5 \1 U) O3 y/ m- }- \
20191127114550965.png

) ]0 o, E1 O4 L
3 M/ p! ~/ d3 m) A        当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。- u' E% v9 y% L3 o

7 y. w' k5 D$ Z) D0 m- T4 c
- [. e( s2 S9 T8 N9 j* I8 F' J7 J
20191127111235302.png
" \' o" t. s( e, _( q3 O( p
* }0 j; k5 E5 n- a  B
        为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。( J. O4 U# ]! d1 l
        读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):
2 z3 \% ?% A  q) v  q1 T/ L  W8 k: X9 z
+ s! D( i0 O% A- }
  1. /* Calculate Rx FIFO 0 element address */
    5 J. d" `7 Q7 N! @) u# n
  2. GetIndex = ((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8);0 j/ D0 F- q/ A+ b+ r, G! q
  3. RxAddress = (uint32_t *)(hfdcan->msgRam.RxFIFO0SA + (GetIndex * hfdcan->Init.RxFifo0ElmtSize * 4));
复制代码
8 F% q3 N3 J" V
1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。
2 m6 ?# S4 b' G5 Q$ G
+ L" @8 i8 }7 E# h& Q( ]+ k
2019112119095892.png
" a# E3 u6 s* G5 M. J
( p, @$ c! ?$ m' T7 K
2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。
, t& A% P1 C6 u, I7 L; e( E/ G4 f8 n. S% a3 z5 C5 G0 j. e/ a* q; V
3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同# D7 B5 D) ~6 A) n. z& k

+ g* W+ h" Q0 j2 ^  K" p! x
) F$ J  @2 g: m8 [2 k$ l8 d

  w: S0 ~. O9 g2 _3 ~3 O如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。
5 H3 M$ E' E! x0 y& O1 U8 r! W5 G! \* v( n3 v9 R9 t& e
如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。  V0 H0 W. I# q9 |

, _5 `/ q* d, M# B, F( i        计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:+ [1 w9 S$ a5 U) [: F1 Y

9 ^0 K, f, y6 K. r
  1. pRxHeader->IdType = *RxAddress & FDCAN_ELEMENT_MASK_XTD;6 F7 [8 @* V, j, k# |: O6 T0 B
  2. pRxHeader->Identifier = ((*RxAddress & FDCAN_ELEMENT_MASK_STDID) >> 18);! O9 u2 M7 Z" L- W* ?  P
  3. pRxHeader->RxFrameType = (*RxAddress & FDCAN_ELEMENT_MASK_RTR);
    - ?! f# Z8 Z5 _" g, T
  4. pRxHeader->ErrorStateIndicator = (*RxAddress++ & FDCAN_ELEMENT_MASK_ESI);  //这里地址自加了一次
    * Q2 Y! A$ d+ R/ P
  5. pRxHeader->RxTimestamp = (*RxAddress & FDCAN_ELEMENT_MASK_TS);) }* ?; f0 A2 d
  6. pRxHeader->DataLength = (*RxAddress & FDCAN_ELEMENT_MASK_DLC);
    * B( t: X0 R  g
  7. pRxHeader->IsFilterMatchingFrame = ((*RxAddress++ & FDCAN_ELEMENT_MASK_ANMF) >> 31); //这里地址又自加了一次: g. B, A& i. d5 W( R5 `4 s
  8. ......
    * l8 n6 g" v. K4 k; @
  9. pData = (uint8_t *)RxAddress;* @- q* n2 a  R$ T7 j3 ~# i! c* H
  10. for(ByteCounter = 0; ByteCounter < DLCtoBytes[pRxHeader->DataLength >> 16]; ByteCounter++)" c0 U' Q% h$ a& }8 i
  11. {
    $ V4 k5 f: I  w) X
  12.     *pRxData++ = *pData++;6 ?6 N& S9 Q( o' ^( S& N
  13. }
复制代码

9 |9 o6 a, w3 W7 f        2、CAN发送流程- N  N0 R' f# N3 E* S+ ?+ ^
# E, V4 i6 q& I1 d3 P
        在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:4 B  r% G! l/ B8 Z" ^2 U8 d7 Q
8 t6 s7 g9 K2 Q5 \9 E* I
20191127150042199.png

, i3 Z6 V. J3 W9 l- R, ~3 X( d. g9 n' @' {0 M( v7 D
        因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。( p6 p: G; {2 E, y% G. F' ]& b1 ?
$ h( @/ P% s. q) W4 p
        下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:
8 b$ X; Y6 I9 W0 I' E/ m& H+ H- P  C8 h! W
        1)、专用发送buffer
2 g8 e7 h4 x3 [% x; J
* p2 }- y/ e% {' n& ~) z         专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。
* Q0 r( E# u9 a2 X+ W$ J        如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。
6 u0 c  T- ~/ v* u. a! {5 Y5 m7 L  }& d
20191122101555794.png

4 |3 k3 D0 _. e4 a$ |
( K/ |9 W- O* w6 {- e        专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:
. I! [1 T( j0 |' }
4 E9 @. v6 E8 U" C6 s% W发送buffer索引 (0…31) *4+发送buffer起始地址。
* V, ~7 U/ Q1 k, Z2 s- \( j. ^; V: G1 L- K2 y. g* g* B+ q( a
9 @6 [' J3 A: A

$ M& |8 W0 D6 `  a+ d3 b# d( S, y        2)、发送FIFO" K  e3 \) \) I$ v

) t9 C: h1 ~2 ~, g/ Q$ z7 O+ {7 N        发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。
$ f* o7 z/ `; B; t        新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。
, d) a" |) q) r
" E' C+ I3 H; A$ h: b
20191127160804244.png
$ J- [2 {- k2 t
* P4 |" f/ \8 {
        发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。0 J& ?! H5 p$ h( b
9 X  P% K( F  w0 B  T% q4 T( s8 E
        如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。
5 `2 w* G# a, {& g( p) R+ d        如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。4 |$ K2 r. R* \
        如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。
! z. t1 H9 ~; w7 F        发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:9 p4 f. h  P. Y/ G* ~; ^& Z

, f6 c2 Y2 B5 s1 W. E: [9 O放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。% |- [5 k9 x7 O9 a/ K7 @
* u* Y" h5 U: F  N1 E1 f
        3)、发送队列
* q/ E( X$ W" L        发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。
7 _( l( F  s+ `. A4 J& j0 V: @        新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。
( r- v, ~1 y6 @! w" b! l" Y( N        应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。
' z+ Q/ L, P* D% ^& R  x7 j        发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是:$ j* Z6 ~% Y2 B( ^/ I# B
; T6 J" b. V7 V5 P) C
发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。7 j- h. V" u3 [# N) a, z
" Y7 Q% Z# d. L/ T/ e
        因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:
9 K+ {5 _% `4 U% x# _! J( a* H5 s$ _3 q9 A7 g
        1)、发送buffer全部配置为专用发送buffer$ o" t! ?4 n/ Z$ z
/ L7 r0 \7 Y) j- W0 X8 L! R
        每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。
7 ^0 O; p0 a1 {+ B. h8 d
8 n! S0 ?3 y0 n  a. m) R: |        2)、混合专用发送buffer和发送FIFO4 b3 D& S5 ~- R
$ g8 q" @3 _  D, k
        在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。) V/ [3 B9 h' l: g; X8 h( S/ S9 ?7 Z* c

( `2 x4 ~4 g% V) ?
20191127162943182.png

" p7 X$ l2 G3 G) {3 a! b
: v2 w" q3 V" z- j        发送优先次序:
$ [" m. [. G9 o7 U8 lA、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)2 `9 O" B1 n# x
B、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据
! @1 S( P+ h8 r4 z0 E! c! ]
! Z" G* `4 M6 R1 I$ Z3 h$ t' q* h/ n        3)、混合专用发送buffer和发送队列
6 c3 Z  m) _& ^. L+ \2 {' T9 v0 Z9 \, e
        在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。- y- e" D) G2 }( @# G* @
9 [8 }, ?- p2 v$ \
20191127163202212.png

" ?$ g+ v; C& z% ~3 V
( M, w& u2 P. O+ n        发送优先级设置:$ c7 Y, y- n7 H+ Z: u" v
A、扫描所有激活了发送请求的发送缓冲区: R* Y& W7 S9 A2 J6 Y& t, i9 v
B、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据2 S( y* D, @, N  W& V" s5 R4 p
, e+ l8 w: K# p! I' k0 q6 I
        再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。
8 ^7 P; b4 H: Z; ^
% Q. }- Q6 f/ d  S" u        FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。
, }4 L) d! g& a        发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。
. l- q. [7 M) K) \+ N6 T5 @$ L        发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。# u8 Z4 p7 N& X
( ?$ @' D2 ?2 w7 P
        为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。% M& w6 [! a5 v% q  s# K8 B) H
2 D  L8 d  {: _$ A  X' T
        CAN发送消息的代码实现:1 i5 E) }4 P+ \* G0 V
) u" ]" N# G$ M$ X. Z6 K# x- K
        发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:/ E  s5 }1 q' [. F
4 M0 s% M* y# o) G  q; g9 u2 ^
  1. PutIndex = ((hfdcan->Instance->TXFQS & FDCAN_TXFQS_TFQPI) >> 16); //获取元素编号
    : s" ~+ }1 l& ], e/ o
  2. FDCAN_CopyMessageToRAM(hfdcan, pTxHeader, pTxData, PutIndex); //复制消息到Tx buffer
    0 O7 t+ T$ M+ ~- V! M
  3. hfdcan->Instance->TXBAR = (1 << PutIndex); //发出传输请求
复制代码

9 F4 G8 b8 }" Z5 t2 X7 _0 @6 m* G        发送buffer请求寄存器FDCAN_TXBAR,描述为:
% `  T+ ~5 V6 C3 K: P8 H
7 z& D# W3 m& K& F$ M- e3 ?! E
9 v! n7 q  D1 h( D* n9 t
! q. D4 b$ Y9 c" |1 U! e1 j
        此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。
. L  P2 b. R# {5 h& t
" o; ], }. e, ~) g5 x
$ Y+ ]: P; k! N) i" `: u
% A+ _+ R0 o3 l0 J五、初始化( X8 z( R& w+ D, h9 s
0 X, |! Y+ E7 o: |
        初始化可以直接使用HAL库,: H$ X3 N! d  n8 T( j: q' c

2 ]7 E2 y4 E4 R; ^0 r
  1. u8 FDCAN1_Mode_Init(void)
    # T# S4 `8 F( K% S
  2. {
    7 E) k4 w8 t% F4 N  g, @% T1 O
  3.     FDCAN_FilterTypeDef FDCAN1_RXFilter;
    2 e. p" ?6 l: n! k# u  b
  4. ; u1 A7 B3 S% J
  5.     HAL_FDCAN_DeInit(&FDCAN1_Handler);                              //先清除以前的设置
    ( e4 `% t% p% a( V* {
  6.     FDCAN1_Handler.Instance=FDCAN1;0 c6 Z0 O$ R$ M2 N5 C, F
  7.     FDCAN1_Handler.Init.FrameFormat=FDCAN_FRAME_CLASSIC;            //传统模式/ J) G1 P. D3 m* w. F/ ~* O
  8.     FDCAN1_Handler.Init.Mode=FDCAN_MODE_NORMAL;                     //正常模式
    ! g* Y+ J1 G1 Q9 t: f
  9.     FDCAN1_Handler.Init.AutoRetransmission=DISABLE;                 //关闭自动重传* L1 }1 f5 P7 a0 B, V/ b& J/ u
  10.     FDCAN1_Handler.Init.TransmitPause=DISABLE;                      //关闭传输暂停           / {6 [$ t6 v: O9 Z3 U+ B
  11.     FDCAN1_Handler.Init.ProtocolException=DISABLE;                  //关闭协议异常处理
      e4 w7 V8 o# f) V# ?8 w+ w
  12.         
    3 {, O& {5 t5 U5 q
  13.         //时钟为200M,baudrate=200M/(NominalTimeSeg1+NominalTimeSeg2+1)/NominalPrescaler,这里配置为1M8 S% Z7 |- a" ?* C: B& |2 i
  14.     FDCAN1_Handler.Init.NominalPrescaler=10;                        //分频系数; n  e) V) l. X
  15.     FDCAN1_Handler.Init.NominalSyncJumpWidth=8;                     //重新同步跳跃宽度1 Y# C$ q: d5 e4 K4 U8 w4 w6 \
  16.     FDCAN1_Handler.Init.NominalTimeSeg1=11;                         //tsg1范围:2~256
      q" D% z8 |4 n: D
  17.     FDCAN1_Handler.Init.NominalTimeSeg2=8;                          //tsg2范围:2~128
    9 n+ t! H" m  E2 T
  18.         
    & W$ O( I3 m9 ]0 V1 U6 R. |  x
  19.     FDCAN1_Handler.Init.MessageRAMOffset=0;                         //信息RAM偏移,10KB消息RAM共有2560字,故可以偏移0~25607 C6 [6 Y6 U6 q( H+ i
  20.         //使用了多少个滤波器就要设置为多少
    : c9 J4 u7 ~- p% _6 S  v- W5 {
  21.     FDCAN1_Handler.Init.StdFiltersNbr=3;                            //标准帧滤波器个数,0~128
    . q; n# }5 |# V
  22.     FDCAN1_Handler.Init.ExtFiltersNbr=2;                            //扩展帧滤波器个数,0~64
    % R. h( H( ^2 I7 f- P' V  \
  23.         
    1 B% f' K  h. _2 B& _! E8 ^
  24.         //接收FIFO0、FIFO1和buffer配置,此处没有使用FIFO1故个数设置为0) `6 K$ H  a# I
  25.     FDCAN1_Handler.Init.RxFifo0ElmtsNbr=64;                         //设置接收FIFO0元素个数,0-643 f0 D  e! y3 n, k( Y% g' ^# G  F
  26.     FDCAN1_Handler.Init.RxFifo0ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO0元素的数据域大小:8字节        
    - I% M  }& ]3 C, ]) u$ M
  27.     FDCAN1_Handler.Init.RxFifo1ElmtsNbr=0;                          //设置接收FIFO1元素个数,0-64
    % I) e1 j7 N: E! R
  28.     FDCAN1_Handler.Init.RxFifo1ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO1元素的数据域大小:8字节               
    6 S; W; ?; Y$ _1 \4 y3 z1 ~
  29.     FDCAN1_Handler.Init.RxBuffersNbr=64;                            //接收buffer元素个数,0~64
    9 t1 m5 v9 P4 e
  30.         FDCAN1_Handler.Init.RxBufferSize=FDCAN_DATA_BYTES_8;            //接收buffer元素的数据域大小:8字节        6 Z  N7 }% V7 ?1 A$ G
  31.         . g, Y+ z8 g1 z# A
  32.         //没有使用发送事件FIFO功能,故TxEventsNbr设置为0。把发送buffer全部作为专用发送buffer使用,故TxFifoQueueElmtsNbr设为0., T% z; s- \+ ?' T' z, c9 L
  33.     FDCAN1_Handler.Init.TxEventsNbr=0;                              //发送事件FIFO元素个数,0~32) [5 f. L7 d: k( p1 s( ]" f! J- ]! M
  34.     FDCAN1_Handler.Init.TxBuffersNbr=32;                            //发送buffer元素个数,0~32
    9 w3 ?: W: W! d$ h5 @
  35.     FDCAN1_Handler.Init.TxFifoQueueElmtsNbr=0;                      //发送Buffer被用作发送FIFO/队列的元素个数,0~32
    2 |2 Q) ]' x# M& S) Q% d2 g$ N
  36.     FDCAN1_Handler.Init.TxFifoQueueMode=FDCAN_TX_FIFO_OPERATION;    //发送FIFO模式选择,可以选择FIFO模式或队列模式7 N6 }9 p) |) t. b8 k
  37.     FDCAN1_Handler.Init.TxElmtSize=FDCAN_DATA_BYTES_8;              //发送元素的数据域大小:8字节& U9 {- l0 q3 i: b3 y1 [7 u. m
  38.         : b3 c: _5 F; `" Q. u
  39.     if(HAL_FDCAN_Init(&FDCAN1_Handler)!=HAL_OK) return 1;          //初始化FDCAN
    : \& e/ ~- n* `9 \7 \" ]6 G# s
  40. ; [/ }" H3 F6 }3 K* V* j
  41.     //配置RX滤波器,标准帧   
    ( E( e  Y) j+ @2 ^( ]
  42.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID: h! D4 b. `4 N, b+ C  g2 @
  43.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                   ! V/ g0 B# V& D: y1 V$ w- p
  44.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    . x  |: [3 s' k1 e+ s7 Z. [
  45.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    ) A. x4 t3 z8 l5 {# M
  46.     FDCAN1_RXFilter.FilterID1=0x112;                                //11位ID
      j2 Q( W# B& g0 _) b1 I( B' ~. e
  47.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    : M' [0 A5 Y6 x* W& H4 |9 [4 W
  48.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    " W+ ]" C- B$ v/ T7 \( S

  49. . n. X; U( _& ?0 }$ j
  50.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    2 h8 I0 F7 J. V3 ~
  51.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                  
    0 G: A8 H& y8 D4 y( `+ t
  52.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    % H  s8 D& G% W" f) d, Y
  53.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    2 e$ U$ N! E% U
  54.     FDCAN1_RXFilter.FilterID1=0x113;                                //11位ID0 P% n' M' }+ w" ^5 d( K, J5 ?. h
  55.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    ( Z( e1 b1 u5 v1 ^& g) i
  56.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    $ `3 h- n/ ^1 B6 p$ w/ P( P; \
  57.         : z. T: J) V" I
  58.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    3 D3 S6 [% W2 J/ w
  59.     FDCAN1_RXFilter.FilterIndex=2;                                  //滤波器索引                  
    . ]+ @4 Y7 D' c( J+ |8 w: o
  60.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型" j; {) u; o! u/ Z
  61.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    ) d5 F/ u% z+ i7 l! v$ h; x- M% a8 c
  62.     FDCAN1_RXFilter.FilterID1=0x114;                                //11位ID, }& I% o% S$ m1 \7 x" l" }' a
  63.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码" S5 h2 x0 f6 S9 L% N: a
  64.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化  r+ v$ r9 m* q- H4 n+ \2 g
  65.         4 o" E+ z% r+ w7 i& u2 N. v
  66.         //配置RX滤波器,扩展帧。标准帧和扩展帧的滤波器索引是分开的   8 ?: D" ~7 z! n% h( L
  67.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID
    6 S9 x" t. G% `* {
  68.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
    " X% z: d3 _; Z. _, y( Y, m5 g
  69.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    % s3 F/ g" h3 V" d
  70.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    5 Q- E2 r, y# m+ r+ E0 u
  71.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(2 << 12);                  //32位ID
    3 h$ A# C& M0 U' ?. O
  72.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码3 C: @# E# Q0 v' Z/ v
  73.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化3 n, m% M% s  ~4 t' C8 j

  74. 0 |6 p5 }9 o1 h6 u
  75.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID8 `4 l$ j0 @0 C# R8 H2 W/ ?
  76.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                     c0 j$ p' j, r) z" B
  77.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    : S7 G3 s, p0 _* V$ W# F  t/ i
  78.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  * f% j1 c( n0 C, z9 M
  79.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(3 << 12);                  //32位ID0 ?6 ^( g5 o- ^8 N
  80.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码8 G2 E1 {5 N" M  _) Q9 E7 e
  81.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;    //滤波器初始化9 N, B9 O( u% C5 `8 L
  82.         ; X- _6 ?4 w1 v5 w$ \* q: Z! D
  83.         //滤除的消息直接丢弃0 k8 y" W5 R% T! @# B
  84.         HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler,FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);  //设置被滤除掉的消息的处理方式/ ]6 ]) B9 P6 u( a( Q
  85.         " D9 n9 j' F0 t9 ~. S
  86.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);   //使能新消息接收中断
    $ e/ N) u& ~; A9 y  H, U. ]' t" C. }
  87.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_TX_COMPLETE,0xffffffff);   //使能消息发送中断,0xffffffff表示所有的发送buffer都触发中断        ' |, k4 V, E5 ^  @* z
  88.         & ~3 Z  f4 I$ b
  89.     HAL_FDCAN_Start(&FDCAN1_Handler);                               //开启FDCAN; B* a  @( N, F6 ?. h7 s' C
  90.     return 0;7 T2 m6 ]! g+ W0 Z$ L6 X8 F
  91. }
复制代码

9 q# [3 Z( ?# Q3 e: t* o- v
7 D; K, o  [: l+ j& _( p六、CAN发送示例(来自正点原子)& R, S# B! _1 o4 D0 J8 T1 s

7 |' Y; |8 e5 J& v" J' g  g
  1. //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)        3 q3 Z% c+ H  n% D
  2. //len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8                                    
    5 f+ i4 o+ |4 `
  3. //msg:数据指针,最大为8个字节.
    . C& p/ p. c: o7 s1 I
  4. //返回值:0,成功;
    . A7 `5 ?9 j; M& \9 s: W: o2 J0 a# X
  5. //                 其他,失败;
    . F& k# C2 R# {
  6. u8 FDCAN1_Send_Msg(u8* msg,u32 len)
    ( w6 l4 C; X1 `+ Q) X8 Y' `
  7. {        
    & S# ?9 I3 A, g  |+ i( Z$ L) p
  8.     FDCAN1_TxHeader.Identifier=0x12;                           //32位ID7 z5 W  U# q. h( d5 t) \7 _
  9.     FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID;                  //标准ID7 p; W7 |, [$ n0 r; O
  10.     FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME;              //数据帧& y  }! h5 h9 l& L8 i3 b: p
  11.     FDCAN1_TxHeader.DataLength=len;                            //数据长度2 J# p( I/ E/ i& U5 y; r
  12.     FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;            
    * S( J+ N$ @- r" z5 f
  13.     FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF;               //关闭速率切换
    ) D! N* g& x6 ^2 W; U' O' E
  14.     FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN;                //传统的CAN模式6 A2 |% T; X. E( O- J2 g2 R
  15.     FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS;     //无发送事件
    % S4 u' ]( M* v9 o: z3 `
  16.     FDCAN1_TxHeader.MessageMarker=0;                           ' e6 n" T9 D" O1 V4 @6 p) c, \
  17. 7 P5 x  m! @" k. m
  18.     if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送
    ' z0 d8 a2 N( ^# y" k* J7 k
  19.     return 0;        
    3 K! ^$ D4 d7 X: N
  20. }
复制代码
: B) J( @: c: v$ w7 J
七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)
2 X( u  m; ^7 r. g; f. r* o
' O) F+ x: z0 r: [
  1. //can口接收数据查询
    1 z% K  [' `% [6 k
  2. //buf:数据缓存区;         
    , S. k( K2 O& f- F
  3. //返回值:0,无数据被收到;
    9 d! m: e+ R: x. |% ~
  4. //                 其他,接收的数据长度;9 @9 z( Z$ h9 z. x4 L( j
  5. u8 FDCAN1_Receive_Msg(u8 *buf)/ `- l6 i2 L; d3 a8 T
  6. {        
    3 R4 Z& D* h' }' x: x9 ~- r
  7.     if(HAL_FDCAN_GetRxMessage(&FDCAN1_Handler,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf)!=HAL_OK)return 0;//接收数据
    6 s  Q7 U- Z3 N: Y3 @
  8.         return FDCAN1_RxHeader.DataLength>>16;        
    . [. z& F0 x8 E4 {7 \
  9. }
复制代码

/ e. }1 p, z4 {3 d" t, M参考:《正点原子》  STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准)6 X; y  V( t* i: I+ `% ]: d
3 Y. u9 T  f9 t

- f9 B+ E' ~/ k8 Y( Q
收藏 评论0 发布时间:2021-12-22 14:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版