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

【经验分享】STM32H7的FDCAN

[复制链接]
STMCU小助手 发布时间:2021-12-22 14:00
一、介绍8 J, i" \# h2 p5 ^
; v$ m. Z* c" x/ U" I
        FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:
$ b4 I, ?3 @' A1 P# x8 I
% N7 M+ l1 {# ?- y# J* z       1、每帧数据段最大长度由8字节上升到64字节。
6 _* B, j2 D9 N  i
( R; V+ [) i1 y7 }        2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。
: s# c5 G( P+ E, |( l% U
7 g+ ~7 I- j/ A& y" v        3、向下兼容CAN。( ^! v1 r* R0 I0 z  f# q* y; [

9 }5 w9 W7 b* u8 O/ B! S8 Z        H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。6 J# ]0 J" p$ L) t, j

! e1 L% g0 k) C二、结构) E# `, ~& k5 R0 ^* I

! c* O1 i6 U: d. A; A% L( H; ]/ \- X        先看结构框图:
. w/ |- s8 N7 [! b1 g2 t" h+ r
: Y, L; _5 L# h6 \
20191121162856859.png
, P- u& r! f8 k; n

5 K) H5 R& H! [7 Z/ v1 l, N$ E2 ?. ~1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。
; `7 o2 u. Q, y2 W, j' X9 |1 `8 K
. l$ L* [4 s9 D6 H; E8 z: L" O
20191127102816991.png

  a/ C( h  Q" H( T& v# z5 c' R/ M5 D" C& M+ x
        可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。
9 a  Q2 x! v" g, B5 D8 q: J7 u* N  n7 l  Q; e% C) U
2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。
4 E" f7 J4 a" o& S$ S9 C1 A4 P
0 m" C+ v( g  Q; y3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。
5 k6 }. y5 e4 P) B7 n3 [& D. n
" q4 |; p. f5 ?+ z3 E4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。5 q. r" H) Q* s0 S

. g: s" Q' p% \) d4 q8 k5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:1 ]& z9 t0 o0 ?0 T. ^
3 V' s0 B7 u5 ~5 x1 _+ @
20191121164211703.png

. P( P' p, @- v. n  l. i1 [6 x/ ~( L+ A0 F/ g
        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 _% B8 Q" |" A( W

' d- d) a7 ^- ~6 t' C8 v        1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。6 x) x( v* t/ o8 e# l
7 J- U- a) R# y/ t4 K2 T
        2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)
- U5 o9 a6 B) x2 H3 Q) _9 ]& ^" I/ x# S) y; d# I+ B
        3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。
) ]# Q& T* E% S5 A4 u% ~
: c) `- }( u7 J! W1 J& p: N        4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。  L4 [0 b* ]4 e! J* C* [  D
/ e. ?! F& C0 `+ @
        5)、RXBC.RBSA:接收buffer,最多可以有64个元素。9 d5 U4 R5 ^0 L, B2 q7 `" V7 r5 G8 a: h
$ p1 h2 Y6 G7 F) T& D/ _4 i7 U8 b5 M
        6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)
% Y/ l8 O! S2 z! c, ^/ \
! O  a* i$ ]0 N- V4 G- A* T        7)、TXBC.TBSA:发送buffer,最多可以有32个元素。2 q3 e; U0 t0 B. q3 M$ I% `

& P  `' w+ I: B        8)、TMC.TMSA:触发存储器,最多可以有64个元素。
' v3 J$ s7 V* Y! C" L8 M& K% l0 R6 u/ _- h% Q/ ~6 F
《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》
. s1 E: W" i3 f3 ~
; S) F  Q$ [! t* S# ]        下面来详细介绍这几个功能区域:
% H% C7 `  B  a- f! Q. c
2 E8 g  E3 F; r; n/ q  g) c# o9 z1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:) y  e" y- c" }+ P" a
: @5 A  c$ r% z" a
20191121170625565.png
0 Y" d' y) U7 w4 m

, P/ E0 {( ]6 I4 W( lSFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:* v3 q# t/ |" H; h
    00:范围过滤,过滤到SFID1到SFID2中的所有信息。8 F0 P0 X6 T+ v
    01:双过滤,过滤到SFID1和SFID2中的信息。
" w# d4 t. p" M7 C% E! G    10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。: K1 i9 w  I) H2 ^
    11:禁止过滤器元素* U2 P$ B+ `+ M6 W
SFEC[2:0](bit29:27):标准过滤配置" d! ]0 B/ P! q7 ]
    000:禁止过滤器元素+ E( o* C5 O. n' T, e% W/ n
    001:当过滤匹配以后存储在Rx FIFO0中。% C6 v8 q& c0 E0 f1 h+ |
    010:当过滤匹配以后存储在Rx FIFO1中。1 [) U% `: P8 n$ `6 K
    011:当过滤匹配以后丢弃。
7 O) s4 \+ ~/ t! o    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。) ?" W$ b* J3 @2 i7 a9 G! D& a" H! B% W" v
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。1 s* g0 f; p. V& Q
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
. k2 K! O* o, s' y: L3 z% V    111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。) k$ Y$ Y$ O9 f
SFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。5 h) ~4 {0 V" w; n7 Y& Y1 a, {# p  h
SFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。
1 b% _# U$ S  }# j/ {8 Z* V
0 ?: T* H0 K, i7 N  x  p; i& w! o6 ^SFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。% @4 G4 W: ^6 c) y  n
    00:将消息存储在Rx Buffer中。( _6 X8 v. t) z8 g5 g* ~
$ L/ G  f: D2 l) {
    01:debug消息A。    0 N9 g, X8 ~3 S1 S. ?0 r

  m3 k) s4 D2 `* S+ ?# }! [    10:debug消息B。+ D  u1 n; K# J9 _1 d& ]( V& T
9 n, R3 B. h3 G4 b8 C
    11:debug消息C。
* S1 X: G3 y! O5 L9 n) ]5 I% a! ?# W8 C4 e5 v, j
SFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。3 B0 o1 e2 W) G; _0 V% J6 R! t

$ V$ D& R' f( w1 {$ f; pSFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。% Y4 E0 Y- C* D" e% C

7 A) u  d. m' _2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:0 l7 I# J4 R  }6 m6 H
/ c1 G" Z; v3 |
20191121171836710.png

0 k8 O* U* N) `8 D* `2 z/ I* O2 G; k" [! v* P4 f8 J. A$ F5 n4 E; h. u
F0 EFEC[2:0](bit31:29):扩展过滤配置! R) C# a3 P3 |& v; R
    000:禁止过滤器元素
8 u5 Y" }* T* a  ^, j$ \" U    001:当过滤匹配以后存储在Rx FIFO0中。
# R8 g$ |& _( T8 P: m' p7 u& [' u    010:当过滤匹配以后存储在Rx FIFO1中% x4 t) B8 o7 F
    011:当过滤匹配以后丢弃。+ O7 t1 \7 V- p9 C" `  b( n+ G
    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。
: n  `1 K: [' g" O- S# I: P    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。/ p* f7 W3 u- I3 C+ t3 m! F+ I0 w$ W
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
" T  A; d& [7 E4 z. k- c- Y6 \    111:存储到Rx buffer中,EFT[1:0]忽略。
3 i  _& i& ?/ HF0 EFID1[28:0](bit28:0):扩展过滤ID1。
8 m4 n3 R$ Y4 H0 O! w( jF1 EFTI[1:0](bit31:30):扩展过滤类型+ g$ j! H- X/ s1 ~
    00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。
& o0 K3 d8 u/ v+ C3 ]    01:双过滤,过滤到EF1ID和EF2ID中的信息。
) |& z" X7 j9 ~8 e2 B: G; m    10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。
% N" ^4 a& [6 _3 s    11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码
1 i/ o! h& D( ^: k" L6 MF1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。. u4 ?: ?* k- S  I& y& _
    00将消息存储在Rx Buffer中。
0 y- ?% ~, N$ J! S6 O0 M3 P    01:debug消息A。
$ [# U- [3 `0 L$ u    10:debug消息B。
3 o' K. {8 y0 M; g6 W    11:debug消息C.
* A" R8 g" N, rF1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。* o% b  W1 h2 t' G) b5 m
F1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。  w8 A+ U/ }5 Z4 Q. O

4 K2 `% O% }& t& @: E3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:, N1 {! u) r1 Y+ x+ f: W
6 i" w  _0 K* X
20191121172451477.png

2 F: \9 u! p$ M, k1 z$ |5 d( b  R$ [
R0 ESI(bit31):错误状态标志- [+ e! P; }5 r! V/ A3 m; W+ F' v
    0:传输节点显性错误。* W$ ^1 i, d5 m+ d
    1:传输节点隐形错误
7 O& h  L0 n# i8 sR0 XTD(bit30):标记是标准ID还是扩展ID! G8 a/ x# Y) A( f+ e6 R% _
    0:11位标准ID
- Q' |% C1 u8 c* D
. U% r& j; |* F- }7 T) }    1:29位扩展ID
  a1 F* s9 ~" [% g0 I5 Z( Y
' p2 y& R8 @% k0 A, l+ aR0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。; {! S, t# M+ c5 ^% A
    0:接收到的帧是数据帧% ?9 w0 B# n. R: }5 `* `% r8 Z

  I9 c" f9 ?5 e& z    1:接收到的帧是遥控帧8 a2 f/ [- s$ A1 p+ A/ B$ y
R0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。5 Q3 R) i4 [% ^7 j1 T
R1 ANMF(bit31):
9 P7 W0 m' r# B  A. _% A* `5 @6 I6 p5 g/ n& U% Y8 m
    0:接收到的帧和FIDX中的过滤帧匹配
- ?/ Q7 ?' s9 v$ d
6 @. R& |, m$ P) M3 {8 c    1:接收到的帧和Rx滤波器中的任何元素都不匹配
/ G+ V! c/ F  q9 B' t, X% C6 qR1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。
( ?% ~7 H- |; c: w0 ]& p$ _1 gR1 FDF(bit21):帧格式
1 P2 t4 X4 l* X! S0 Z" Y( v    0:标准帧格式
" j9 |; w+ ?2 q, T/ b0 f$ c' p    1:FDCAN帧格式。( J9 V2 z0 H) E9 U7 k
R1 BRS(bit20):比特率切换: J' `* ~8 A( j- d" v
    0:接收到的帧没有比特率切换
$ Y. a3 }6 N: g% G  c
8 k. m# t/ p9 [/ q0 \    1:接收到的帧带有比特率切换3 E( J) E7 E! o
R1 DLC[3:0](bit):数据长度。
3 U( b+ U; Z- g* E    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。" d' @3 G" X# r2 n3 F& F
    9-15:如果是传统CAN,接收帧长度为8字节。
1 |' e7 T1 d% V: t0 y4 g8 M+ K5 U    9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。
) s0 a5 h2 e$ d' K6 |R1 RXTS[15:0](bit15:0):接收时间戳。" Q; u- g( P" |3 c% A. e- I
R2-Rn:接收到的数据。
( P& k' _# W! Q: q. J3 }% T) F0 I) V$ t6 ~
4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。) `, P# x( h% I& a  `

8 c1 ]0 C( s8 h3 G
20191121174147402.png
4 g5 q) z4 ]% u3 k

# ^( \" v9 [+ _6 VE0 ESI (bit31): 错误状态标志- ]6 s3 b- V$ n* |

5 p4 |9 ^; h4 a7 A% _' }! ^    0:传输节点显性错误
" l& [) E  j4 U' l/ |0 g3 K    1:传输节点隐形错误。5 I3 |# Q3 Z2 q3 k( U
E0 XTD(bit30):标记是标准D还是扩展ID
1 r6 Z  _; H" `) K) _    0:11位标准ID.6 ]3 S6 d, k2 u. l
    1:29位扩展ID
1 j& F. B8 W+ l- H
: B  D& u. Y0 x4 fE0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
; ~7 w2 e  o' b5 v& G    0:发送的帧是数据帧
0 V( c/ i. n. ^+ N. Q! d0 O* B  @    1:发送到的帧是遥控帧
8 z: p1 [  B, {. e. |E0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。7 e1 p9 f& ]" X- j0 n
E1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。
# y: ?5 G: O! g8 r, XE1 EFC(bit23:22):事件类型& c" z+ _% M% X6 I
    00:保留。' K4 F  e% `  f# F8 V/ O' b
    01:发送事件。
4 j3 M- y. N# U- I) e3 K2 @* G    10:发送取消。
  A" a% \, ^) M' X2 e5 g    11 :保留。: M' `+ D9 R9 g% w/ |
E1 EDL(bit21):扩展数据长度' A3 s* r1 m5 p) x% E$ [) E, _
    0:标准帧格式
! X* S+ d8 Z4 {3 y0 \+ A/ i9 s    1:FDCAN帧格式。& o$ b! }: _! Y8 F7 r6 S  ]9 S
E1 BRS(bit20):比特率切换& Z3 i7 d) o0 D6 E  X
    0:发送的帧没有比特率切换  x' b. V' _. U* `$ W, |" K

. C3 _3 @' g9 j    1:发送的帧带有比特率切换5 `- a/ J, G. o& R
E1 DLC[19:16](bit):数据长度。9 G( }4 k+ p: ~, \; m
    0-8:传统CAN+CANFD,长度为0~8字节。; K% r8 _  Y8 ~" {) J. R* V
    9-15:长度为8字节。. _) {5 \6 Z" P5 Y
E1 TXTS[15:0]bit15:0):时间戳。
5 a" _0 @; v& L' Z  Y9 o" U# D, `6 s2 S% ~( N5 h8 W3 |& i6 ~/ @/ H) }
5)、TXBC.TBSA:发送buffer,有32个元素。, v9 `, Y4 B5 a8 O9 A
' j" x- A; F: e
20191121175121493.png
2 l" {) m( F6 ~; s  E3 }3 X
0 M) H% s! k1 ]# Q# n( s& @
T0 ESI(bit31):错误状态标志. D: o& a% `/ Q6 k
    0:CANFD帧中取决于被动错误标志。( G! d- A( W" ^) x# L& ~+ x+ S. W) G- }
    1:CANFD帧中隐形错误。/ h; [2 m" M# \$ c: W
T0 XTD(bit30):标记是标准ID还是扩展ID! I# p+ d  }. E+ r: j
    0:11位标准ID
0 s3 g) y1 Y7 I9 ?6 @+ j! Q
4 ]! M; o6 L0 H    1:29位扩展ID
! V9 p2 c7 K6 h6 f- S' R
. W6 J6 i0 N3 H! [8 ~T0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。8 s7 X2 f# F! G/ h" {
    0:发送的帧是数据帧
. f5 R# {: o; y6 [" @% U4 r    1:发送到的帧是遥控帧! l# ?7 B! Q8 k8 P, L- f3 a
T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。  v) @- V1 _# c+ f1 P
T1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。' B0 m2 u! M5 |$ b" t
T1 EFC(bit23):事件FIFO控制# C: _8 f% B, O, Y; M- H
    0:不存储发送事件。
; p$ s: a# u4 f7 G9 N" o9 w    1:存储发送事件。
$ \* q% \7 ?& p( H- [T1 FDF(bit1):帧格式( c) k5 J+ C5 W% Y, _3 U3 F
    0:标准帧格式
" ^7 x" P3 f+ ]' ^- x" G) c    1:FDCAN帧格式。9 F2 m' k7 Y5 y" c- |& W
T1 BRS(bit20):比特率切换: x4 g& u" |. y! ?# z7 H
    0:发送的帧没有比特率切换2 |2 W9 @" @( i# Y2 E1 Q

* a; `# k$ A! R$ f2 H+ o) m8 O    1:发送的帧带有比特率切换# z/ j( u' g) d4 r+ i( R  Z( ^
T1 DLC[19:16](bit):数据长度。
! e1 @/ L( N, I  S1 `: H) t5 W    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。5 H) c9 l3 }4 @7 F! R
    9-15:传统CAN,接收帧长度为8字节。& q$ t) f0 t9 k1 |5 ]
    9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。; `5 H% S! r# }4 t& A2 F
T2-Tn:发送的数据。
- Q% {0 f( Z. I. J- r; L: m' i  p. f, n6 C- w! h$ e+ H7 K3 t
三、过滤器设置8 \! w( f# ~$ I  i
8 ^  c' C' C' w
        标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:* d! R2 s1 F+ m$ S
- u( y( G; B  Q! B3 _
        1、指定范围过滤$ X$ o+ j& F# Q, a
        通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:
# b8 w' `% u4 @2 F) s& o. J6 T. s! a7 J: ^4 c& Q/ a$ [
  1. SIDFC.FLSSAn.SFT=0        //范围滤波
    6 g# \" k% a( i8 o4 x, t3 A
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到RxFIFO中
    ; T6 B( a( |* S; N
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    : L0 K' W# G7 r" ^! |6 p8 D
  4. SDIFC.FLSSAn.SFID2=0X321  //ID2
复制代码
+ N9 c# u6 Q3 O# D3 ]7 C
        2、指定ID过滤1 p# s& Q) ]8 e/ w; e0 F
- C: e; G% E7 n! P* E0 t- r9 u% Y: N
        我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:
: t, |  i7 Z8 H2 ^8 J5 j6 U# @, r5 z
  1. SIDFC.FLSSAn.SFT=1        //指定D过滤
    7 t7 b" V: }7 ]) }1 U
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到Rx FIFO中# F5 d' C0 t8 I  c' K! Y3 M; m( [5 k
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    # e7 \% X7 \( @
  4. SDIFC.FLSSAn.SFID2=0X123  //ID2
复制代码

2 o- p) l9 E$ `  e        3、传统的位过滤
: [2 M& S8 n: d: v! k" M        第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:
" a7 M, t& O! X, n) r+ C) S( M2 o) |  p; s$ L/ \9 S' m: Q
  1. SIDFC.FLSSAn.SFT=2          //传统位过滤2 m' d( A* [: r+ a$ I
  2. SDIFC.FLSSAn.SFEC=1         //如果滤波匹配成功的话将消息保存到Rx FIFO中# {$ A" T  b% q0 I4 d) G  C9 E
  3. SDIFC.FL SSAn.SFID1=0xFF00  //ID1- y0 `! \/ [) F! i
  4. SDIFC.FLSSAn.SFID2=0xF00    //掩码
复制代码
7 m2 r2 }. y" ?
        其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。: z/ d. {2 O0 ^$ M) e

# z  s( E# A$ f" r, k$ t四、CAN的收发过程
1 r' j6 [: `% k7 K3 p8 Z/ ?- ~/ _. i/ N: T9 h- d
        1、CAN接收过程+ a+ w! H, Y5 d4 A# G- f8 F& p5 B

" k* H, X, J: g. A" d        CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。1 B3 }7 W' A' P

9 Z& @2 _+ J" c' n        接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。  [) \+ c) t- U& W) ]

! @9 P9 [$ Q4 ^) u0 a
20191127114550965.png
% W" o7 V  w1 _5 D" S4 R# C

: n& ~1 ?. P; t; D# ]4 g        当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。1 t9 L( v6 v+ m& Y

% ?' D& ^* S3 Z" u, U  z& C. p2 p2 i. O) }2 M. m( W% N
20191127111235302.png
! l8 m2 p0 P# H( x- S* K/ _/ m5 z
* R7 _$ D2 |$ t& E6 s
        为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。: j  b4 E  S% P( d2 o
        读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):+ C$ P; I/ o5 s; S
, d: Y8 ^+ |; F# v9 Y. N% x! r* H$ H2 y
  1. /* Calculate Rx FIFO 0 element address */# V1 Z7 a+ h. H4 k
  2. GetIndex = ((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8);
    / _2 {( {: }/ P& }
  3. RxAddress = (uint32_t *)(hfdcan->msgRam.RxFIFO0SA + (GetIndex * hfdcan->Init.RxFifo0ElmtSize * 4));
复制代码

# E* v+ k' @0 \- w7 O1 H2 K' q1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。  X% w& L! X  A
$ M' z# t5 m: q
2019112119095892.png

7 {& e9 ?- G" K0 a1 ?" J: B- b* e1 @$ n* K% P+ c" R
2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。' s$ k# f+ E7 g; X1 j8 c; Q

2 K. C& T* u& ~2 L% j, Q" X0 o: q3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同& c, s, Q6 T7 B  ^

  J( y0 d4 s/ p  @3 p$ d4 ^0 G

# X# u4 P6 m5 \  w5 V* ^
' {! k. j( U8 `' A( N* ?4 v如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。
0 e* W$ ^$ c0 ?% S: G) ]7 l; m% @$ H7 W2 a& K! j2 {( r4 N6 J
如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。+ ~7 i' P; {( X. ^

  n7 z" J# V& d9 H) P' O        计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:
0 O* z0 i/ ^! g, h; d/ o6 M& x3 C7 g7 X
  1. pRxHeader->IdType = *RxAddress & FDCAN_ELEMENT_MASK_XTD;
    2 R0 e; o7 W! a7 J" H. L* g' P1 c
  2. pRxHeader->Identifier = ((*RxAddress & FDCAN_ELEMENT_MASK_STDID) >> 18);1 l) j# l# I, Z  ~3 t
  3. pRxHeader->RxFrameType = (*RxAddress & FDCAN_ELEMENT_MASK_RTR);# @5 `8 V  r, [7 N1 H% k
  4. pRxHeader->ErrorStateIndicator = (*RxAddress++ & FDCAN_ELEMENT_MASK_ESI);  //这里地址自加了一次
    % H8 h: n$ R5 N6 ~0 K, L$ P
  5. pRxHeader->RxTimestamp = (*RxAddress & FDCAN_ELEMENT_MASK_TS);
    % _  ]$ V" H2 S9 ?3 Y
  6. pRxHeader->DataLength = (*RxAddress & FDCAN_ELEMENT_MASK_DLC);
    6 N% i. S4 l0 F3 b  |# I/ H
  7. pRxHeader->IsFilterMatchingFrame = ((*RxAddress++ & FDCAN_ELEMENT_MASK_ANMF) >> 31); //这里地址又自加了一次
    9 _; n! _. f  x0 G, g4 C/ C
  8. ......
    1 u# O4 n0 g" @6 o1 C: b
  9. pData = (uint8_t *)RxAddress;; ~1 T+ N, e& \
  10. for(ByteCounter = 0; ByteCounter < DLCtoBytes[pRxHeader->DataLength >> 16]; ByteCounter++)
    3 X5 b& x( h% z% S$ x2 J7 M
  11. {
    ' p: W! `, Q, K
  12.     *pRxData++ = *pData++;
    4 a: ?: q; J/ i4 ~# e4 ]# O7 F$ M
  13. }
复制代码

, ?2 d$ D4 g4 U2 |3 p- f* e0 @        2、CAN发送流程4 ^4 _9 u# Q* J8 M) z. H2 J+ L
6 c& \& U  c) R3 |; \
        在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:
  G" I0 U" V) ]; e% a, T2 h. `4 Y1 f
20191127150042199.png

2 O5 L. m# j- s$ {3 I$ V  q3 P1 v; d8 y5 j& t$ x2 }( r
        因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。0 o7 C- F" C5 n; t2 t) H" R

7 z/ e: s- K( b' J        下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:' d& v1 ^* I0 s) X: u& {
. T% T% l: @* R5 \5 l
        1)、专用发送buffer
) G5 i1 n0 N( Q, u8 r  p. t8 p! [* {% f- b# ?/ m9 ~( ?0 v
         专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。5 V6 g; L2 n4 i" q1 q8 h
        如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。
* J: N. N0 E1 y: M/ v& z
" t' |! Q; r& {- h, h: ~" d
20191122101555794.png
, I! p. w) J+ ]; W. H( E
$ N4 O& I- C8 A3 }7 [' \6 a
        专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:8 d) _+ d- ?) J1 r8 i2 V0 ~/ n

9 }, [5 W/ i6 ]" c1 i/ c) X发送buffer索引 (0…31) *4+发送buffer起始地址。1 r$ e% l) W8 V3 o3 N( T7 @( |* @

5 M, @3 E4 N" r' u
/ C8 d! n" H: U1 V3 E! O( V- o- I
9 F+ ?& Y* |9 k+ B$ E; S" e        2)、发送FIFO5 i; d! G2 Q7 @$ Z8 B1 z
3 u+ S& g* W. u4 P
        发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。$ H4 I; w8 L7 U! v, b
        新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。- u8 V- @# [4 R7 X7 F- f9 v0 m

  A6 _! Z) V# @2 L
20191127160804244.png
. T; H% \2 ~- y0 p
! f8 ?# Z, z5 O7 ^
        发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。
0 }9 l2 ~8 E4 F- S& a
" B& b" d, h! D; Z" D% W        如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。, y& _& O$ @! r$ v: v6 U; }
        如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。
; Q  O, h4 B- j- c        如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。5 n2 t% a1 n  A2 B' R
        发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:5 R9 `! ]* ]. F5 H* e4 @: K

' D8 V* M5 y1 f8 ?放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。
0 w( Q/ L' n- ~5 \# O6 J* X# r  C) ]$ t, U( c4 _
        3)、发送队列
% J9 d  X1 M; k' z9 [7 C" ?  N9 V  F' i        发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。
$ S, z7 K- [, i3 a2 z' c( |' R/ A        新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。6 V* r$ g* x& `( M
        应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。
/ t1 k6 N- S' I' c4 X, o; T: k+ O        发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是:) ?; S, q/ i* b: k7 L+ y4 J

& n$ h& m& c# e4 I3 C  q+ C发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。* ?9 u- G* _5 Z4 i) I" p
) g- ^+ ^4 d1 j
        因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:
5 ]3 X' G  G! n* h8 b$ a/ L" g: j% ~; q1 F
        1)、发送buffer全部配置为专用发送buffer
. j! N% r( I: A2 }: S, m# z% v
. Y2 ]$ m7 g( |        每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。
3 R3 Q, [6 y/ [$ Q7 a, i
: o1 x, g: T/ s$ _: E( T        2)、混合专用发送buffer和发送FIFO
% z+ w: P4 S0 a! n: i- ~7 }  j3 u5 s  J" ^9 b  o! s1 g
        在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。
' V5 V, O( R6 }9 b' W) P# ]& V  `( @0 s
20191127162943182.png

- L/ W5 h  M& R2 S
$ Q& c; B- `( }! J/ ~        发送优先次序:% H. L6 ~; C  a" E+ u, x) j
A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)" n1 m- [2 N& S8 X
B、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据
" A7 U0 z2 C& I" @1 ]: R& W! ?" U; i! ?+ w, L/ l; J, P
        3)、混合专用发送buffer和发送队列
  {' P9 t. N7 B+ d! J
* }' e* K* A6 J) @7 b" i4 L        在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。
8 }! W3 m* j$ l6 b
, a+ s) _* O. `2 [- ?
20191127163202212.png
7 {% }! C( x( e% v' a7 b% H

  k2 _8 m! _  Z5 Y; q( O* u/ Z" H. v        发送优先级设置:
) @- O+ o2 [- SA、扫描所有激活了发送请求的发送缓冲区: q/ c) A4 d* z6 U
B、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据, r8 F1 T4 X, N+ {
% M4 A, a  G% l" _
        再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。/ L; ^. ~' V. B# m! z( N8 o) s

% z9 c. Z0 J, ]+ ^: p. S0 z- h/ S        FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。
. R( P0 g5 Q# ~        发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。
" [  u* v, _% U        发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。
) h$ w0 S' V  P% \1 z, m
( S3 z+ A( B4 l, N% e* B        为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。: O1 }7 V% z- d  Z' K5 ]

3 ]4 _6 g! y# m3 @        CAN发送消息的代码实现:
8 s  w" U3 O$ r/ s- S+ J  f( I# I& Q" O! }
        发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:' Z1 q- ?  t2 K2 i6 x2 @' j

1 D6 K/ W! z% n! g0 }
  1. PutIndex = ((hfdcan->Instance->TXFQS & FDCAN_TXFQS_TFQPI) >> 16); //获取元素编号 ; e. O+ i, x! M% X+ M- o
  2. FDCAN_CopyMessageToRAM(hfdcan, pTxHeader, pTxData, PutIndex); //复制消息到Tx buffer 7 W, |- \8 }- n! J' V6 `
  3. hfdcan->Instance->TXBAR = (1 << PutIndex); //发出传输请求
复制代码
% `& V$ @8 O$ [6 ], D
        发送buffer请求寄存器FDCAN_TXBAR,描述为:
  l; F, K- O# p2 G+ U  N9 `6 T6 k) |
7 ]- R8 y4 y" Q0 Q
  l7 X, }4 w# T, \( K. _
        此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。
; e1 C$ `3 f4 v; D
1 Y0 O/ V3 ]8 L, T+ }. m) b
5 H7 W7 z" z& `. f/ @
5 Q. e; j! E0 J, z五、初始化
! E6 r# \  o7 ?) z0 C8 ^+ K
- I2 b. ^$ s( T  D& V5 D3 a% x        初始化可以直接使用HAL库,, e5 p# ~6 O5 T
% ?- @) P5 Z- Q1 n; w( B$ M$ u4 Z. f
  1. u8 FDCAN1_Mode_Init(void)
    8 I0 `7 {) v* p+ W9 W
  2. {
    . b; ]. ~. u- R2 ^- {
  3.     FDCAN_FilterTypeDef FDCAN1_RXFilter;: }1 |6 W2 S  P/ c! e3 W
  4. ! Z/ \9 ^$ ]/ f$ K6 N! b, L0 D! `
  5.     HAL_FDCAN_DeInit(&FDCAN1_Handler);                              //先清除以前的设置
    / i; c  L8 Q) O$ s
  6.     FDCAN1_Handler.Instance=FDCAN1;
    0 M0 h3 L' h0 ^6 [2 q7 p
  7.     FDCAN1_Handler.Init.FrameFormat=FDCAN_FRAME_CLASSIC;            //传统模式1 I  v  Q4 f$ ?2 E9 }  _- i
  8.     FDCAN1_Handler.Init.Mode=FDCAN_MODE_NORMAL;                     //正常模式
    % j6 v) w8 O! Y, s% U% a
  9.     FDCAN1_Handler.Init.AutoRetransmission=DISABLE;                 //关闭自动重传9 w- n$ s! e2 {3 l/ K- h
  10.     FDCAN1_Handler.Init.TransmitPause=DISABLE;                      //关闭传输暂停           
    - [2 y- ?7 J8 x  B# B
  11.     FDCAN1_Handler.Init.ProtocolException=DISABLE;                  //关闭协议异常处理
    7 R, m& m9 E0 E1 [" o
  12.         6 @  B2 Q# E! ?' P' D1 r
  13.         //时钟为200M,baudrate=200M/(NominalTimeSeg1+NominalTimeSeg2+1)/NominalPrescaler,这里配置为1M
    3 Y, ^8 H( \0 @: `
  14.     FDCAN1_Handler.Init.NominalPrescaler=10;                        //分频系数
    0 D$ x# d% L# c& ?4 ]4 {9 T  e$ F9 g
  15.     FDCAN1_Handler.Init.NominalSyncJumpWidth=8;                     //重新同步跳跃宽度
    % @$ l6 D5 a" k/ f9 P  ]7 C+ G
  16.     FDCAN1_Handler.Init.NominalTimeSeg1=11;                         //tsg1范围:2~2567 W3 k" Z. q0 T' p" j8 E( E& O
  17.     FDCAN1_Handler.Init.NominalTimeSeg2=8;                          //tsg2范围:2~1283 ^% [$ |& Y# U& [8 {: t$ A
  18.         0 B+ [% f3 N0 ?. P. l! e8 M" s
  19.     FDCAN1_Handler.Init.MessageRAMOffset=0;                         //信息RAM偏移,10KB消息RAM共有2560字,故可以偏移0~2560
    " [" a1 u. [: r
  20.         //使用了多少个滤波器就要设置为多少0 h+ z& Z9 `' n
  21.     FDCAN1_Handler.Init.StdFiltersNbr=3;                            //标准帧滤波器个数,0~128
    3 z9 \5 N  x; p2 V# w& K4 A
  22.     FDCAN1_Handler.Init.ExtFiltersNbr=2;                            //扩展帧滤波器个数,0~64- [- ~5 G( O1 C. ^' E" j" G) h  z
  23.         
    1 s5 v& i" D, b. x' g( {5 {  K
  24.         //接收FIFO0、FIFO1和buffer配置,此处没有使用FIFO1故个数设置为0
    - a9 ?/ ]3 ~3 u+ [$ B
  25.     FDCAN1_Handler.Init.RxFifo0ElmtsNbr=64;                         //设置接收FIFO0元素个数,0-64
    9 J, ^6 j9 E" G4 P# [, A
  26.     FDCAN1_Handler.Init.RxFifo0ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO0元素的数据域大小:8字节        
    ) m9 l, c5 {% g) _: z- t
  27.     FDCAN1_Handler.Init.RxFifo1ElmtsNbr=0;                          //设置接收FIFO1元素个数,0-64
    # }1 c1 O5 k9 i4 |5 L( @8 e6 ?
  28.     FDCAN1_Handler.Init.RxFifo1ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO1元素的数据域大小:8字节                  o) V: R! q$ J0 |
  29.     FDCAN1_Handler.Init.RxBuffersNbr=64;                            //接收buffer元素个数,0~64
    - r/ c/ X" G: u! m$ ?
  30.         FDCAN1_Handler.Init.RxBufferSize=FDCAN_DATA_BYTES_8;            //接收buffer元素的数据域大小:8字节        
    $ `, G; i0 s$ l- o7 g; }$ s! ~
  31.         
    ( @0 D+ R7 D( E7 j) A
  32.         //没有使用发送事件FIFO功能,故TxEventsNbr设置为0。把发送buffer全部作为专用发送buffer使用,故TxFifoQueueElmtsNbr设为0.
    * J4 R% j! ^. p4 c
  33.     FDCAN1_Handler.Init.TxEventsNbr=0;                              //发送事件FIFO元素个数,0~32& }7 c  O" O; U; g9 f
  34.     FDCAN1_Handler.Init.TxBuffersNbr=32;                            //发送buffer元素个数,0~327 P: M* u% D$ @2 c8 z) o5 }
  35.     FDCAN1_Handler.Init.TxFifoQueueElmtsNbr=0;                      //发送Buffer被用作发送FIFO/队列的元素个数,0~32* Y( H) s  P) j) A- o2 u9 H, Y. i
  36.     FDCAN1_Handler.Init.TxFifoQueueMode=FDCAN_TX_FIFO_OPERATION;    //发送FIFO模式选择,可以选择FIFO模式或队列模式. c, C; d1 @8 C3 z' Y( O
  37.     FDCAN1_Handler.Init.TxElmtSize=FDCAN_DATA_BYTES_8;              //发送元素的数据域大小:8字节
    ! h' Y1 R$ m5 w& U% |! ]
  38.         
    1 [/ @" e' h7 u
  39.     if(HAL_FDCAN_Init(&FDCAN1_Handler)!=HAL_OK) return 1;          //初始化FDCAN2 V* X3 @! m6 _6 `  v  v
  40. 5 C$ V0 Z; ?- I! M, W
  41.     //配置RX滤波器,标准帧   
    6 O3 [( A. m4 G* ?$ e5 L7 v% B
  42.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID: A& x+ l, ~4 W! Z
  43.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
    + P+ e! K9 B7 _& W1 p$ L, l
  44.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    9 ~2 @% N$ \7 i: G5 i7 J3 r* |! N4 p
  45.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  1 I4 @8 k( a+ t; d  `/ J2 s* s$ l
  46.     FDCAN1_RXFilter.FilterID1=0x112;                                //11位ID8 o# z. G& H8 R. D9 A/ L$ r
  47.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码+ N6 ]$ ?  I2 \- J
  48.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化8 V# s  k8 @, ^! G5 f! e9 p

  49. ( H) k' K, o# b& e
  50.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    / ]) b$ R1 M. P8 \  \
  51.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                  
    & }+ @& ^1 E3 A# |
  52.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    4 z* m) ~- ]6 a0 d$ ]
  53.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  0 T, y7 X. C$ Z1 `4 p
  54.     FDCAN1_RXFilter.FilterID1=0x113;                                //11位ID) X1 m8 L2 k) }: @9 d' V& c
  55.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码! R7 ]2 ~0 e6 i- Q/ t/ ]
  56.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化/ l1 ^7 w) a" z. R
  57.         & M$ q, i4 S; }4 L0 E
  58.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID: U0 W' M% D, ]* `+ P5 H
  59.     FDCAN1_RXFilter.FilterIndex=2;                                  //滤波器索引                   # L* t' S, Z3 m- F" c3 l
  60.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    ! G& @& L. z; t2 A9 g* Y
  61.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    0 ]+ ~4 Y) [8 [3 F+ g
  62.     FDCAN1_RXFilter.FilterID1=0x114;                                //11位ID2 |" n- J! F& }
  63.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码3 u" H. N" k0 M; g! ?/ z. j$ \: y
  64.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化$ i# G! W9 s$ j. Q# H5 N
  65.         
    + }( y+ \' H7 c4 g
  66.         //配置RX滤波器,扩展帧。标准帧和扩展帧的滤波器索引是分开的   ! I6 f5 c: D2 W  W3 v2 q! I
  67.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID. j* R+ E  w# f9 m" [, z# H
  68.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                   1 m% t6 b$ `/ M
  69.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型: t- o; q" o4 U9 u0 A: T. I: E! [
  70.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    . j/ I+ h, `+ S( h  T! |1 W
  71.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(2 << 12);                  //32位ID# w, J& J' T! K' U9 a6 J
  72.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码
    6 j; a/ g( E" |) `
  73.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    / p- {, A2 X* u+ i
  74. 5 @% H( I( h( o' X4 @4 [1 C2 k/ p0 E7 ]4 \
  75.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID( M9 Y8 q! y; R. V$ C7 a
  76.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                  
    6 y, R1 b* o6 {' b3 W1 [
  77.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型( {  P4 Y% S! T- ^$ d+ q+ b2 ^1 ]
  78.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    3 `9 Y% M9 b; O9 x4 o
  79.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(3 << 12);                  //32位ID% U' a1 x1 t5 P# G* r
  80.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码6 s( T, R9 f- ]  @3 x
  81.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;    //滤波器初始化
    . f2 T/ i5 L$ v. Q# p( ~
  82.         % f6 J( z% w# n0 [
  83.         //滤除的消息直接丢弃7 A8 `& A3 s( ^
  84.         HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler,FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);  //设置被滤除掉的消息的处理方式: q! d9 h1 [  H9 G
  85.         
    7 F! T" t+ A# l4 @$ A
  86.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);   //使能新消息接收中断
    " ]% n0 y" H! w% V
  87.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_TX_COMPLETE,0xffffffff);   //使能消息发送中断,0xffffffff表示所有的发送buffer都触发中断        8 K0 m8 z) o. B: a9 E% G& r
  88.         
    % s; U) K* s4 E$ f4 r, u
  89.     HAL_FDCAN_Start(&FDCAN1_Handler);                               //开启FDCAN6 {- R: P4 b$ \1 ]4 M
  90.     return 0;
    8 V2 B. ?$ X+ m/ W( c+ r. t9 x
  91. }
复制代码
; e5 N0 j; Y$ g1 h
( d5 l2 R% Y9 _; b
六、CAN发送示例(来自正点原子)
" F. F$ N; s" u6 ?9 t. H) Z
2 D+ a4 s8 \  G/ t! M$ q
  1. //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)        
    # `: h+ D6 y" P  q* {
  2. //len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8                                     0 N# k' L, ?3 ^' G3 i
  3. //msg:数据指针,最大为8个字节.
    ) ?& m, K/ M3 E5 h  Y
  4. //返回值:0,成功;
    - \5 v  l0 `) b: z
  5. //                 其他,失败;& M. C# J# D6 o; h
  6. u8 FDCAN1_Send_Msg(u8* msg,u32 len)5 A2 _- ?4 L5 C# A; |. ]3 ]
  7. {        . d& ]2 `1 u9 Z: o' b+ H
  8.     FDCAN1_TxHeader.Identifier=0x12;                           //32位ID
    # j; Y9 D% }$ ?8 L1 p. ?
  9.     FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID;                  //标准ID
    " z, k% I3 Y( H+ ]$ r! u
  10.     FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME;              //数据帧
    ) L$ u: \4 x. M, W
  11.     FDCAN1_TxHeader.DataLength=len;                            //数据长度0 @( e, V; w( K7 a0 @: C6 E
  12.     FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;            , _# C. q# m3 j" {8 v
  13.     FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF;               //关闭速率切换* j" H- d- e1 \, ?
  14.     FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN;                //传统的CAN模式: s/ J. p6 o* c9 n
  15.     FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS;     //无发送事件
    $ R+ u1 _$ T/ Q) c8 A$ X, P
  16.     FDCAN1_TxHeader.MessageMarker=0;                           
    & {- W8 h3 W  d+ H

  17. & ], q) k7 x6 w6 ]  Q
  18.     if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送
    " y; r! o" i1 N; A: H
  19.     return 0;        ! I4 f6 C$ F1 }% a; s
  20. }
复制代码

3 t# G$ m7 N' \) p8 M- r七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)1 m3 x/ f- @5 v" s9 e
7 h0 A- P5 c: u+ ~5 B& X
  1. //can口接收数据查询
    5 J& p" M# X) X- n
  2. //buf:数据缓存区;           B, ?. s) t* ]( \
  3. //返回值:0,无数据被收到;
    7 L3 n3 o/ S& x) a+ F" a
  4. //                 其他,接收的数据长度;: o2 X4 V. n, f" h
  5. u8 FDCAN1_Receive_Msg(u8 *buf)
    1 _4 y  {  W# S/ X7 t
  6. {        
    8 }; W" e+ H/ F( c5 O1 F
  7.     if(HAL_FDCAN_GetRxMessage(&FDCAN1_Handler,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf)!=HAL_OK)return 0;//接收数据  k8 X! v  h3 n; Q* T% V
  8.         return FDCAN1_RxHeader.DataLength>>16;        
    : d. P5 t$ `! ~3 n1 }, E3 M0 K
  9. }
复制代码
5 U9 [8 e; P' d- n
参考:《正点原子》  STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准)
1 }7 v* G; W- L0 c: u! {. D/ b" F. ?3 n

% l/ O# o" v5 x4 w) M
收藏 评论0 发布时间:2021-12-22 14:00

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版