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

【经验分享】STM32H7的FDCAN

[复制链接]
STMCU小助手 发布时间:2021-12-22 14:00
一、介绍" t3 P$ L! X$ Y0 ]7 f8 a' P, b
2 F/ E  P( Y$ U
        FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:, F3 J/ j( V, h& z4 j  L5 A4 Y, X
6 I* B9 s) J+ K0 d$ w
       1、每帧数据段最大长度由8字节上升到64字节。! l& x4 }% B2 \) ]% {) k, d
$ e' m) @8 R( q) \3 L
        2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。
4 s( M2 `/ R* m  A/ q4 w9 s8 G% x9 O# ?3 D7 [/ T" Q6 S& v6 k. |# l
        3、向下兼容CAN。
: z+ x& G3 [) ]4 d5 ]" [# _: ]2 q. t$ P. ?% r
        H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。: X4 P4 q9 g6 P( y

/ E: |0 P4 i/ s2 d# [4 ]* o" {二、结构
5 @- h/ O& e* w* E' X: e- b5 ?$ f1 L5 V' J, A  ~
        先看结构框图:) [: J4 l5 k* Z, E

" }4 q9 g; G  m* W8 }/ o
20191121162856859.png

8 n: q. _7 O- ^% o! m% e  |7 N, T: C' Y: K- W
1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。
& O* q! _& v# ]
5 g: g! U8 }1 X: z$ q& H+ S+ Q
20191127102816991.png

- ~# m$ s3 R+ f' i1 D5 b6 X) }9 Z7 [1 P" J* V7 w- q6 I: o7 ?
        可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。, X% ]& ^: H- {1 a

0 x8 i4 A8 {  q+ ?% z/ _+ a! a2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。
1 f1 ?9 ]' c9 N1 P/ h; p" D* g3 K
1 l( U8 a$ v; O/ r3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。7 k8 K% ]0 w" ~. b
  q( f" U+ k- e3 t8 M& l" o
4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。# {- t& Z" ~" U

1 u/ e  j9 d% t% K) x3 j6 y5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:6 T0 c0 C" |, ^

6 Q; s* ]) y0 {7 t- Z
20191121164211703.png
/ I' b6 B1 N( a( D
; _: r1 R6 ^" ~
        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字。( N" z* }; Q7 J0 k  _
. l% g& ~: E8 k' O4 ~
        1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。3 D& b( h/ [+ i! E

& S) b9 ~( w1 M# n2 `        2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)  U/ M; E8 |1 b5 S- b: V
+ }" b. ^+ j# _4 ^
        3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。
: X# w5 g4 \/ E- z, \; ?
1 K9 d" f, [, u2 J        4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。
, C# T, @- z9 E8 @! R
, [- l6 \  u$ f# q3 F8 m; A& h        5)、RXBC.RBSA:接收buffer,最多可以有64个元素。! T$ [& A$ B& o& ^; N$ x
2 y  e" D+ Q) L- D
        6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)) q5 g. `# D0 F, c. Y# b& T1 B
4 f7 j8 T# p$ J* ~
        7)、TXBC.TBSA:发送buffer,最多可以有32个元素。6 V/ H& I! ]; f( H9 _0 b- H

) r& @# U" W) F" K0 J, n        8)、TMC.TMSA:触发存储器,最多可以有64个元素。- h+ T6 }' w! M$ n% y1 R$ r* N

) n6 G  }; p7 X! I) y《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》/ r5 d; p# z7 W# w
) O' ?% g" {0 a& R6 \; Z
        下面来详细介绍这几个功能区域:* I! T) e7 c& v# r% V
. `* U- s! E1 n
1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:
6 H1 j4 h% I) |+ d* V' r8 B/ L* ?" j+ A9 a' @
20191121170625565.png

2 B) x  q+ H& k' C& ]( p1 y( X9 u  p. j9 W
SFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:
( p5 b: k. L* q: T! \' j    00:范围过滤,过滤到SFID1到SFID2中的所有信息。' R  M! V& C6 F/ I
    01:双过滤,过滤到SFID1和SFID2中的信息。3 }, e5 Q* B+ M. A  R! S+ v' h
    10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。
& l1 a" u1 U; H! v( ?6 ~    11:禁止过滤器元素
2 K% M5 n* q: Q0 B( QSFEC[2:0](bit29:27):标准过滤配置
$ l1 p! Z" w- t4 _+ K7 X    000:禁止过滤器元素2 ?& |+ a9 w# y# }, g2 T# h! L% z
    001:当过滤匹配以后存储在Rx FIFO0中。1 f7 Z+ Q4 K$ }; }
    010:当过滤匹配以后存储在Rx FIFO1中。
! }6 v6 t4 T- s0 T. U    011:当过滤匹配以后丢弃。
5 w  t- N; Y& E& \8 h    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。
1 s) K6 B3 ]2 D% H# r    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。) M4 m2 X# y" G, l; c, G
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
9 U, k; r8 @! ^! t    111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。
' g4 a$ Q# @& u9 n; \SFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。
! u8 i' A. `" l3 OSFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。
" y3 Z- Z( b' o0 j
' O4 m; D0 k2 U- `) F! \" [$ c- USFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。
, X' g% J, t' _7 {    00:将消息存储在Rx Buffer中。+ c( D- p% x/ Y! }$ O3 G; I

+ g! f, J! l+ V4 o+ w; ^    01:debug消息A。    " t. |/ u" Z4 ^& D

. @9 ^0 x' y& W; p/ v7 o3 y    10:debug消息B。! s0 O) K1 A( P6 V
: f: c8 f. k# n+ j: ?3 E
    11:debug消息C。, a. B) Z& m0 l* w

2 ~+ k, V- Y* n8 j5 [6 Q* U; rSFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。! r! _; [" Y4 b, s& v' Z. T
1 ?& q- d; ]* B7 N* a
SFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。" L- P: e1 z, r# _7 m3 @3 \

7 V2 z& {  `% L7 K2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:( i8 C9 _& ~/ t; q3 I2 ]0 v

* U8 E3 [4 j$ w) \6 h7 n+ b
20191121171836710.png

, T3 N2 [; D# Z2 q
6 ~6 ^% n( U% R; PF0 EFEC[2:0](bit31:29):扩展过滤配置' }* b) }9 U" @" N
    000:禁止过滤器元素* Q2 H! @: c& j  G3 K
    001:当过滤匹配以后存储在Rx FIFO0中。* @8 s4 }/ v6 E
    010:当过滤匹配以后存储在Rx FIFO1中' U# g2 R" C6 n
    011:当过滤匹配以后丢弃。
* ^0 r1 S) @5 |% q1 z4 ^" ^    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。
! _9 z/ t* O0 h. }    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。: V8 O# Q/ [9 _! T' u8 B3 S
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。/ u9 B9 Y% e: V5 R9 f
    111:存储到Rx buffer中,EFT[1:0]忽略。" N0 _  e" C2 Y9 h+ i3 H3 }+ O$ }
F0 EFID1[28:0](bit28:0):扩展过滤ID1。9 ]$ W1 Y" g3 c2 o1 i
F1 EFTI[1:0](bit31:30):扩展过滤类型; S8 o9 t  [/ z4 u
    00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。0 k& ?3 }3 }7 I& E) N
    01:双过滤,过滤到EF1ID和EF2ID中的信息。
! b% C; o8 c5 @/ b8 q/ w+ K1 q3 p    10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。
0 }; h5 t  V, t; x( Q/ ?    11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码
- p8 R, m6 l' z& j/ lF1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。6 \& S- _# T$ k8 C! y
    00将消息存储在Rx Buffer中。, Z: {# h! ~$ K* T3 }' F
    01:debug消息A。3 u3 a# N+ ~' A5 }$ g
    10:debug消息B。
, V7 B. ~: e) ?& _6 j' b* Z    11:debug消息C.
- L* \+ u: d* {# QF1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。
: {1 R; a8 J' P9 eF1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。( ~' ?( ]4 d8 F" T; d* G3 a

/ L# P6 N% M" o) `) J8 \6 V- W) i3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:
( @; D4 u9 C. t& c. E. R. u  }1 w) r* G; f1 P2 \# Q1 U( g  A9 {! ~$ n
20191121172451477.png

( X$ B6 y9 h4 V; D2 h& g; a2 n! f" B% q( s* x
R0 ESI(bit31):错误状态标志
0 W/ m/ H7 K7 y" ?: r4 u    0:传输节点显性错误。
' p: `- H' |* }2 C    1:传输节点隐形错误: w" @6 d7 X' D, T( ]& e* V' h1 t
R0 XTD(bit30):标记是标准ID还是扩展ID
( ^  S; b2 t9 S9 b% l6 c2 y  `* A    0:11位标准ID
4 Y7 u: \! h, I$ L: I
1 Y, L* {; A+ ?0 z* Z    1:29位扩展ID8 d7 z; E. x, d

: U. [- {, {" W7 C& MR0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。0 p% T( k: c- [1 V
    0:接收到的帧是数据帧% {& Q, r1 N, e5 h& ^' B

' _, t5 ~) F4 B/ S    1:接收到的帧是遥控帧
% ~) T6 j0 ]7 x& S8 XR0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
* N) M5 f- z) `8 NR1 ANMF(bit31):
6 o9 e  j+ l+ Q# ^
8 o: }: q; q$ ~5 t    0:接收到的帧和FIDX中的过滤帧匹配4 V1 f- z  h, ?: D4 U
) f" @4 o8 `6 O: G  P
    1:接收到的帧和Rx滤波器中的任何元素都不匹配; @% I2 Z1 d$ C, x1 C% G
R1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。0 U5 l/ j  ?7 _; @9 n0 {; U
R1 FDF(bit21):帧格式3 Q6 Q1 s: E$ X1 b. Y* I7 h% _  B
    0:标准帧格式, i" d2 k1 m. T
    1:FDCAN帧格式。  _  n7 d! @6 R  F3 `
R1 BRS(bit20):比特率切换
/ W+ \$ w4 E9 v) L3 n; H( E! m+ y    0:接收到的帧没有比特率切换3 r- `* m3 X+ s- p9 ?
7 v2 ]2 T# y0 X" L9 |1 H
    1:接收到的帧带有比特率切换$ {" S. \; q) K2 X: O& z
R1 DLC[3:0](bit):数据长度。
% O7 Y7 T- Q" U    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。) U5 Y$ }$ _" s0 `1 d2 r, L
    9-15:如果是传统CAN,接收帧长度为8字节。$ v6 x2 p8 f* G6 _9 R; W; \
    9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。
1 F- c$ A3 a8 F5 a0 I9 x9 S* OR1 RXTS[15:0](bit15:0):接收时间戳。
  n# q+ p8 [5 B, r' o- [" }$ M; I! AR2-Rn:接收到的数据。
8 \; R' F9 ^$ c5 f$ x
  [5 s, c$ S. }+ w$ ~4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。
% l& {) Y$ P: ~# A* T8 G4 ?0 \; }- g7 i+ [5 Z
20191121174147402.png

/ [9 e2 t+ f+ l1 j( w
- t4 l+ t/ Y* M4 C  z$ P' yE0 ESI (bit31): 错误状态标志
0 L+ `" M6 J, l% u+ t, E. T1 ?
( I% {% C2 U6 G* j    0:传输节点显性错误4 M5 ^1 n' f+ m  c+ v" H: M* o5 `
    1:传输节点隐形错误。; c, L. M! u' O0 Q% L. y
E0 XTD(bit30):标记是标准D还是扩展ID4 r4 @: y4 U6 h  v2 o- e
    0:11位标准ID.
  |% K8 S: N" z* h3 ^    1:29位扩展ID+ S4 m) P3 `1 k3 e0 D" V
, O  n- T7 a$ \7 j+ U
E0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
9 P* G; M& p) e( g    0:发送的帧是数据帧
9 H0 Q* ?; h' U0 c    1:发送到的帧是遥控帧& a) z& f+ g# O* L2 X1 e& g+ h
E0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。0 e/ C$ Y9 `+ y8 `- R( w
E1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。% D) N' J& s- Y$ y4 }0 w) y
E1 EFC(bit23:22):事件类型
* D0 m4 @8 }0 N  ?4 l$ |' l    00:保留。( I9 I$ Q1 ], q; v  G
    01:发送事件。3 [! U* g& n# g
    10:发送取消。
: n2 L3 Y2 m( a6 {3 l& Y/ R    11 :保留。( w. d* B/ B7 t) h/ d
E1 EDL(bit21):扩展数据长度
! M% }* K/ g4 c$ l! K    0:标准帧格式6 a2 o1 v! Y- `! o( `# W
    1:FDCAN帧格式。2 n+ `. }0 y* W$ s5 R! S3 n
E1 BRS(bit20):比特率切换
, h% B" |* M0 y7 Y( y- Q    0:发送的帧没有比特率切换
7 f. X2 G( @- x5 ~$ B
5 m& E' ?6 P) [6 Q) Q4 O0 e    1:发送的帧带有比特率切换6 _; b/ o* u/ u% [) R
E1 DLC[19:16](bit):数据长度。" c6 w- n* u3 x6 v" a) |5 e" F
    0-8:传统CAN+CANFD,长度为0~8字节。  Z1 b* L5 Q- G! g1 P# N2 d
    9-15:长度为8字节。
: U/ Z% v" N; x* g) W) X" eE1 TXTS[15:0]bit15:0):时间戳。
- w) x( N( l, w, j" \' H- l; v2 N6 ^9 Q/ \' A  c: o
5)、TXBC.TBSA:发送buffer,有32个元素。
6 f$ X  G* i8 K% H& q% T: S
/ \* a% r+ ?5 l, G
20191121175121493.png

, B% P/ E& S8 c
/ J( C. O+ m& q4 }( i3 \T0 ESI(bit31):错误状态标志4 N7 f5 S% C8 w$ j& {6 c
    0:CANFD帧中取决于被动错误标志。
! W$ ]1 q( C9 E  {    1:CANFD帧中隐形错误。
9 E' |+ O' l2 S, O% V- \7 FT0 XTD(bit30):标记是标准ID还是扩展ID$ ^- U/ z- r& }# ~2 C
    0:11位标准ID6 r! O5 F: C( E8 v; |6 M
8 u6 h% j% G( a7 [5 t: J4 d6 q
    1:29位扩展ID5 N0 y* X' v, O2 _% Y/ o: q( `* l

% L0 o6 ~% x3 p7 aT0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
4 L0 R5 |- g$ ~) n    0:发送的帧是数据帧* g  N: z: ]! @/ h) D
    1:发送到的帧是遥控帧" H7 r4 N! q2 w. t& m
T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。; x* ]' W1 P) g% t! Q& O6 ~  S
T1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。
0 t/ R* {4 u+ f2 v5 R; l4 cT1 EFC(bit23):事件FIFO控制
/ c: E! k. k3 {" c7 F    0:不存储发送事件。
5 [) v" c0 ~2 \3 d. f0 Y    1:存储发送事件。, O3 L$ Y1 W5 d, H' L
T1 FDF(bit1):帧格式5 C8 ~" ?3 _/ L5 H
    0:标准帧格式" p8 i% c+ X2 r+ h7 {/ }
    1:FDCAN帧格式。
4 @, O5 A, ?# x* f4 r7 q$ oT1 BRS(bit20):比特率切换  \' y  f2 z) `# D+ a
    0:发送的帧没有比特率切换3 B5 Z% [2 U9 [# l; s
+ x) r& [' g9 U
    1:发送的帧带有比特率切换
# L  [8 G# R# X- J+ c1 hT1 DLC[19:16](bit):数据长度。
) z) i/ K  c; G  |9 G+ J8 R2 f% m    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。
4 N% i1 z9 `; n7 @6 o    9-15:传统CAN,接收帧长度为8字节。
! H! U8 p" b2 l& ^+ O2 c9 S' ]% p    9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。
# V0 l& R$ @8 M' M3 M8 PT2-Tn:发送的数据。
+ @1 Z% o$ o; K/ p& S
& ~. G7 E& `. O" {& q& b+ p三、过滤器设置
/ w1 d8 p" k" G6 l4 h( E9 n0 _1 J4 w3 O
        标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:
; U8 ]; F8 A3 v- {5 ~6 \' U6 M0 v& B6 ^* u/ k! _8 H
        1、指定范围过滤% c  S5 F$ J6 _$ R0 }: w" N
        通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:8 s& H( d5 i8 X* J
" [: z9 B8 f* _, _3 f4 Q
  1. SIDFC.FLSSAn.SFT=0        //范围滤波
    6 M# X3 R) i( ]) [" L
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到RxFIFO中* n- ^6 O8 D2 q) N5 O
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    , w' E) g$ i; X+ [5 D1 e
  4. SDIFC.FLSSAn.SFID2=0X321  //ID2
复制代码
1 T9 n3 f+ |5 \8 h# s
        2、指定ID过滤8 F' L& d- x# u7 g5 N2 N/ M
! L( l# J5 I! A( P
        我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:
0 H. y$ r, ?" w0 d' @2 K6 z
' O# ~( X+ m9 p+ R+ v5 i! A+ [0 l* F! f
  1. SIDFC.FLSSAn.SFT=1        //指定D过滤8 I# ^+ c9 R+ {4 R# x  j2 q) k
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到Rx FIFO中
    0 n* p9 Q9 z% E
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    ( n8 k$ c. l) k% L3 ]) u/ \' |5 F0 l
  4. SDIFC.FLSSAn.SFID2=0X123  //ID2
复制代码
4 P7 R4 U+ V4 B% N* x' ]
        3、传统的位过滤- ~8 k' F1 i! C4 e
        第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:
) w4 @* N/ @5 ?6 ^1 G% i* U" J7 v/ @
  1. SIDFC.FLSSAn.SFT=2          //传统位过滤
    + v  p0 H) B) h& c4 v
  2. SDIFC.FLSSAn.SFEC=1         //如果滤波匹配成功的话将消息保存到Rx FIFO中
    % x+ c% q& F- G& M. _/ A1 Y6 i
  3. SDIFC.FL SSAn.SFID1=0xFF00  //ID1
    : {- @- o2 N- B+ [8 d8 a
  4. SDIFC.FLSSAn.SFID2=0xF00    //掩码
复制代码

- u' I7 Y, V, c: w& T% M        其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。
' d$ j. K4 |3 G- W7 `. d5 m! z8 D5 ~3 ]
四、CAN的收发过程) P" s+ g# z5 l- H+ _5 h
& Q( v& \, `0 B
        1、CAN接收过程, ^8 ~8 j$ g) H

0 P" C  K* u+ |/ K7 A% }2 e% v' J) ~" _        CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。+ i- ]) s: \, a& O

' J7 H/ L, y! l1 B/ U        接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。7 }0 j4 B. _) z3 e/ x3 |
2 S; D7 \0 n. w( i8 l0 I- j+ O
20191127114550965.png
" F! ]  c$ t/ G
: _7 M* w& B, A" Y' k# _8 {
        当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。
0 e0 S5 q2 P0 R; @* B7 V$ ?
, ~% i+ }0 H% ]/ s& R1 O( U) U8 Q5 ^+ ~" z. A
20191127111235302.png

5 N$ Y, q; i' W. J2 D# n% J6 K6 \$ I$ V( |
        为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。
  ]; K( }( i# C1 J% p! [) c        读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):- V$ ~! P! G# p
+ H- b" [$ |9 F# C6 x8 W/ d& `
  1. /* Calculate Rx FIFO 0 element address */; m4 `3 h3 M7 i: Q6 A- h
  2. GetIndex = ((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8);
    & h/ t: b8 R% |4 w: U' m0 F7 t
  3. RxAddress = (uint32_t *)(hfdcan->msgRam.RxFIFO0SA + (GetIndex * hfdcan->Init.RxFifo0ElmtSize * 4));
复制代码

% C# G; e. R+ a5 j1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。  G& Y( C+ r& g* u6 Q
' C% Y; P8 ~% y3 T
2019112119095892.png

- M  }8 f1 Q$ i' t1 ?" A
/ R& H+ W7 d, O- p" {" N: ~2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。0 y) n0 \0 K9 c* W/ l1 R

3 M. O0 V  @5 Y) P# C3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同7 e: e0 o6 V9 P# ~4 l* L

' ^( M3 L% h% n/ X8 q6 R  o# {& O) P

7 q8 C2 H7 q& f) {
; Q" E* E! e. Z7 ^" Q如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。$ X2 y+ J" {- P

8 I* [, B7 x( R+ |6 R7 N如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。! w0 t+ \3 }3 V  O& Y' b
' P& |; R  s  `/ R4 l, Y; s
        计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:
4 Z' U! u* v+ Z6 e$ \' [) C: k1 V  Q7 P: [: \
  1. pRxHeader->IdType = *RxAddress & FDCAN_ELEMENT_MASK_XTD;- @7 ?8 j* c5 u( ~- @
  2. pRxHeader->Identifier = ((*RxAddress & FDCAN_ELEMENT_MASK_STDID) >> 18);2 x: [8 [9 o0 g6 E! a
  3. pRxHeader->RxFrameType = (*RxAddress & FDCAN_ELEMENT_MASK_RTR);3 o/ {6 E/ O& v( ^, g
  4. pRxHeader->ErrorStateIndicator = (*RxAddress++ & FDCAN_ELEMENT_MASK_ESI);  //这里地址自加了一次
    / [) j$ x- w- U0 g) r
  5. pRxHeader->RxTimestamp = (*RxAddress & FDCAN_ELEMENT_MASK_TS);
    ! l1 }9 R6 a# v4 G4 P- ]1 ?5 R! S/ U
  6. pRxHeader->DataLength = (*RxAddress & FDCAN_ELEMENT_MASK_DLC);
    ( x3 {9 k5 p/ |4 f4 i
  7. pRxHeader->IsFilterMatchingFrame = ((*RxAddress++ & FDCAN_ELEMENT_MASK_ANMF) >> 31); //这里地址又自加了一次
    * X8 ^5 g! L, P+ n7 y8 @
  8. ......
    4 m" K2 X6 o" O; l5 p
  9. pData = (uint8_t *)RxAddress;
    : [( _  Y( ]1 M) p9 u5 |  w! `
  10. for(ByteCounter = 0; ByteCounter < DLCtoBytes[pRxHeader->DataLength >> 16]; ByteCounter++)1 t8 ^- X# y5 k5 q2 q
  11. {
    ; z6 C0 v- w, A3 ^; o) V0 b
  12.     *pRxData++ = *pData++;
    8 i% P6 d  ?" F; \$ \
  13. }
复制代码
7 [+ `/ i( `9 s" V' ?0 |4 Y4 f
        2、CAN发送流程
( l) k7 k- C$ a8 b
1 D. Y/ M3 F2 g& o+ A0 F* W) p7 D4 S/ }        在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:
) \, W0 v0 j: v' j# K4 T0 E+ r# `2 A+ S
20191127150042199.png

8 J- C9 h9 t4 f# y" C/ O
  B( E% W7 r+ y3 M2 f        因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。
/ y* H" E' N" W( E6 n+ y  H( O) V6 j
8 I  H* P) E7 C. |        下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:  ~; r! N/ k) y0 k2 ]& x
: p6 @4 o/ M  |
        1)、专用发送buffer6 K, l- Z: a, A, X( q0 L$ x* z' C

4 o  D$ j; ?: O3 c. e6 P. |% h         专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。% o) [: u( L2 M: A5 t, y
        如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。
7 D' x) D5 R* I8 i$ N# P- d% h5 e; T$ J5 x6 \( w7 f3 z8 ~" t
20191122101555794.png
( ]9 J( W  M# r7 Z, {4 \) T& ]2 ?
- g- v) T0 S* ~+ Q9 a. o# x
        专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:( v  F+ `- r) I6 {$ `% {  M

% Z( ?- E+ |& U发送buffer索引 (0…31) *4+发送buffer起始地址。+ _) w5 y4 J9 \& A3 ?+ \

- G( d% M1 w2 q' A
6 j+ i5 B+ Y# N0 E4 ]' ^+ Z0 e# n
/ ?& z( ]: X+ i( d/ z1 D6 n        2)、发送FIFO2 U. g. G* U' J. M! l8 |/ G$ h) m

% s! D1 ~2 W+ K8 W/ Q        发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。) e9 X( Z4 x/ K& D- `& Z* v
        新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。
9 V* N* m4 M% _
4 z5 u% ^- \+ ^* F
20191127160804244.png
9 P/ q! S' a6 |$ {. v* m

9 Q& F9 @" j9 l0 T- R7 A        发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。
/ ~: I) U. \' w5 n% @
, Y) n  \1 |& s3 P6 i5 R% P0 ^        如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。
1 Y, {2 s, F& l  G" j        如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。) [3 I' P' w" ^+ N
        如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。  O7 C# i' G) p& [
        发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:
3 v4 @7 V% e2 D0 N. ~1 U( m6 N; O% O# a, k5 [9 p2 d
放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。
) ~2 x, S7 K& q2 U
! a$ Q* n* d6 C8 v/ ?        3)、发送队列! e4 \( A& @9 z& [
        发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。* \9 f2 \6 B7 G) \2 r/ K5 \% H
        新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。
9 \6 L' C/ _0 V$ G' e- M        应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。) ?7 p: D% l$ p; U, A3 L
        发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是:
  L6 h& t1 e3 Y$ S. O
! U7 t' \  m# e$ l; p0 `发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。- W1 f8 U0 C6 U. |7 z
5 M; L) U. D) C
        因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:0 l) v& S- ~7 C& ?

5 E- h2 k. {4 D/ M* k5 D' e$ W  E        1)、发送buffer全部配置为专用发送buffer: {# J! T$ i  f" u* @
$ T; H1 g( n: t, Z: C
        每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。( t. u  s+ D3 V) E) f6 |
2 U3 J& ?# z. N" O- z
        2)、混合专用发送buffer和发送FIFO
& T) D, Y" |% A/ ?
6 ~% O* _  v  x6 g0 p, i1 J0 R9 Y        在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。6 t( X; B/ p  v' d5 c3 L/ B

- x& Q1 ^; P; V1 ]3 a
20191127162943182.png
+ W- B5 \2 R# N- ~: Q

3 e8 ^* c: R1 G7 @; B2 u/ r. A) z" k        发送优先次序:. H" p6 n! Z0 w5 D5 ^
A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)
8 N/ o) U. {* C* |# F% CB、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据
2 S9 |# U+ U8 B; C. c
8 T9 v4 u6 {' O9 E        3)、混合专用发送buffer和发送队列4 L+ e+ x9 A, u% h& [( n2 a) F; S
# y  I% `5 l# M8 K4 h7 u* \' o
        在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。
  \! v- D& \4 N! i. S- A/ t$ g7 |8 s! [
20191127163202212.png

5 G0 P  e2 x, W' W6 U9 v4 E5 k" `4 r9 u+ x" d, ?+ t
        发送优先级设置:$ x* O- g6 v' D+ L4 |  b( J
A、扫描所有激活了发送请求的发送缓冲区
/ G! n% }# m" e" Z) c% \8 ^B、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据$ r; O2 c. ~) W, u! I2 W+ y0 m" Z

" B( J# _6 p- R4 e; N        再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。
% q7 M/ z6 I) K- f) M/ e1 b! q& m& I2 [" v- p0 t3 X1 d" `# B
        FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。
$ C; S/ L! G' K4 w        发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。  _7 {. {- G0 i/ e' {
        发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。; L! v! h0 G! ~/ k; p" V( r. a9 y

: y- }. ~  M' g6 N4 C# G        为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。
- P1 u5 O2 _3 k, l; s0 h: x; U, m* q. {# I3 m/ C
        CAN发送消息的代码实现:  [2 q8 N( }4 q* U
( \* G5 l, F; K
        发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:
2 h: o9 ]3 L* ~$ p+ `) i8 A
( u+ W6 b  G, j% s* Z2 O
  1. PutIndex = ((hfdcan->Instance->TXFQS & FDCAN_TXFQS_TFQPI) >> 16); //获取元素编号 1 Y, z/ w3 V7 l# r' n+ F( w9 e
  2. FDCAN_CopyMessageToRAM(hfdcan, pTxHeader, pTxData, PutIndex); //复制消息到Tx buffer
    ! h! }$ p7 w& _4 G" G( U0 x
  3. hfdcan->Instance->TXBAR = (1 << PutIndex); //发出传输请求
复制代码

6 S" g7 G2 h& ~) D+ G( Y        发送buffer请求寄存器FDCAN_TXBAR,描述为:
* a* ?( p3 P6 y; _/ r( d
+ O$ ^$ ^* g# r2 _

2 a% \! }1 k7 ]' r* j; L5 l3 @) X4 y. V6 `" O
        此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。" q2 J7 W; j7 |9 A2 q  S; C3 E5 `, p

6 r% A( u7 R3 L! p+ W
2 Q' u! N1 S% j- L/ L& d. t1 c% f" l& o$ c
五、初始化
7 a5 `2 L# Z3 g
, Y7 z7 N' @5 @; l5 r* a        初始化可以直接使用HAL库,
* Q1 Y( I4 d- z- T$ L9 d3 n8 D3 r. F
  1. u8 FDCAN1_Mode_Init(void)2 Q' w9 B$ g$ _5 |9 J- a. G
  2. {
    ( @4 q( g4 x- q! u  R, \1 D9 k) k
  3.     FDCAN_FilterTypeDef FDCAN1_RXFilter;
    1 m  h/ V$ I# ~: ^+ K; W/ w

  4. * ^5 Z  K+ U+ l+ u$ _; Y$ V
  5.     HAL_FDCAN_DeInit(&FDCAN1_Handler);                              //先清除以前的设置
    4 o* S5 h9 C6 w) K5 E
  6.     FDCAN1_Handler.Instance=FDCAN1;
    3 _$ v  R9 }; \% [, S4 S! Z( g8 r8 ?4 z
  7.     FDCAN1_Handler.Init.FrameFormat=FDCAN_FRAME_CLASSIC;            //传统模式# D& }5 k$ h* K
  8.     FDCAN1_Handler.Init.Mode=FDCAN_MODE_NORMAL;                     //正常模式9 `% |; _+ E; o4 ?& F, x$ r0 C
  9.     FDCAN1_Handler.Init.AutoRetransmission=DISABLE;                 //关闭自动重传' c5 v2 L; g5 D
  10.     FDCAN1_Handler.Init.TransmitPause=DISABLE;                      //关闭传输暂停           
    . J, V( q7 u2 b$ }2 _
  11.     FDCAN1_Handler.Init.ProtocolException=DISABLE;                  //关闭协议异常处理4 \1 e" T/ K' U+ n  p7 F- d
  12.         / z  ?/ n8 ?$ N+ b4 r& z* {
  13.         //时钟为200M,baudrate=200M/(NominalTimeSeg1+NominalTimeSeg2+1)/NominalPrescaler,这里配置为1M
    1 j' t+ [2 ^9 B( D# N
  14.     FDCAN1_Handler.Init.NominalPrescaler=10;                        //分频系数
    9 v) A+ B" G) y9 z
  15.     FDCAN1_Handler.Init.NominalSyncJumpWidth=8;                     //重新同步跳跃宽度
    ' t9 r) z7 u% d8 i
  16.     FDCAN1_Handler.Init.NominalTimeSeg1=11;                         //tsg1范围:2~256
    ( V* G! w8 u0 g9 s' U
  17.     FDCAN1_Handler.Init.NominalTimeSeg2=8;                          //tsg2范围:2~128
    7 f- R  e' K6 T/ q) o
  18.         $ W$ T8 D: A- G$ _. a
  19.     FDCAN1_Handler.Init.MessageRAMOffset=0;                         //信息RAM偏移,10KB消息RAM共有2560字,故可以偏移0~2560
    / e  C: P( b# a& S+ m- o* r, X' t
  20.         //使用了多少个滤波器就要设置为多少
    0 M/ k* _0 D: K, L% {
  21.     FDCAN1_Handler.Init.StdFiltersNbr=3;                            //标准帧滤波器个数,0~1287 j: K8 f" x, d5 i/ P
  22.     FDCAN1_Handler.Init.ExtFiltersNbr=2;                            //扩展帧滤波器个数,0~64. J; h3 D; p; t) j7 \
  23.         
    1 n9 j+ N1 ?/ ^& C# A4 M
  24.         //接收FIFO0、FIFO1和buffer配置,此处没有使用FIFO1故个数设置为0
    5 b3 e- M# h/ o# _( J! f$ \
  25.     FDCAN1_Handler.Init.RxFifo0ElmtsNbr=64;                         //设置接收FIFO0元素个数,0-64( ~0 e0 w7 d$ j2 ~, z% S/ ~$ |
  26.     FDCAN1_Handler.Init.RxFifo0ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO0元素的数据域大小:8字节        ' ^4 ]1 T0 L! R. M. H$ f8 F. u
  27.     FDCAN1_Handler.Init.RxFifo1ElmtsNbr=0;                          //设置接收FIFO1元素个数,0-64
    2 U  \' B- K/ H+ X$ d8 e
  28.     FDCAN1_Handler.Init.RxFifo1ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO1元素的数据域大小:8字节               
    # A0 P$ {4 b2 D0 K
  29.     FDCAN1_Handler.Init.RxBuffersNbr=64;                            //接收buffer元素个数,0~64
    , C4 W. Z  j" _
  30.         FDCAN1_Handler.Init.RxBufferSize=FDCAN_DATA_BYTES_8;            //接收buffer元素的数据域大小:8字节        
    # W5 A# i8 }2 j/ ]
  31.         9 y+ ?: f! N5 I& i' K
  32.         //没有使用发送事件FIFO功能,故TxEventsNbr设置为0。把发送buffer全部作为专用发送buffer使用,故TxFifoQueueElmtsNbr设为0.
    / L3 v7 \3 i" b$ Y
  33.     FDCAN1_Handler.Init.TxEventsNbr=0;                              //发送事件FIFO元素个数,0~328 S  L7 e5 V4 N* A) `/ v
  34.     FDCAN1_Handler.Init.TxBuffersNbr=32;                            //发送buffer元素个数,0~32
    1 \- U. l+ Q1 B. k0 L" K. b; M
  35.     FDCAN1_Handler.Init.TxFifoQueueElmtsNbr=0;                      //发送Buffer被用作发送FIFO/队列的元素个数,0~32
    : q5 K- Y, d5 k% D- K
  36.     FDCAN1_Handler.Init.TxFifoQueueMode=FDCAN_TX_FIFO_OPERATION;    //发送FIFO模式选择,可以选择FIFO模式或队列模式
    / \9 m& ~8 g4 W4 ?2 }/ z
  37.     FDCAN1_Handler.Init.TxElmtSize=FDCAN_DATA_BYTES_8;              //发送元素的数据域大小:8字节
    & j+ O5 }, X3 D9 {! B! z. g
  38.         3 G4 Y- U! Y$ I8 J$ {1 l
  39.     if(HAL_FDCAN_Init(&FDCAN1_Handler)!=HAL_OK) return 1;          //初始化FDCAN
    ! U" Y. C# s! Z% `
  40. - G/ j- @  Q% z- p$ i* S1 P
  41.     //配置RX滤波器,标准帧   # y; m2 c7 Z8 ]. M5 v% q3 c5 h
  42.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID; ~5 m( A" ~* G0 R1 z
  43.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
    # E+ @  d) W* W8 \
  44.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    0 s) \, \! N0 `1 W* d
  45.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    & c' W2 b3 e. E5 |
  46.     FDCAN1_RXFilter.FilterID1=0x112;                                //11位ID& D3 g; g$ o4 D6 C3 g& f, }& }- |
  47.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    ) X' t$ C. d% Q9 M
  48.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    ) _5 D* L% D4 _( o

  49. 7 g7 [$ K. D; I
  50.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    + U) F9 X+ o0 s6 e. u1 R* x
  51.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                  
    . P: W5 q- O1 g3 y% w+ A1 m* N6 y" s
  52.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型  F0 M$ F* U" }+ Q" O* }
  53.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  + S9 a, Q7 p- p, ^; l  ^5 d
  54.     FDCAN1_RXFilter.FilterID1=0x113;                                //11位ID
    . s8 N$ q% y; i) V; u
  55.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码) k  q9 t4 \: ]" e! r5 t/ l1 t
  56.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化0 I" C, C! t# w5 h7 O% @
  57.         
    2 Q& ]6 t% h0 T/ j6 o* H6 B7 ?
  58.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID) P5 q5 j1 v2 v0 m
  59.     FDCAN1_RXFilter.FilterIndex=2;                                  //滤波器索引                  
    & G: o% b! ^* P0 {1 ^. E/ R
  60.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    , b- T; p. _! ~( y% e$ \% \
  61.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  # [" Q7 c6 C2 Q" p" O
  62.     FDCAN1_RXFilter.FilterID1=0x114;                                //11位ID2 T3 @( j' N0 a+ Z
  63.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    ; i! J: W, \9 T% G3 H
  64.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化8 R6 o" X, s  ^0 |7 K
  65.         
    ' t- [+ M; O3 N, V0 n9 G  Q
  66.         //配置RX滤波器,扩展帧。标准帧和扩展帧的滤波器索引是分开的   
    ; |5 _. L# C4 Z6 x6 @
  67.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID3 v  v2 U7 g4 m; p( u# S" o
  68.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
      f; }* S" a" l. G2 C9 Z) |: [8 P
  69.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型* ^$ i& q7 R, U+ p& t% E7 B  q
  70.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  : l- x& R3 {$ c' B
  71.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(2 << 12);                  //32位ID
    5 y* b# B) p; x( }# M
  72.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码5 O1 _6 o/ \3 t0 T# d& s, D
  73.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化1 z) J6 g3 S4 X/ l7 z
  74. & [, ^0 [6 w1 v, T1 \) Q
  75.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID
    ( ]& e* v; Q/ J: G* E: e9 Z
  76.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                   + N" G" ^. F: o- |/ c
  77.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型7 K. A/ |' V6 K4 ?$ G
  78.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    2 `# L, r$ {# B* W& p5 @! I
  79.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(3 << 12);                  //32位ID* R" y+ n9 x; \
  80.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码5 `8 g+ R. o# E+ |5 W
  81.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;    //滤波器初始化, F# V+ f5 Z. D# x6 }; V) b
  82.         
    ' e4 h! X# }# z9 \; v0 K2 L
  83.         //滤除的消息直接丢弃
    ' K# f& k% @3 x/ x! W. T  N8 V
  84.         HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler,FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);  //设置被滤除掉的消息的处理方式# H: [6 M5 M4 S! v
  85.         
    8 }5 L# c; d' q) i/ ?+ U+ I- ]1 ^8 r
  86.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);   //使能新消息接收中断$ P$ \1 z: U1 ?8 y/ o
  87.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_TX_COMPLETE,0xffffffff);   //使能消息发送中断,0xffffffff表示所有的发送buffer都触发中断        
    4 S( j! ~. P: S+ b
  88.         
    & o9 t+ I7 w2 d1 a  s& W/ t* F  R
  89.     HAL_FDCAN_Start(&FDCAN1_Handler);                               //开启FDCAN
    9 `+ E8 [4 S9 V6 v9 \) Y4 n0 W3 C
  90.     return 0;
    + L9 `" a. }) M
  91. }
复制代码

) O! e" n/ |( ]' Z# [! m% k3 }/ B0 |5 r0 Y2 P+ |" g! E
六、CAN发送示例(来自正点原子)* D0 a' I8 B# O3 M1 f- k+ B

3 x4 [2 B  {8 W8 g  |! }
  1. //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)        4 k& N; J* ~7 B
  2. //len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8                                    
    - u6 M0 m9 ?  J0 b2 P4 S
  3. //msg:数据指针,最大为8个字节.
    / ~! t2 p$ _# W; I1 N) M
  4. //返回值:0,成功;
    " n; ~8 [! j. ]0 B
  5. //                 其他,失败;- A. s" d6 b% s% f  B3 x9 c8 o- }
  6. u8 FDCAN1_Send_Msg(u8* msg,u32 len)
    9 Y' x) Q! V# ~6 D$ X* V+ c
  7. {        ( n& u5 ?; n2 a
  8.     FDCAN1_TxHeader.Identifier=0x12;                           //32位ID
    , D: a5 `2 N8 d# O9 X
  9.     FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID;                  //标准ID1 C( A% F- b! d) V+ O+ v
  10.     FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME;              //数据帧
    * A6 V* e4 ^9 P' w" h* \
  11.     FDCAN1_TxHeader.DataLength=len;                            //数据长度: a. L- T, W8 b. J- B
  12.     FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;            $ c1 R+ }. X* J2 h% n" z! L
  13.     FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF;               //关闭速率切换% N' a* u0 N: t! m
  14.     FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN;                //传统的CAN模式8 Z) @$ {' p4 L$ W, [  B% p
  15.     FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS;     //无发送事件  J# u/ }* Q$ L* ?
  16.     FDCAN1_TxHeader.MessageMarker=0;                           ! D: t9 u9 Y0 Q& y& b' n1 k

  17. 9 q3 f% n" ?7 ^
  18.     if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送$ A* y2 l+ L) B* q0 N' Y% M" p! ~1 F
  19.     return 0;        , r" p& S8 H, v+ b1 a
  20. }
复制代码

* x$ Q, h1 Z7 U' `4 J2 V; x  X3 V七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)
( T* y4 Q6 F* f' H+ g+ F0 @# d& V3 \8 z1 t7 S- \/ E. p
  1. //can口接收数据查询5 W/ z2 W' X) o: I% U8 m
  2. //buf:数据缓存区;         - w1 y4 _6 x3 X9 J6 |
  3. //返回值:0,无数据被收到;( E( K- Q) ?4 p: a5 a7 T& L
  4. //                 其他,接收的数据长度;
    9 ^: V6 d' t8 E, @; y
  5. u8 FDCAN1_Receive_Msg(u8 *buf)! a1 V4 t6 v1 i1 m8 ~  v8 T0 P
  6. {        
    8 i  {" t" z; ]% G: A8 m/ C) ~
  7.     if(HAL_FDCAN_GetRxMessage(&FDCAN1_Handler,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf)!=HAL_OK)return 0;//接收数据9 i7 ]+ g7 ~* [( V
  8.         return FDCAN1_RxHeader.DataLength>>16;        
      q  K; [! _4 p' F7 s& D( n* I
  9. }
复制代码
5 R) ^% A& u: e+ y" |$ q1 p% I
参考:《正点原子》  STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准); X* j( X  v5 C8 O6 w+ c7 D# z# e
, P& ^" R) ~; g- d# F: b

  F/ @9 i) h: J' K4 c
赞 收藏 评论0 发布时间:2021-12-22 14:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版