请选择 进入手机版 | 继续访问电脑版

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

【经验分享】STM32H7的FDCAN

[复制链接]
STMCU小助手 发布时间:2021-12-22 14:00
一、介绍6 x+ I1 ^# v8 F; @; O. Q
5 n/ V1 F0 H! h- w5 ]
        FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:
6 I" s$ z/ |2 `$ v3 A, j8 i! V( L% v9 J! ^
       1、每帧数据段最大长度由8字节上升到64字节。
  V  P0 Q8 R# l2 f! e) G
# P9 {* ^4 S/ a) T  ^, I        2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。
2 r8 I2 @8 K; N/ J6 p; E" j4 R
/ F+ u1 E' B3 m" C4 |% N3 G        3、向下兼容CAN。
5 E; [4 Q7 i' C; E/ V; W6 Z5 g# L& X; D
        H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。
1 R" ~; _  ]6 x2 {0 @8 ]7 \2 Y( E# F: @
二、结构
5 M$ |; X% P* B7 f$ F3 l7 @! P+ O( C: Z/ z
        先看结构框图:( |* h: C0 M- e+ p
7 q' L5 `9 E; a2 K1 L" Z
20191121162856859.png

4 o5 D9 x5 v! m
4 c! N2 w2 M- k' E, T1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。) P6 X1 V& q7 G3 R; d
: ^5 o6 b- N$ R7 ^9 w, B
20191127102816991.png
3 p2 r& ~& c& {7 [& p3 I; i- r% z) z* k

9 a2 \7 \# v# q) T        可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。
% X8 p) ?2 o  `) r% _8 b, x, y; \' I1 r1 f$ S8 u4 d" Y9 D! ]
2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。5 C/ ~4 I' o2 U+ S& ]" \* R" L
- v6 [! _" E4 i$ T, _, H" x
3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。0 G. J+ m- L& D, i+ T

% ^* L/ T2 W" _( H' w4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。
* |3 y  o5 Q4 I& f: T' n
2 a- ^/ x- t' G5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:
0 v2 N7 A1 P; N: l6 I+ I+ Z8 |2 o# w( W& z5 k8 E, H
20191121164211703.png

6 S) _7 F8 ^! c" P6 _9 G* g
6 k" V& j; _8 x# r# O+ U. K        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字。( l6 L! b& V7 v! |; n1 u0 I
" j7 H' h# \, L4 {. b2 v
        1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。, U! F8 t$ z, B$ u9 K' |' J/ T! f

; b# W3 w. T5 N        2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)
4 q( y& o- S4 V# W' V1 {6 |
. c3 _/ n0 k5 l3 a, v        3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。
  ]( P; a: Y* N5 q0 ^! ?8 V3 `# u  {! H5 Q( U
        4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。
4 z7 T7 j2 m- V- E% [; |" c
: r. y, X3 d. \) r& l' j7 w        5)、RXBC.RBSA:接收buffer,最多可以有64个元素。, K. z' C" n! E$ X) @3 n% P9 H

( z% D. b% [5 j7 G1 p        6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)1 @: y/ V+ s% ~  E/ w

7 b" C; f% q! u( d        7)、TXBC.TBSA:发送buffer,最多可以有32个元素。
' S8 ]1 J; J+ h) c9 r2 u& l* a( g" v9 i0 C3 M  h
        8)、TMC.TMSA:触发存储器,最多可以有64个元素。' K) r: V6 r+ i, I4 Q! a9 V' e' A
* }; M& v( ?/ x9 w/ E
《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》
# y. b: c! {* f' C6 ^1 T+ G" \0 [, u3 M4 c* k" G: ^. W! @1 Y
        下面来详细介绍这几个功能区域:
) j) `  v. K3 `1 p5 `) B6 ~
6 {, T5 z4 r8 y: r  x* X, s1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:
7 v3 m: q$ R7 k
1 \* k5 S& w' ~: m: A8 P
20191121170625565.png

5 F6 D7 [) j; T4 ]9 Y. ]
( D5 R1 V, @& }5 K: cSFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:2 v- i& f  x! [6 \
    00:范围过滤,过滤到SFID1到SFID2中的所有信息。
5 G/ _0 N, }* r& V    01:双过滤,过滤到SFID1和SFID2中的信息。
" U4 r( H, }' z: {7 i9 A7 z2 s    10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。
; B+ r- Z/ g+ e7 A0 H0 {    11:禁止过滤器元素
2 S3 k  P: X4 Z0 RSFEC[2:0](bit29:27):标准过滤配置; e* G% k- C3 w# B* a  }8 c' c
    000:禁止过滤器元素1 u6 i1 S2 W' S# Q% G$ e. \* `# t& ^
    001:当过滤匹配以后存储在Rx FIFO0中。
( ]* O% }* r9 F! y    010:当过滤匹配以后存储在Rx FIFO1中。) x+ ]$ ~  l1 o" B
    011:当过滤匹配以后丢弃。
" p* e% t- f: t: ?# r' n1 Z+ D    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。- c% l) {( C6 a6 O
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
1 T# p. o. T: {: j+ k2 z    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。! m; t% p( U2 o% B( P& j
    111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。
' _2 v8 L; D% Q; M- K- i! ?SFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。* \1 G0 ~' `" [* I3 G& f( [; n
SFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。
  o/ p2 |" f; D/ E: t  A9 |: V1 j" E/ {; q4 p5 i. C
SFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。% H( m- z- B* G; j+ w
    00:将消息存储在Rx Buffer中。
0 I! U' l3 D9 }8 ~" m0 G7 k6 R3 h$ z6 g1 J
    01:debug消息A。   
; t$ \# r( ]/ W/ u; G( Z% z7 l+ L! d& s# ?0 l7 X2 G
    10:debug消息B。
4 v) x' r2 M$ Q& g+ f- |) M/ \( I2 {% T) {2 D) o
    11:debug消息C。
5 b% f/ K  ~3 w, s0 V
" p$ F1 u( _0 _. rSFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。- w4 h" [, w+ n9 l* p

# @' W5 e' L3 o9 ]" KSFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。
9 X, }! V- z5 \, ~$ w, N; c' m- I& ~- d. [& ]- }( Y, @9 R5 j% B
2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:( Z& j& [8 J: E' z
5 j' N0 g' l3 g* `6 f( P
20191121171836710.png

+ X" L5 e: l# D3 I& o
+ \' ^, L, v9 c. e5 E, L& e( qF0 EFEC[2:0](bit31:29):扩展过滤配置' q) R" m1 k  L+ Z* ?
    000:禁止过滤器元素
8 L5 D4 B  ^* f. b: ]3 q    001:当过滤匹配以后存储在Rx FIFO0中。5 T# B' r  ]$ V6 Z
    010:当过滤匹配以后存储在Rx FIFO1中3 s$ R+ ~8 g3 j5 |$ J8 B  G7 P" `: a' E
    011:当过滤匹配以后丢弃。$ ^1 d% S, V+ ^$ E! M; y
    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。7 w" Q9 y1 D- ^! C+ V) ?  s( ~
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
9 T* C4 Q8 {3 [5 k/ Z% _- H7 P    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
3 r/ _' q( W! @2 l, c; f' ^    111:存储到Rx buffer中,EFT[1:0]忽略。: u/ w( |! i" y& m9 O7 ?6 d3 R& b
F0 EFID1[28:0](bit28:0):扩展过滤ID1。
4 q! ]" x$ R, d4 d; UF1 EFTI[1:0](bit31:30):扩展过滤类型$ {$ I, o- N; \
    00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。$ [- k4 S2 D6 q
    01:双过滤,过滤到EF1ID和EF2ID中的信息。+ B/ a& t# u8 L. |3 {" V0 L; w, Q
    10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。
# D  I/ J5 A" _+ q    11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码2 h- N" r0 h4 e! i8 \+ _+ ?
F1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。
% n+ K; q+ e3 P    00将消息存储在Rx Buffer中。7 H7 d, S5 ?; C; M% z
    01:debug消息A。
. R3 A. _) t7 M5 X4 Z! u5 b6 o    10:debug消息B。
" ], F) a; r1 z% z8 b' ]0 x    11:debug消息C.: n0 U  x0 F- d
F1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。
, K3 c7 M8 K* yF1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。
  B! z$ ~1 P3 l( d& f5 L2 O8 x9 w; }) r% \. N+ R
3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:
1 P% u/ B! k* G* q1 g
; J8 }: Y) f) [6 l& F( ^1 M5 Y
20191121172451477.png

0 l- Y8 J' R0 i9 d: ]& _  `- Z/ n! ^
R0 ESI(bit31):错误状态标志8 v7 p3 Y0 m  @' c
    0:传输节点显性错误。* t' n+ |) S& X0 q
    1:传输节点隐形错误
8 ]. i' n/ A1 f8 X/ fR0 XTD(bit30):标记是标准ID还是扩展ID' Y, ]6 G, x; {3 K$ s) S
    0:11位标准ID7 o% X. v( f$ s

1 `9 j& F3 k( y8 Q' e8 d" W    1:29位扩展ID9 A' B$ M+ I% [
# y( }4 {( P# P! l5 z, v
R0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
* h/ L5 f# R/ p% T1 g/ k3 c- H5 F    0:接收到的帧是数据帧$ ]4 d: |$ @) [' o/ s
  m: r7 H" M& @
    1:接收到的帧是遥控帧
, t/ a! Z* R9 F3 bR0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。, ]9 [  b3 ^, q5 C% e- K
R1 ANMF(bit31):7 w9 r& E7 d$ ]1 F7 I

# h2 |" Z3 O5 j9 b! o    0:接收到的帧和FIDX中的过滤帧匹配
7 W5 E2 F+ X! X; y3 B# h4 `$ r/ n
    1:接收到的帧和Rx滤波器中的任何元素都不匹配# T7 v8 _. ^  [. P# c+ }" g/ y3 x. ^
R1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。
; Z8 P& Y+ u' XR1 FDF(bit21):帧格式+ X8 V- ^, F/ {+ v1 p
    0:标准帧格式
! ?; a: ^& L; B# Y    1:FDCAN帧格式。
$ E$ a  o7 ^% LR1 BRS(bit20):比特率切换
9 p6 V( V0 x/ X$ p- s/ \4 g5 s; V, R/ F6 D    0:接收到的帧没有比特率切换8 U: b5 X- e! s% D9 ~

% W  a+ @7 S9 T' g& b7 J. c4 V    1:接收到的帧带有比特率切换: l  B. k) x+ E+ w3 U( }
R1 DLC[3:0](bit):数据长度。* Q8 x3 k) \/ f4 x7 t3 b
    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。6 b5 T+ H& @8 q8 J- \
    9-15:如果是传统CAN,接收帧长度为8字节。* y! O9 b% [( m6 f+ P- e& c
    9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。
3 ~6 _' R6 F+ m( Y% \R1 RXTS[15:0](bit15:0):接收时间戳。0 r$ S0 {$ I2 l" \- v
R2-Rn:接收到的数据。
8 R* I: F* A# G9 u# c8 i3 B( B" O. \+ Y) W8 ?. Y$ L8 {% L6 n
4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。
, U; H8 f5 `! L9 @0 W4 K0 V, J6 I1 e% i3 ^/ |8 `; ^0 r# c, N
20191121174147402.png
* H0 c, [4 g, w- j
/ _0 ^- u9 ]- Y% j6 u! E4 z2 }8 l
E0 ESI (bit31): 错误状态标志
( n7 U( Z5 k! F% A7 I9 N8 [% {) R
. _; G, o0 G. L% D6 ^3 h    0:传输节点显性错误
& P6 y3 ]+ \4 }+ N* Y" m* X    1:传输节点隐形错误。
7 ~0 d# [; i# O0 P% F/ Y- q+ kE0 XTD(bit30):标记是标准D还是扩展ID
# |  k6 E& a; |4 V5 [0 h" g. V    0:11位标准ID.
: z9 P: k, H* X9 ?    1:29位扩展ID
! k" Q% I9 w! |; c7 r" P- M! t2 G/ y4 D- S
E0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。7 P3 j# ^" j# l0 \( m
    0:发送的帧是数据帧
9 O: y5 u& ^, }/ {( N: n    1:发送到的帧是遥控帧
$ F1 Q  a6 z) n! F) Q! qE0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。
- |# W6 g( {" d+ S3 Y- ^- m$ b5 GE1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。
3 h8 T  s( R- M9 n! A" T8 f: }  {E1 EFC(bit23:22):事件类型
# J/ }( i, }2 J. a  m5 V5 k. Y    00:保留。: {* E& w* @0 ?9 _& D
    01:发送事件。
1 Y0 S5 e# _$ `    10:发送取消。( i# i0 E3 H0 L+ H, ~6 w. k
    11 :保留。
/ F* ]( @# F3 P; V6 ?) WE1 EDL(bit21):扩展数据长度
: ~* {: W9 ~' P2 B/ {    0:标准帧格式- U& N* p( m7 v4 u
    1:FDCAN帧格式。6 ?, B( g+ i1 u) y  R3 U, ~
E1 BRS(bit20):比特率切换$ j9 E. k2 H2 x. v" H
    0:发送的帧没有比特率切换3 M' d; V% Q' S# r
9 F3 x! U; V' i( p
    1:发送的帧带有比特率切换
1 ^% S3 ]. m- |+ f8 jE1 DLC[19:16](bit):数据长度。' e, |  g: K: p, x
    0-8:传统CAN+CANFD,长度为0~8字节。
" U1 m9 F9 L# O" X6 r/ i    9-15:长度为8字节。
' Q) D/ @$ Q4 Q' ]+ wE1 TXTS[15:0]bit15:0):时间戳。
0 a: L7 M$ H! ]6 H8 h  B4 C* N0 L4 [
5)、TXBC.TBSA:发送buffer,有32个元素。
; C# w- ?) C1 ?7 s3 T; w3 \
; j# T! |9 Y, p7 ^; h& `
20191121175121493.png

  w3 m- ?9 \9 |
/ ?4 L; K% P; I: C( AT0 ESI(bit31):错误状态标志
% c5 f3 g; b9 D, Z    0:CANFD帧中取决于被动错误标志。
! }* B- }0 o% K2 z' S- f    1:CANFD帧中隐形错误。
3 m) w! I( t. kT0 XTD(bit30):标记是标准ID还是扩展ID/ ]' \5 p& Z  W! [- w7 w6 k" L
    0:11位标准ID; U& [8 l" z( s
! y1 G# G  ]4 F0 A: }! N
    1:29位扩展ID- p) i, Y% {+ e

0 y9 M) h0 c3 Z( rT0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
% U' `6 f2 {+ l( w  m- T    0:发送的帧是数据帧
" s5 n) B! e, L+ u+ J: i# J    1:发送到的帧是遥控帧! W" C( p1 E6 B4 @' s
T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。; Z. X, G; y( |9 {7 H
T1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。- e5 m# J2 [9 b
T1 EFC(bit23):事件FIFO控制  H3 j6 m0 J1 D, C9 @6 L
    0:不存储发送事件。
% S+ _/ o4 {7 T: N0 \) b    1:存储发送事件。
, ^' R$ U' ?1 K; g5 I, [7 cT1 FDF(bit1):帧格式
& @6 Y8 f8 a0 C- ]    0:标准帧格式
0 [- Q, @: U# d1 [0 M    1:FDCAN帧格式。
( t, ~% a- q  {% d. NT1 BRS(bit20):比特率切换4 k/ [( ]0 q3 S1 C5 b, M
    0:发送的帧没有比特率切换
7 B) j* Q) F* y" O  w1 v
5 j9 R, ~7 V/ V. z8 y4 H    1:发送的帧带有比特率切换- \1 ?% f, x2 l6 s- Y
T1 DLC[19:16](bit):数据长度。6 |, ^% C7 r, Q& D! I, G
    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。
3 Q4 h6 C! J9 ], r: J9 @/ |    9-15:传统CAN,接收帧长度为8字节。
5 d0 o; E) t& s( [) u( U( B    9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。& W: T/ ?/ f& X6 E# A5 M! ^/ V
T2-Tn:发送的数据。$ X) U' n# G4 [
" [9 b. T! Z) A. }& N+ b. w+ j
三、过滤器设置
0 u! k. Y0 ]* B- A# t. ]4 i# w# F8 H' y) S
        标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:! P4 g! J3 C& j" V, Z
5 O2 K- c. n5 B. F( g
        1、指定范围过滤
2 @1 Y- a3 y) v9 r7 l" }        通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:
( k* W: w' ~, H( E4 q
6 s3 r2 {8 j/ M  J
  1. SIDFC.FLSSAn.SFT=0        //范围滤波
    * k5 L( l  w6 \" h; B2 B& M
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到RxFIFO中
    # y7 E+ m4 b6 L" r+ Y" E0 u
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1# F5 l2 {% X" P& V
  4. SDIFC.FLSSAn.SFID2=0X321  //ID2
复制代码
) v6 i- Q( B7 o/ C0 @2 o, {. Q
        2、指定ID过滤
, j. M' U  `9 Z; V( ~% e3 R' Y$ x( Q, j* i/ ^% m
        我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:4 |& w! u' b3 z5 [+ E

1 U' b$ e2 }) }8 [
  1. SIDFC.FLSSAn.SFT=1        //指定D过滤
    & m- u1 [- `9 a( p
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到Rx FIFO中2 ?2 G1 O) f, h
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1" j# j4 R5 |3 S0 @3 Y
  4. SDIFC.FLSSAn.SFID2=0X123  //ID2
复制代码
( Y: J# w6 p7 g( R
        3、传统的位过滤; N3 m: d" p* e9 }# f
        第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:+ c4 N, v5 D4 R- V! u8 r" B$ O
6 R2 N4 B% S5 F
  1. SIDFC.FLSSAn.SFT=2          //传统位过滤8 g7 v5 `5 _2 w- i; t! h/ o
  2. SDIFC.FLSSAn.SFEC=1         //如果滤波匹配成功的话将消息保存到Rx FIFO中9 U( l* @9 W' w, q
  3. SDIFC.FL SSAn.SFID1=0xFF00  //ID1
    * }! x9 N3 Q0 J, ]
  4. SDIFC.FLSSAn.SFID2=0xF00    //掩码
复制代码

9 R; i: e. ~$ a. C7 o% D        其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。) E8 U' C, z- a

' x2 z/ ]" \' y# ^* w2 D四、CAN的收发过程7 P( t) ]8 ~! o5 Q

' k9 W* O9 Y% }: y7 H        1、CAN接收过程
7 d/ ~8 m6 \- F' e
$ Z2 G( R% I) d        CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。
/ V2 x, d- v" q
2 G, v, A8 V4 q3 a" ~' M        接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。) n% @9 ~5 M1 V) \" v
: ]" |- b3 y- r6 j6 B  Q
20191127114550965.png
3 r" b- ~( V6 t3 x* N" ^8 O

" c) W' m3 c/ k" a7 c& a! W        当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。1 h+ q) ^: o# ^: M: r. R/ @
) e% o+ T' w' ^2 N$ P: K& W
0 [, ~1 o  }1 ]$ t4 T' W/ N
20191127111235302.png
  F" {- P! U* j) p; t: ?
! X3 q7 ]% |" i) x% W6 b. s! J, W
        为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。, O# p/ w% A, v4 \4 b+ {' B
        读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):
( ?, r" b' X$ ~% e9 x, k/ D2 X+ g/ J( u6 T' S! l% P: U% f  l
  1. /* Calculate Rx FIFO 0 element address */
    0 h8 \2 {8 r5 N5 [4 r, D! U
  2. GetIndex = ((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8);
    $ f9 i6 D) j8 W# G
  3. RxAddress = (uint32_t *)(hfdcan->msgRam.RxFIFO0SA + (GetIndex * hfdcan->Init.RxFifo0ElmtSize * 4));
复制代码

& p0 u1 ~1 P. c4 W3 W- O9 [1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。
7 J/ T; B: r3 U8 P& O, A. M8 a7 Q- b1 c' G( i
2019112119095892.png

3 N. y  _8 w4 g0 X- S/ @9 h7 q/ _0 L+ V+ q9 C
2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。2 Q. ?7 L; d3 n; D

2 J* U! r, e: ?7 z3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同
. x5 I0 E% ~1 C) J! m! Z5 j( H4 z6 y* n; L2 j: T( R2 ?
* g6 b  }& ~* X8 r3 _7 q1 |4 {

. ~$ I' o+ ]; W如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。' R4 X7 V' \7 U

/ e3 H) b  X; m! e如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。) ?4 u7 ~% J" U

% K# [# ?1 k3 U, A        计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:& K! ?: W% y# C) g6 U! w$ A+ o
; j1 V0 M; ?7 D+ U; M' V
  1. pRxHeader->IdType = *RxAddress & FDCAN_ELEMENT_MASK_XTD;
    ; m5 w% F7 c, T0 N" j7 ]! U) H6 P
  2. pRxHeader->Identifier = ((*RxAddress & FDCAN_ELEMENT_MASK_STDID) >> 18);
    $ S; @8 I4 a( `& c; N
  3. pRxHeader->RxFrameType = (*RxAddress & FDCAN_ELEMENT_MASK_RTR);
    / U2 H( t) @7 e; x$ k5 E% e
  4. pRxHeader->ErrorStateIndicator = (*RxAddress++ & FDCAN_ELEMENT_MASK_ESI);  //这里地址自加了一次
    - P/ K8 v  U+ j
  5. pRxHeader->RxTimestamp = (*RxAddress & FDCAN_ELEMENT_MASK_TS);" E2 ]/ f* I8 r
  6. pRxHeader->DataLength = (*RxAddress & FDCAN_ELEMENT_MASK_DLC);
    ' R3 t# \% s3 o  O7 G8 _7 m- I
  7. pRxHeader->IsFilterMatchingFrame = ((*RxAddress++ & FDCAN_ELEMENT_MASK_ANMF) >> 31); //这里地址又自加了一次
    7 w4 O2 `, }3 c9 j8 p7 R( L  {
  8. ......+ z4 P" ]) ^. |! P' ]0 V. L
  9. pData = (uint8_t *)RxAddress;
    : C& y6 i% ^+ s" H) v; @
  10. for(ByteCounter = 0; ByteCounter < DLCtoBytes[pRxHeader->DataLength >> 16]; ByteCounter++)7 [6 k1 R. Z7 j0 Q
  11. {) i5 V+ e4 Y4 S8 m' M7 e: |
  12.     *pRxData++ = *pData++;
    5 T2 s. u, C! ^9 Y
  13. }
复制代码

; w' S% z' u  W5 v3 L- Z' S/ _2 Q        2、CAN发送流程
1 B% e8 W, y+ `5 o' f6 V
  q+ O  K5 z4 m/ S0 \        在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:2 e) ?$ Z" g) A" Y+ g: I1 D9 A
1 o: v. j& O4 e7 {1 {
20191127150042199.png

  `" d( {- W6 x9 t1 k. _
2 [2 Z: N" s( H4 Y        因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。
( W' ?5 H# a, l' `4 p  X0 p* `& \
        下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:
% w5 Z% ^+ y5 U% x) {) ?9 v8 V2 V3 K( v
        1)、专用发送buffer
4 v; K6 T5 r4 _  K( e2 u! k  x0 c% }4 U. L( f) e
         专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。5 q7 ?$ d2 z; H( m) }7 Q# a5 _# {
        如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。# ^; P- ^* Y3 T) E% x; l2 n
' l5 O' D: r. x. _/ d; D
20191122101555794.png

5 I* j8 E4 g- P/ A' ]) k. r! X0 {8 }
        专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:4 f; s- x3 `, [% j
$ q& ~2 W- P) c
发送buffer索引 (0…31) *4+发送buffer起始地址。
+ d7 U0 E0 B+ K8 p7 o" L) R: R% E/ Y( K: u% @. C; z/ p4 `
/ U. x; _) j8 n3 B, c
* M% t* F1 D7 R& u' E- H8 G
        2)、发送FIFO
& Q, n6 A& Q4 d) n" F% C6 Q2 k
8 I4 l2 _7 F) q  G        发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。' A. E' k7 c! q4 Z, `6 a5 {% k
        新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。5 ], ]; O  \% X

$ B& ^, c( F& y) A% u
20191127160804244.png
+ R" a6 s1 U3 `$ h
% t- m( C3 f; B4 y: p6 p9 v$ w
        发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。
: O# x+ |, g6 t. s5 F4 k9 L& H" D' p# z1 e
        如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。' x; U- Z- f* N- o, l. o
        如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。5 _2 N" b2 s* }5 }% c
        如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。# ^2 w4 p' m6 U' J3 S/ p
        发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:* ]! K# }- x( V8 B9 F) p2 y

) g5 t! p, G& r2 s' i% D& i放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。) _( \. e: g5 N8 T; w% Q

7 e" X" m! W* Z) t        3)、发送队列0 g: D) N0 Q0 X% H. {# m/ T+ M
        发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。
: E1 s) D, K" x: A- z$ B        新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。- Z! U1 b/ X  P* Z3 _$ C
        应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。
0 M! Q( X, A$ N. W+ L5 }        发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是:9 L8 X8 J$ S, ]7 z

* N3 s5 c7 P4 ?! f发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。
. T1 j& q9 S  V5 l3 Y2 r! O  V: d
7 V- _4 c2 D0 m7 I+ O: Z/ ?1 k+ C( _        因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:
3 Q( \$ E- _1 D' E8 s: L6 H0 N  D
        1)、发送buffer全部配置为专用发送buffer- j! t! R9 c  [0 Q) i

* s: o* y9 b- ~9 g        每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。
6 }! u  f$ w7 m5 w
  J3 ^; O/ N( _/ ]        2)、混合专用发送buffer和发送FIFO7 Z) ?& I. m7 C6 q" }3 c( n6 o1 ~
& T: Y; q' v3 s" `
        在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。
; n5 x0 \& o/ i$ r$ f/ ~0 {# @
2 a0 M) x8 o# N
20191127162943182.png
7 J( m  p5 y6 O) P
( h0 h' _7 R' l& O
        发送优先次序:
! W& v) }' \3 a; S  B8 M6 m6 mA、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)
  j) C6 r1 L" I, O9 WB、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据/ t. u+ f* X) m
+ n5 {/ f) T4 \- j
        3)、混合专用发送buffer和发送队列
3 S/ w8 J* N# T+ u7 p$ ~
) z6 n6 q- ~2 v8 N" `1 j4 F% i6 C- K        在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。
* v# N! E( Z  G7 i# x6 w
' Q1 p! h% j4 o6 v4 n
20191127163202212.png

& Y8 [% u1 ~% J2 k: s7 x
: W5 S3 O) o0 M/ Q        发送优先级设置:
) ^; q$ d2 ^* I% ?: OA、扫描所有激活了发送请求的发送缓冲区+ w% ^# z" B5 {& r: c/ k2 K
B、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据; l7 F6 ~0 r5 e" R7 t7 T5 S
' x  v8 V( }8 R. `
        再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。
( r: p5 j" Y* ~/ t) z& R; s% H) C
3 m& t% ]# B* `$ `( a4 \+ Y9 ]        FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。
2 T9 p" ^# m2 z$ b$ }        发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。0 ?: P2 a2 O: e  k) f$ W% t
        发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。! p5 U1 ?. S7 E% L

0 K6 f% R& y; e+ D$ X        为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。! h$ E0 j1 a+ A% U

" Z. C* ~0 L# P0 e. W2 A0 @. F/ d4 I        CAN发送消息的代码实现:/ A$ d( R- R: t4 s- b& s/ N
) Y0 b- ?! {6 \* u: m
        发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:/ E- E! w9 w, w

6 ^. b* f& h; w- V- q# @9 c
  1. PutIndex = ((hfdcan->Instance->TXFQS & FDCAN_TXFQS_TFQPI) >> 16); //获取元素编号
    7 l5 L7 N$ M& [0 e7 S9 S8 i
  2. FDCAN_CopyMessageToRAM(hfdcan, pTxHeader, pTxData, PutIndex); //复制消息到Tx buffer 6 i( Q0 S! ^' U4 ~5 P
  3. hfdcan->Instance->TXBAR = (1 << PutIndex); //发出传输请求
复制代码

4 a) m  Y; G4 c. U        发送buffer请求寄存器FDCAN_TXBAR,描述为:
- Z2 u8 P  `$ E. ]  T7 m4 y, M' t  [+ s% n; H: S2 W2 [3 ~
- ]% I( E5 T( C
8 e* i! _+ W8 h7 @4 t9 E. h
        此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。6 B5 [8 `% R# C& m: H5 l" }9 c
7 O* v5 Y" Z) P" t; j
7 j* ~4 R: a6 B9 T5 K( J, g. S

) f1 ?8 U& O2 x. `0 a8 ^五、初始化/ L2 H1 p1 R" d( u
2 R9 P. n9 z( p/ z4 Z: o' b) m
        初始化可以直接使用HAL库,: _3 {9 p# _+ W2 [8 q6 T
; R6 n. R' [1 f* R/ q+ ~7 q8 z7 J" _
  1. u8 FDCAN1_Mode_Init(void)
    $ D& {7 f6 V6 s
  2. {
    ! R/ H( g1 D6 ?5 _& {, C
  3.     FDCAN_FilterTypeDef FDCAN1_RXFilter;
    5 B  {3 i; u1 o. G, O/ w

  4. % B" K+ a# O, {/ K5 l7 q
  5.     HAL_FDCAN_DeInit(&FDCAN1_Handler);                              //先清除以前的设置5 }! X8 E' v) H: {) t+ l( T3 Y( w/ u
  6.     FDCAN1_Handler.Instance=FDCAN1;
    0 d1 j; Z; t% ^4 @: k
  7.     FDCAN1_Handler.Init.FrameFormat=FDCAN_FRAME_CLASSIC;            //传统模式. c2 |6 _* E3 H
  8.     FDCAN1_Handler.Init.Mode=FDCAN_MODE_NORMAL;                     //正常模式2 P1 z& ^) G! \  G2 S7 |) |. f
  9.     FDCAN1_Handler.Init.AutoRetransmission=DISABLE;                 //关闭自动重传6 [- x0 Z# z! D3 U* @. Y# K
  10.     FDCAN1_Handler.Init.TransmitPause=DISABLE;                      //关闭传输暂停           
    ! g" G( ~& j& s9 `1 J* T$ U
  11.     FDCAN1_Handler.Init.ProtocolException=DISABLE;                  //关闭协议异常处理4 |- M' M$ E0 p* J
  12.         , j9 }# Q% N8 r9 S9 M
  13.         //时钟为200M,baudrate=200M/(NominalTimeSeg1+NominalTimeSeg2+1)/NominalPrescaler,这里配置为1M
    ' n  D- @+ H  X, ~
  14.     FDCAN1_Handler.Init.NominalPrescaler=10;                        //分频系数
    5 p+ M# T6 C1 |
  15.     FDCAN1_Handler.Init.NominalSyncJumpWidth=8;                     //重新同步跳跃宽度
    & S9 ~6 I( ?0 x! Y- r% o
  16.     FDCAN1_Handler.Init.NominalTimeSeg1=11;                         //tsg1范围:2~2563 D2 N) _5 R" e0 I9 Z6 X0 g7 O9 q  r
  17.     FDCAN1_Handler.Init.NominalTimeSeg2=8;                          //tsg2范围:2~128
    2 ^* |/ D3 d' K6 t
  18.         7 r* ?9 s# C7 U7 m4 n& L
  19.     FDCAN1_Handler.Init.MessageRAMOffset=0;                         //信息RAM偏移,10KB消息RAM共有2560字,故可以偏移0~2560
    ( j% ]1 j+ ]8 U! F8 r- \/ y$ s6 B
  20.         //使用了多少个滤波器就要设置为多少
    * Z  |% K& N- ^) u# I3 E4 h8 h
  21.     FDCAN1_Handler.Init.StdFiltersNbr=3;                            //标准帧滤波器个数,0~128
    5 O$ [  C4 f/ k1 W, i
  22.     FDCAN1_Handler.Init.ExtFiltersNbr=2;                            //扩展帧滤波器个数,0~64
    9 A  d# d) M0 O4 o
  23.         
    , ?+ U2 h) M+ O* `  I5 V
  24.         //接收FIFO0、FIFO1和buffer配置,此处没有使用FIFO1故个数设置为0, U" H& H6 R0 W1 @6 I# c% C8 C
  25.     FDCAN1_Handler.Init.RxFifo0ElmtsNbr=64;                         //设置接收FIFO0元素个数,0-643 c8 S' K8 j( N' E9 d
  26.     FDCAN1_Handler.Init.RxFifo0ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO0元素的数据域大小:8字节        ; M& q5 J+ M- j1 G0 ~& [
  27.     FDCAN1_Handler.Init.RxFifo1ElmtsNbr=0;                          //设置接收FIFO1元素个数,0-64! B* F0 {0 m# L1 ~1 G* h1 [& m
  28.     FDCAN1_Handler.Init.RxFifo1ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO1元素的数据域大小:8字节                % S/ q) {/ {3 L3 g: _7 `+ H
  29.     FDCAN1_Handler.Init.RxBuffersNbr=64;                            //接收buffer元素个数,0~645 i" V+ h$ A* K
  30.         FDCAN1_Handler.Init.RxBufferSize=FDCAN_DATA_BYTES_8;            //接收buffer元素的数据域大小:8字节        
      t2 Q% r3 v0 X& _4 d, r
  31.         ! R* Q$ e8 o, l/ r5 w
  32.         //没有使用发送事件FIFO功能,故TxEventsNbr设置为0。把发送buffer全部作为专用发送buffer使用,故TxFifoQueueElmtsNbr设为0.' f9 e/ {3 w+ S4 }: @+ \+ H
  33.     FDCAN1_Handler.Init.TxEventsNbr=0;                              //发送事件FIFO元素个数,0~32( y6 H9 z. X. G7 q% {
  34.     FDCAN1_Handler.Init.TxBuffersNbr=32;                            //发送buffer元素个数,0~32
    & e8 o# T9 B5 a4 u. j
  35.     FDCAN1_Handler.Init.TxFifoQueueElmtsNbr=0;                      //发送Buffer被用作发送FIFO/队列的元素个数,0~32
    9 D% G7 r9 {3 `" V1 u1 h
  36.     FDCAN1_Handler.Init.TxFifoQueueMode=FDCAN_TX_FIFO_OPERATION;    //发送FIFO模式选择,可以选择FIFO模式或队列模式
    : g0 ]* @0 T3 P2 x4 m
  37.     FDCAN1_Handler.Init.TxElmtSize=FDCAN_DATA_BYTES_8;              //发送元素的数据域大小:8字节  n8 b* I9 E* G: Q
  38.         
    / ~" B+ m/ j4 h0 M. e- W
  39.     if(HAL_FDCAN_Init(&FDCAN1_Handler)!=HAL_OK) return 1;          //初始化FDCAN
      a) P4 `8 L' _  Z5 v% \

  40. 8 R8 K! t) B. w7 u) p, ~# t
  41.     //配置RX滤波器,标准帧   ) x- x$ }3 |; h9 N) H  ]5 z0 i
  42.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    3 I6 z) G7 S: k
  43.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
    6 m. D  Z' K* w. i
  44.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型! F# n7 c* h8 c+ A
  45.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    6 @9 B3 w7 f/ U/ `: U
  46.     FDCAN1_RXFilter.FilterID1=0x112;                                //11位ID0 j* U! A1 z0 Z
  47.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    0 ?9 m% g( d, G
  48.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化6 `# v& b8 z) y
  49. 0 s0 [" y7 D; z/ k* y
  50.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID5 \+ r: @5 @: Q/ E6 R0 z+ C- Z
  51.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                  
    & u5 _- F% e+ D. n# s
  52.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型6 ^. B# }4 X- Q& B5 V
  53.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    7 G. R2 }+ |: B1 I" V" D
  54.     FDCAN1_RXFilter.FilterID1=0x113;                                //11位ID3 r7 ^0 q6 ~( V* I8 @
  55.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码9 s& U' [4 y7 p! ~1 I7 z! |
  56.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    ) }& R/ T8 B" g0 ^. J" l
  57.         % @! a( D6 n; A2 A
  58.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    3 q  @' N: J3 O
  59.     FDCAN1_RXFilter.FilterIndex=2;                                  //滤波器索引                  
    1 I) L2 y5 L1 R6 w, l! _7 i* x. K
  60.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型+ y4 O; ^6 j. e8 q
  61.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    7 S" O7 |$ t. w0 o( h
  62.     FDCAN1_RXFilter.FilterID1=0x114;                                //11位ID
    ; W7 n, M5 F) l
  63.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码+ n' c* H8 A" b8 ~+ N
  64.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化" D; V8 B9 d2 q  T' ?- H! q. c5 ^
  65.         
    8 _/ N$ J! |7 a
  66.         //配置RX滤波器,扩展帧。标准帧和扩展帧的滤波器索引是分开的   
    4 A$ V" K+ n9 J% G
  67.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID
    : x4 [$ Y; v1 A4 {& ?0 i  G. C. R
  68.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
    * n4 `* y1 l2 ^2 H6 j' _
  69.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型- m" Y  T$ ^& j) p& i
  70.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  0 r( W! @0 D9 q0 s* X' a# U
  71.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(2 << 12);                  //32位ID$ a7 X+ g5 w3 n/ ?( g2 k
  72.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码
    * L8 s- i6 z8 g1 Y$ D2 a' X, ~
  73.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化- H3 o5 l5 {3 V% p
  74. ! H# e( O& T2 ?7 b( G3 ^# _
  75.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID
    " R; V' H9 ^+ I4 g5 r+ a( |! |
  76.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                   7 \+ c' W& b; ~7 C4 x% n2 w0 [7 r
  77.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型3 Y0 F& b/ s, c- R
  78.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  " b- l# d5 m7 a1 d
  79.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(3 << 12);                  //32位ID! T( D9 w% c' v4 N
  80.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码
      f3 ]: H) C/ `: b, z4 B- B
  81.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;    //滤波器初始化
    & d  O* X. Z: r% P" @0 f9 J$ y& y
  82.         % U$ f9 i) b0 J% U) X" n
  83.         //滤除的消息直接丢弃
    ! M+ f  j" @8 O% T0 l
  84.         HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler,FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);  //设置被滤除掉的消息的处理方式, K$ ]& v+ v9 _6 r/ L! d9 n
  85.         
    8 k7 m3 y7 K: H$ B/ r
  86.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);   //使能新消息接收中断
    1 y2 n3 I! ^9 H( }4 @3 b
  87.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_TX_COMPLETE,0xffffffff);   //使能消息发送中断,0xffffffff表示所有的发送buffer都触发中断        
    3 J' h' W; c0 z- \3 F: M" ?
  88.         
    2 d1 n( @& ?" q% S9 V& C  X: V
  89.     HAL_FDCAN_Start(&FDCAN1_Handler);                               //开启FDCAN$ G2 H. n( E/ H0 E& ^6 n! U' C  ~
  90.     return 0;& y9 _: W2 V1 A
  91. }
复制代码

( I6 q( f/ H! Q* I4 h
1 R' u1 v4 ]' r* E, j# P六、CAN发送示例(来自正点原子)5 v5 L' P7 f8 E) ]* i  w
6 A' B/ b/ F, P2 c  _
  1. //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)        
    - E0 z! Q' o  q( v
  2. //len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8                                     / ]& b4 ^1 }# H1 v9 x* E
  3. //msg:数据指针,最大为8个字节.: @! G. |+ M0 U5 }( _
  4. //返回值:0,成功;+ n9 w9 F+ P9 O
  5. //                 其他,失败;: \) f1 F7 p/ `9 n
  6. u8 FDCAN1_Send_Msg(u8* msg,u32 len)9 B8 d4 y  |! M2 e. m% B* k
  7. {        5 x- v! A2 V, u$ S1 w
  8.     FDCAN1_TxHeader.Identifier=0x12;                           //32位ID* f5 G7 d6 {' B' Q, T0 D
  9.     FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID;                  //标准ID: S2 E0 U% J6 |) t. @
  10.     FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME;              //数据帧" a7 w. Z+ Z8 B9 D; m
  11.     FDCAN1_TxHeader.DataLength=len;                            //数据长度
    ( }! Z% l- W# [) o
  12.     FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;            
    1 n+ I# x9 m4 ^5 V
  13.     FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF;               //关闭速率切换* l% A& c3 n6 ]* u
  14.     FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN;                //传统的CAN模式4 H* u! l$ e! Z# @8 Q( O
  15.     FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS;     //无发送事件
    % k& u2 x9 o2 \( u8 k
  16.     FDCAN1_TxHeader.MessageMarker=0;                           
    ( m$ Q  ?3 k. J1 D( q
  17. 6 \$ ?8 `# u2 e. H9 D
  18.     if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送" a2 ~  u2 @& ~0 @7 O$ h
  19.     return 0;        
    / v* h) z# {. P% `( S/ @6 J$ ~. z
  20. }
复制代码

- Q/ t2 ~$ B  b3 n( e5 |: S七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)3 T4 Q9 M, E* N1 p+ b% ]

4 j9 A6 U' p% n' p% |. G
  1. //can口接收数据查询" d8 |* h- y* p. t5 V& U+ n2 K$ k
  2. //buf:数据缓存区;         
    0 m' j! I6 a4 y
  3. //返回值:0,无数据被收到;
    % C) D1 P/ |7 j7 f" D
  4. //                 其他,接收的数据长度;# y) Z. _& m! K3 R4 B% |) T
  5. u8 FDCAN1_Receive_Msg(u8 *buf)
    % ~' u( [2 ~2 F" }. `
  6. {        
    3 N# k4 Z; h& Z+ L  d& Y! V
  7.     if(HAL_FDCAN_GetRxMessage(&FDCAN1_Handler,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf)!=HAL_OK)return 0;//接收数据6 G' |3 ~  O& q6 m4 a& q3 w
  8.         return FDCAN1_RxHeader.DataLength>>16;        
    7 m6 v7 g1 s+ v. X, F2 i" g
  9. }
复制代码

: f& Q" g' t& _: v参考:《正点原子》  STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准)1 N/ A; S  n' S

* u1 j4 O9 o& [! j1 X* t
) z% h9 H8 {; e3 D  L' d- C: Z
收藏 评论0 发布时间:2021-12-22 14:00

举报

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