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

【经验分享】STM32H7的FDCAN

[复制链接]
STMCU小助手 发布时间:2021-12-22 14:00
一、介绍. T  H: t2 [4 i) T
  \5 q+ d3 R' d
        FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:
  E* F0 L: P0 U6 d* C8 ~6 `5 A6 k1 z
       1、每帧数据段最大长度由8字节上升到64字节。% i: _& r* L" ~/ P5 c; W3 L
2 [  r$ s. x; v3 h; y4 _
        2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。( F# K  f& o! i

4 d# S0 P" t. S: _9 h9 W        3、向下兼容CAN。
! Q$ }0 V2 D( g4 i! k0 w  w
/ l: |* Z$ Z: S1 h$ `  w# w. H        H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。
, f. S& F( O( E4 |6 u
2 d1 x5 ^$ x' v二、结构
" D" {& q: K& `
7 t! s3 D1 u: Y0 Z6 j+ Y3 m        先看结构框图:# e) E: p! i+ B) c
( \4 G) m+ N( A" D- B
20191121162856859.png

& U( r. i- \; g9 a0 p) c8 y& t8 ^9 F3 K3 ?  t
1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。
  B5 @6 |) A: L
; A1 e! M/ e, ~
20191127102816991.png
7 h/ I- h6 ~" \9 Z2 k6 l

; v- \/ V5 x; Z) T: `8 U" ~        可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。
( u# v: G  r4 I% M# P7 u$ P9 @/ y! h6 J$ q8 d9 G# A0 o
2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。
, U/ }  k2 O5 N( @# J: E( B8 u2 o: J# c% q+ H
3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。: ]8 ?4 h" ?7 v7 V+ y$ o9 r  R

4 {# ]# t. i. ~/ ]4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。/ a' J) N& i9 E/ ~: _
! L/ Y) d9 u4 X7 I
5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:
& D3 `+ M- G7 C& x/ h( T" X
( }  c3 j1 q$ `4 ~( e
20191121164211703.png

4 }0 `. Y' T4 b' R/ {7 K& B& b0 L& A7 }
        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 L" c) T2 f$ B' y; e, ?# K6 {$ Z& N
        1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。
% G* q) c: j1 b
9 m/ G# M( N) ?9 ^: e        2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)- [' S  |; |; W+ |. l6 c

5 T4 f( l( S! Y; X1 ?        3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。
% r1 Y1 r5 P. r0 C" ~
8 ?7 Q( ]( }' d4 v        4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。
9 h, c( O; g5 ]! \& ?  ]2 B/ {4 u7 j% m7 g$ b: d
        5)、RXBC.RBSA:接收buffer,最多可以有64个元素。, Q- k% V; K4 z7 t" V9 p; M
+ V0 T+ @& K9 x) A; B# H! _% I
        6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO), y6 \: d& O* ]. R# T" ]! B
* i: t7 ~4 w" t+ t! z+ W
        7)、TXBC.TBSA:发送buffer,最多可以有32个元素。
  O  `6 z) ]/ j$ h% k% Z- q0 J2 M2 H5 a8 D4 w
        8)、TMC.TMSA:触发存储器,最多可以有64个元素。
! a' T0 M. Y7 n2 M% ]
# r% ]. ^# G9 u' I- C《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》
" o0 h0 m4 c3 ?8 Q/ \+ Z) d- i+ F' N: @/ F; r/ ~
        下面来详细介绍这几个功能区域:$ z) i, T; X0 X/ E
+ Z) y, R  [: t; S1 m% D
1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:
2 }3 F/ J6 s/ ^" S: i
6 @& C$ \6 M8 ?1 J  I, n
20191121170625565.png

/ G% Z3 z( K5 p3 C+ K$ [0 c. y, g" E  V' _7 A' U
SFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:" Z  Z1 P; A5 e
    00:范围过滤,过滤到SFID1到SFID2中的所有信息。3 x( g4 T: h, {$ m5 W% f: ^/ n# \
    01:双过滤,过滤到SFID1和SFID2中的信息。
* Z5 H3 y( M( P  n2 r+ a  b    10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。
( s; n& E' S; n  \6 o$ z    11:禁止过滤器元素+ X1 E- Y% z  `4 l
SFEC[2:0](bit29:27):标准过滤配置% x; X6 {  D  j7 g
    000:禁止过滤器元素! ~" ?. g+ F$ F- T  G
    001:当过滤匹配以后存储在Rx FIFO0中。, }7 r$ n4 B: i  W# W' o
    010:当过滤匹配以后存储在Rx FIFO1中。( J: y3 r& B; l) F0 ]
    011:当过滤匹配以后丢弃。2 ^6 A! _) C. E8 e! P
    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。, g! Q1 A4 k) v' N$ U
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。$ a$ l1 S! i: n, @1 D* H3 l
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
; O$ v2 `) Y9 ]: R* R9 J    111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。
( \/ P! X6 g) l8 O- C+ M0 v/ [SFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。
5 A' L8 o( _# G+ O& wSFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。
- O. D9 Q" ~& V0 z0 k/ r3 C1 k0 r$ b( B7 \
  Q8 O8 }, q" ~' r! f; K. {SFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。$ @! h9 w3 N$ y: _3 `( ^7 ]5 `
    00:将消息存储在Rx Buffer中。; q1 C- ?( v2 y3 F
4 p: n4 r0 A$ H: [- e+ D, |" y
    01:debug消息A。   
4 q' ~) T9 h" t, X3 K
" `, j/ r$ ]* x1 X1 j+ g( q: J; e    10:debug消息B。% B1 P% k) \+ K- f* y9 w

" L8 k; v# _: R6 u, O# i! N    11:debug消息C。
# r, I2 ]- W. U- M9 y, R$ W4 S8 Z
8 |- U, h0 W# F- \" h! E+ ASFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。* s9 `2 Y* R1 c( g4 g) z

1 A% ^" \  h) o. mSFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。
) X; b& x. v' ~% p% G' u$ Y! b" O5 C3 A  P6 k
2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:
- l. D% F7 r$ N1 d6 Z8 w4 y
7 t1 C  L5 A6 M' ^& c$ s0 ~  X
20191121171836710.png
5 D2 }) u; ~: ]5 O5 {# E0 h
- y0 V; @4 e2 ^  m4 P' U( v
F0 EFEC[2:0](bit31:29):扩展过滤配置6 G! F( c7 R+ [3 w" G  |/ R0 n" u
    000:禁止过滤器元素
$ v% `. ~+ R$ B3 }. m$ v. x    001:当过滤匹配以后存储在Rx FIFO0中。9 [/ h$ f/ K4 t" e
    010:当过滤匹配以后存储在Rx FIFO1中3 d( L9 Q$ P/ ^0 c; g6 e! i) {
    011:当过滤匹配以后丢弃。
/ ?8 A  d+ Q. P, }0 K& e1 ?% }    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。& u/ x1 u; N' C( A6 B/ C/ s7 A4 D% V
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。/ V6 q1 f# B8 \3 p
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
* F3 x. S# @3 c: s  A: M/ a    111:存储到Rx buffer中,EFT[1:0]忽略。
) h/ ^2 h0 h: l1 nF0 EFID1[28:0](bit28:0):扩展过滤ID1。0 B, C+ M5 R! \6 m. A
F1 EFTI[1:0](bit31:30):扩展过滤类型
, U* C4 b$ @" t+ t, v; W    00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。* T" h* @( Q( y7 i8 D
    01:双过滤,过滤到EF1ID和EF2ID中的信息。  }% r& n7 c! L' {+ J
    10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。
: t2 e& H1 B- w# p* ~    11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码
" M5 t) i4 D! _# C& [2 mF1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。
: n1 {/ o/ Y% p5 q6 o! E' \; y    00将消息存储在Rx Buffer中。
5 e1 @4 S. @1 y4 I) b( a    01:debug消息A。
1 P% W+ w( \( H' c    10:debug消息B。6 X" ?) g1 i2 x
    11:debug消息C.
7 G% }: T" Y9 O) G8 zF1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。- n' m% d+ e* C% p8 B; Z9 Z0 I
F1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。
, T8 O7 o1 u. {1 v/ y2 A, _
; G; [! ~8 Y/ ?+ r3 U+ J% m- Q: \+ ^3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:
; n0 K% D! a+ J5 R+ _. E3 h
0 R/ Z, R3 ^8 I/ t+ h% E' f
20191121172451477.png

' t8 a8 H3 N. {8 `; r, i" M9 B' k" l, Q0 [' j  \
R0 ESI(bit31):错误状态标志
5 w# {) m( T+ _/ {& }    0:传输节点显性错误。2 Y- ~5 c' t- Q7 E: Y1 h: f
    1:传输节点隐形错误
9 L( K% C' R6 ~0 t  sR0 XTD(bit30):标记是标准ID还是扩展ID5 N# Q& a+ |: A+ p4 e5 j3 ^
    0:11位标准ID
9 _0 M$ `9 J( e& c3 T9 `/ X& |3 B" f- z+ T! T
    1:29位扩展ID
0 c6 \: n- a5 _. j. b% C
8 M$ `. v; u# L4 jR0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。/ C4 i, F! {- j5 [% `" Z
    0:接收到的帧是数据帧/ o) G' Y# i/ f+ d
1 n- [3 V( I3 G& w
    1:接收到的帧是遥控帧
* J0 D3 [) c3 g0 hR0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
% d' M, h3 S8 P! u3 AR1 ANMF(bit31):! v9 @' E7 {" ^$ t/ |

: Y$ P1 N2 b( v+ n+ M/ Y    0:接收到的帧和FIDX中的过滤帧匹配
% _6 f9 B7 q2 {6 R/ r. {6 T+ ?4 u2 f/ i( i
    1:接收到的帧和Rx滤波器中的任何元素都不匹配* z+ E8 i& T/ ^& c6 z: a
R1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。# H% y3 S0 y/ L% T
R1 FDF(bit21):帧格式
- ^1 y3 d3 P6 S, s    0:标准帧格式4 a( L  q8 @1 ^# }2 e
    1:FDCAN帧格式。( Q$ `. U- L, X8 A3 u* |  ~* K1 `
R1 BRS(bit20):比特率切换
! j9 U4 S3 V1 k2 K0 {% i" l- W    0:接收到的帧没有比特率切换3 U/ K$ j+ c7 M4 u. z$ i" G7 u
* z" C* S9 M4 h% j. q0 Q+ S
    1:接收到的帧带有比特率切换- D1 u* H7 E. W3 M1 g  i6 ]* _; Q
R1 DLC[3:0](bit):数据长度。6 N# n8 o4 U  Z
    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。
& D) w* C0 T0 J! @! C* c# y    9-15:如果是传统CAN,接收帧长度为8字节。8 r2 u: Y0 [9 q  y9 B" N
    9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。
& [  B; f5 Y" A& MR1 RXTS[15:0](bit15:0):接收时间戳。
7 [/ v+ ?$ Y2 e# p* rR2-Rn:接收到的数据。$ s' \7 l7 H6 \7 H1 \* [

$ D8 g, h+ \7 w4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。  b  D1 K2 c- ], E5 T
" q; H  {1 \$ l2 H) y4 u
20191121174147402.png

2 F3 D/ F5 W3 n6 u& I0 K5 Q: ^. c: ]# j6 o9 H6 X8 K1 W3 `
E0 ESI (bit31): 错误状态标志
5 T& p& [3 n7 _" G4 q
* `* [, _1 O+ Z+ r5 T" P    0:传输节点显性错误8 T/ y% w! w  u0 `* U. j6 T7 O7 K
    1:传输节点隐形错误。
+ b! o) O# N$ g  {' x0 bE0 XTD(bit30):标记是标准D还是扩展ID
) Z& \# r4 `( l" F! ]3 S    0:11位标准ID.( Y5 ~/ d/ v; [0 d+ s; a6 n
    1:29位扩展ID
5 e2 h3 G8 R" Q+ J! o
" {) k, i- x6 X9 R  C+ BE0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。$ @4 O- D, x! J' z  F6 ?4 U2 ]3 T
    0:发送的帧是数据帧2 Q( O) X% _0 ~: `) b
    1:发送到的帧是遥控帧
$ N; ^9 V  H/ |8 i5 a& k- sE0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。
/ `0 e! Z# B" lE1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。
# M+ Y! ~! a5 uE1 EFC(bit23:22):事件类型* }; z- L: T) t( c/ v
    00:保留。
4 M3 i, Q# H( [& |! w    01:发送事件。* J7 Z( N  X4 q4 K
    10:发送取消。
: I& b3 {! _. [- D7 E: t    11 :保留。
' |6 T, j4 q0 L  o% I. A( M7 QE1 EDL(bit21):扩展数据长度
+ h7 w5 ?; `3 s    0:标准帧格式( S/ X- c4 [# _) U
    1:FDCAN帧格式。% \/ j$ B* {0 |4 a! e  K
E1 BRS(bit20):比特率切换; C+ [" D8 o& m" n- _
    0:发送的帧没有比特率切换
  Z5 q" n1 u% P( t5 [, u  d; U. _# [$ }) R
    1:发送的帧带有比特率切换- e' [9 s( e3 D; P
E1 DLC[19:16](bit):数据长度。
4 G6 ~# G* _0 K5 ^0 `    0-8:传统CAN+CANFD,长度为0~8字节。
  J! U$ |  @% p* Q) J, T  d8 {    9-15:长度为8字节。
$ b5 M. E/ q. n5 f3 kE1 TXTS[15:0]bit15:0):时间戳。3 s# K1 |4 a1 ?4 h8 K9 m

5 r5 Q) X$ L. N& b& v# o1 b5)、TXBC.TBSA:发送buffer,有32个元素。9 W0 {2 s* a0 S* G" c$ }
4 u0 k/ C3 I8 R! X* [4 q1 A
20191121175121493.png

" a! P$ j! `1 }2 v* q
, x& c  x2 [! T# b# VT0 ESI(bit31):错误状态标志
  U/ q6 d3 O1 z( v; ?. v2 a+ N    0:CANFD帧中取决于被动错误标志。
: }2 D: y* m# V: j1 K    1:CANFD帧中隐形错误。9 D& G1 x# D9 B+ i
T0 XTD(bit30):标记是标准ID还是扩展ID
) b$ `0 N) t9 p4 }5 Y- `    0:11位标准ID7 e9 b$ {3 l8 x: z7 h4 i

1 n+ s0 T$ f* w' G4 t% ], P    1:29位扩展ID
6 }  Q* ?* o$ N9 \8 s2 {' }- X3 e$ S; R( A2 ?1 O+ N
T0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
" V- C" r& W0 d$ r! S, w: \    0:发送的帧是数据帧
+ F) {. E$ L! s2 F    1:发送到的帧是遥控帧" H" f+ m  @/ N5 e4 f) M
T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。8 E" _" V- A4 ~" Z" K2 H
T1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。3 \. L* Y1 Y5 R1 {. H! y
T1 EFC(bit23):事件FIFO控制; i' L; B* j/ G5 h. X& p8 E) |
    0:不存储发送事件。' O/ I) a: N6 L+ @' k
    1:存储发送事件。' b/ x# `2 Y6 ~, X
T1 FDF(bit1):帧格式! {2 V+ U+ ]4 E7 t7 F' e7 a: n
    0:标准帧格式
+ `; ?  H' O, M, X% _. h    1:FDCAN帧格式。
" F) Y, F- _8 Y3 z8 hT1 BRS(bit20):比特率切换, Y4 U" ]; T/ s8 i' t; K! {' `
    0:发送的帧没有比特率切换
+ x% A/ ]. h0 g' P/ X  U* U! K- n
6 ^/ V( K; ]! F6 s9 B; }$ ~/ z    1:发送的帧带有比特率切换
2 P  j; V8 T, K  FT1 DLC[19:16](bit):数据长度。
: i" |8 Q! ~. I8 g( k+ X) k; ^' ~    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。) I7 E3 Y. W/ Y8 _. u! ?; s2 E, r
    9-15:传统CAN,接收帧长度为8字节。  x0 q/ w0 o& s9 e
    9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。: J, C/ ~4 \# |- g  b
T2-Tn:发送的数据。
/ g4 Z! H; f9 G- m% Y1 @4 W
/ U! K2 N0 U& [% u三、过滤器设置5 J2 Y5 Y4 p4 `

" Q: G% O7 s; _' Z        标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:$ @, z: U2 f0 l, Q
- S- T3 S* G! A. P+ t
        1、指定范围过滤* r$ _4 |& U# T- M( r2 ?) `+ ~) H3 o
        通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:
$ U. q5 u1 u. x3 Y; C- K0 L. N2 a8 L% ?
  1. SIDFC.FLSSAn.SFT=0        //范围滤波/ ~. O6 q7 c- m# e1 y. B0 J) s
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到RxFIFO中
    ' W# K. f/ C( e& v0 `, V
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    . x( z$ `: c/ P
  4. SDIFC.FLSSAn.SFID2=0X321  //ID2
复制代码

. {. q! D7 I# d        2、指定ID过滤
$ V# u' C9 a9 P7 n
9 H4 D) F$ N  b) }4 B2 C        我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:
: q" ?; g. A4 V
5 w! d3 A( e6 }7 v- u& }5 ~+ o9 ~
  1. SIDFC.FLSSAn.SFT=1        //指定D过滤' `0 ^( ]5 v% E' v2 T4 A
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到Rx FIFO中1 Y1 o" J3 u# [5 c7 H) b
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    $ L. a/ s6 m: I6 {( V
  4. SDIFC.FLSSAn.SFID2=0X123  //ID2
复制代码

4 H. X) _, A6 `6 d9 g" k        3、传统的位过滤
0 E8 M  Z  _# E$ F+ ^9 O2 O        第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:( r, K2 m" V1 F; _7 W
6 v! P$ O; u. q# M
  1. SIDFC.FLSSAn.SFT=2          //传统位过滤9 z( l: v& o: U! |* N) ?
  2. SDIFC.FLSSAn.SFEC=1         //如果滤波匹配成功的话将消息保存到Rx FIFO中
    , X9 P" `* D' u  R; T
  3. SDIFC.FL SSAn.SFID1=0xFF00  //ID1. l" M6 I; g& D0 ?  H3 l' j
  4. SDIFC.FLSSAn.SFID2=0xF00    //掩码
复制代码
$ X# F, w. e4 U$ C- B, L0 o6 E- y
        其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。
4 x. T8 F3 m1 v( u: h7 k- V. X1 ]8 O8 p/ L) s
四、CAN的收发过程; d+ [6 |) N0 `: b. p' G

, h4 v& E& W! i7 O& a; [* ]4 M6 n) h        1、CAN接收过程2 V+ T5 I: ~$ O, H4 N% g( _
# l! r0 u) c1 h/ J1 \
        CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。0 U. Q# _8 S/ e  R. ]- v

# M) I: V6 e4 ]8 I7 w( k( e! q. G        接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。
8 V7 i% v1 ~& ^6 D- K; a4 @! W0 m2 b  q5 A" S# L- c8 _7 l
20191127114550965.png

1 C7 v- [) @2 ~+ A, A4 X, A& B  E
, B% r: V4 i! s( ?/ E( u        当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。
3 |& ~/ ?  a' y9 v+ c, D% Q& f# b: _3 N7 y1 I

3 W/ n$ l5 y2 a6 c
20191127111235302.png
% R" h. e5 F4 f9 Z( W5 R6 ^. b" H
1 }  |2 p$ Y0 F0 y0 z4 q+ t$ p
        为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。- b6 E. c5 A5 e; E& b
        读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):
* V7 G+ ]) R. l/ s6 C$ ^' t4 ]9 `0 B7 x
  1. /* Calculate Rx FIFO 0 element address */% B/ M. ]" T* A4 v1 z' I$ u5 E0 \
  2. GetIndex = ((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8);
    + V: d2 V1 X) w0 t1 p% [! t% i
  3. RxAddress = (uint32_t *)(hfdcan->msgRam.RxFIFO0SA + (GetIndex * hfdcan->Init.RxFifo0ElmtSize * 4));
复制代码

2 Q# y- T% p2 O5 c+ P+ Q% A1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。. R/ g8 e$ p. \4 y. C  F
6 q+ @  U8 j$ G8 W$ y
2019112119095892.png

# z, u) P9 z% i* d) I3 i$ z; h  D3 J  r: H5 n- D. y
2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。
3 j8 J- S/ e8 y( w3 v5 x
! P; T! v% ^& l9 y& ~3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同
% ?* u" P) |, f1 S+ e7 k8 @2 t: J1 D/ `: Q$ }" W# G

. O  U0 I2 X# V, v6 t% b9 C5 |* K- _" Q- y* ~. K$ O1 ]
如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。
: }& G7 C3 ]: T0 D! A7 ^- Z
% G) `. R, m" ]2 D! g) `( M9 r如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。
8 N3 [- h# `- ]/ S* K* c- ^
* o  d3 }- ^) ~7 v9 {) A. b. h) }        计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:( Z* n; x) i9 a( U) S4 L

0 t/ t; w7 j0 r5 J
  1. pRxHeader->IdType = *RxAddress & FDCAN_ELEMENT_MASK_XTD;
    5 C4 i# c! n4 A, [/ m% i
  2. pRxHeader->Identifier = ((*RxAddress & FDCAN_ELEMENT_MASK_STDID) >> 18);
    , @) R% c  N( e5 e/ U) `2 |2 z
  3. pRxHeader->RxFrameType = (*RxAddress & FDCAN_ELEMENT_MASK_RTR);5 g  K( Y( q  |5 K" Q
  4. pRxHeader->ErrorStateIndicator = (*RxAddress++ & FDCAN_ELEMENT_MASK_ESI);  //这里地址自加了一次: R7 y! z$ u: [% O
  5. pRxHeader->RxTimestamp = (*RxAddress & FDCAN_ELEMENT_MASK_TS);
    3 ~0 V; H7 l. X1 Y
  6. pRxHeader->DataLength = (*RxAddress & FDCAN_ELEMENT_MASK_DLC);
    ) M6 o" u0 O- o& N/ l
  7. pRxHeader->IsFilterMatchingFrame = ((*RxAddress++ & FDCAN_ELEMENT_MASK_ANMF) >> 31); //这里地址又自加了一次
    $ F' E* x$ t- @
  8. ......
    $ e" P. ^. e( a0 F
  9. pData = (uint8_t *)RxAddress;. b# G! L6 r; d  F
  10. for(ByteCounter = 0; ByteCounter < DLCtoBytes[pRxHeader->DataLength >> 16]; ByteCounter++)3 z: N/ x3 u! O1 \, S/ l
  11. {0 D7 F4 ^  s/ l) |- h
  12.     *pRxData++ = *pData++;
    & h0 e8 ?5 a. {. Y- }: Q) @, F
  13. }
复制代码

9 Y3 L. R7 O# f( [! Z2 q        2、CAN发送流程
7 p- z) U& g) S' X9 x9 x- u
1 n  f  J, M3 @        在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:
( t! _% j0 r: e! p
+ g; d7 g: p8 U! r1 ?9 n' n, |
20191127150042199.png
) l+ `# P: Z* {/ h

( C5 O' y: L% T        因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。; _+ @2 o) W( ~1 Q

* I1 Q) @; L; o9 F        下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:/ G+ d8 g, f) j! N$ a2 I3 z
& G7 t8 ?. d( x* m- V
        1)、专用发送buffer
/ X4 ^1 B0 G& o5 r3 c# e8 ~; Y5 X5 ?/ y4 k
         专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。% Q7 z6 Y' V  h; @( ]1 q
        如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。- g: X5 w8 x) s/ x, l
+ X2 M& W! x" z" q5 X* U, h6 T  A, }
20191122101555794.png

% u4 T# X1 Q+ F$ U3 w) i7 M( J" B, b" A
        专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:0 r" A$ A5 o* }6 T4 j- ?
) D1 h+ j5 W+ ~; R, \9 j
发送buffer索引 (0…31) *4+发送buffer起始地址。
4 l" \% n, ^. r3 {. D1 ]: s% h; C# q7 Q" a! N
9 S, j' F# n9 e( E

0 t* n. C& H; Y2 F" |6 ^! F6 V        2)、发送FIFO
8 Z' ^) W, K3 r9 l* J( z/ n0 M1 G% G4 R$ k( b5 Y* g1 A0 m+ m
        发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。, ^8 p# ?& H/ K% s. J, W: T
        新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。
" q; G5 j+ _+ U) ]  D6 M7 x5 C3 W; x9 e  a" b4 t, j, T, C  D
20191127160804244.png

  ^& g8 q) f; {+ _7 Y! M: F$ Z6 ]- n" n0 l9 }
        发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。
3 J* t: _1 H$ F" v: v$ |% @  L  h& G; k+ r
        如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。  r& K3 k7 j0 Y' K; C
        如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。+ m0 V; R6 ?! q) f5 X7 S
        如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。0 d7 f9 m( u  h; p4 [4 Y4 L3 ^
        发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:: n$ S( B; J. z  u9 G6 \5 F
) R4 ]0 Z- l) @- R. u
放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。+ E8 q9 \9 Y" |. V" ~; i8 i

+ {8 U1 Y# K% K' F  }# F        3)、发送队列
4 h' r3 c, s/ z/ g5 |        发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。0 [+ ?0 O4 q# W1 a% R1 r3 t$ f3 ~
        新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。- m3 q5 x& h9 l( B; {
        应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。3 y$ W& B2 [& c( T. d
        发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是:  R; v8 s& M! R$ m" r# I6 L

( I# `& [$ B  {# P发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。1 v6 K' v. n8 P
6 H2 ^6 I) ]: t3 E9 B+ x
        因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:4 n9 X8 T4 \, H( j0 o7 S# {3 Z% V
4 Q% B( J4 b3 v5 e6 P
        1)、发送buffer全部配置为专用发送buffer' S; Q( M2 H8 D2 `. R0 p3 Z! [

( I/ C$ t  q0 C" k8 a( K) E6 m        每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。6 C% B' g' u$ L' y2 ^: O( j

+ L$ G4 O5 O0 s        2)、混合专用发送buffer和发送FIFO
1 j1 D1 p  J" b) w
4 m; e5 m5 n) s8 y        在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。6 E* C( ]3 Z4 n, _
. Q5 Z3 Y: R1 F* N
20191127162943182.png
3 t, d; Q4 x3 r) `* \
$ U& Z) Z8 r/ p& c9 ]& Z! t6 }
        发送优先次序:8 B+ H% V; C4 w% A5 r) }
A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)
& p2 R3 w4 C4 _( N' I$ VB、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据' c; E5 `" }, L& L- _1 ^

6 s/ }1 @8 g' t6 [3 s0 s! _        3)、混合专用发送buffer和发送队列9 D" X7 \7 P, _

8 c7 c' [# k; l! t( Z        在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。
* z& U( `) s. _3 L! I
; b* S1 p  _) l( w* J5 i* K( y$ q
20191127163202212.png

/ D) F2 i% ?2 j) ~+ e; v
, L( y) `% j5 x- {) Q        发送优先级设置:# G. A, `+ i' B  X; y
A、扫描所有激活了发送请求的发送缓冲区
# n7 k9 s+ t$ s* X9 FB、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据, P. l: t$ V! S. F, U/ B+ I! K

/ J, }& W; ]+ T" x9 P  t! E        再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。" }  v! S9 I# ^

8 r) a, @" K- H( J* L5 E! b        FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。
% g& Y) A# z. @        发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。, F0 O# k; @/ S& x/ l
        发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。
; ^/ O, d0 k9 D  k
, z& R4 J3 O  l/ I& p% D        为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。
5 v  |, a* Q+ c) h0 q7 Q# I- L# ^! c, D, D2 @9 j7 L5 I' x( n
        CAN发送消息的代码实现:  V6 c3 G+ `7 a5 D
: f1 h" _3 L! ]5 K
        发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:' Y/ H/ _0 C( G# R. n! Y0 l* x6 |0 ~

/ E& B2 e& T0 N
  1. PutIndex = ((hfdcan->Instance->TXFQS & FDCAN_TXFQS_TFQPI) >> 16); //获取元素编号 ! a4 V  t) }8 e, @
  2. FDCAN_CopyMessageToRAM(hfdcan, pTxHeader, pTxData, PutIndex); //复制消息到Tx buffer 3 m- Y' M6 J) G: m  M+ |
  3. hfdcan->Instance->TXBAR = (1 << PutIndex); //发出传输请求
复制代码

3 D6 D" h- N" E        发送buffer请求寄存器FDCAN_TXBAR,描述为:
+ ]+ Z- S" T: m* `& {+ P5 W( C8 @6 I" X4 p4 k8 ]3 ?. p5 r

' S5 W: m: Y# y8 [: M2 }
4 R3 E/ f% J4 x4 Q1 O/ Z        此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。' {; A* ?* v4 R0 N0 O- e
% c1 x9 O1 z3 l( o3 Y0 r. \

" z0 k$ G5 A3 }# {2 y  H5 D, G0 G& ?
五、初始化$ z3 r3 j* {/ n2 [& x* X( w4 ^
/ s9 t" t& W6 N
        初始化可以直接使用HAL库,
9 U' g1 D$ G* n8 p4 T% d- w& {' Y7 P. I" @
  1. u8 FDCAN1_Mode_Init(void)
    $ s) v6 p" f; V" z% v; m) A
  2. {9 p7 T& g4 k" V& Y; D# j
  3.     FDCAN_FilterTypeDef FDCAN1_RXFilter;
    : r3 V" I; u+ h* X. x
  4.   u1 E  A" _3 O
  5.     HAL_FDCAN_DeInit(&FDCAN1_Handler);                              //先清除以前的设置/ h- a/ e1 y' j3 a
  6.     FDCAN1_Handler.Instance=FDCAN1;
    * _" ^6 B6 y+ l% J, ~7 \
  7.     FDCAN1_Handler.Init.FrameFormat=FDCAN_FRAME_CLASSIC;            //传统模式% s% N  S2 _. n9 {" Q# E# C
  8.     FDCAN1_Handler.Init.Mode=FDCAN_MODE_NORMAL;                     //正常模式/ d5 c5 T) f# W$ Z" i" _+ E
  9.     FDCAN1_Handler.Init.AutoRetransmission=DISABLE;                 //关闭自动重传
    # j# o5 }+ e/ x9 e
  10.     FDCAN1_Handler.Init.TransmitPause=DISABLE;                      //关闭传输暂停           5 F5 \$ |  H, g- y3 C" H4 c( D
  11.     FDCAN1_Handler.Init.ProtocolException=DISABLE;                  //关闭协议异常处理/ I  P; O$ d" R, W8 a7 C
  12.         
    . G& Z: Y! Q, E
  13.         //时钟为200M,baudrate=200M/(NominalTimeSeg1+NominalTimeSeg2+1)/NominalPrescaler,这里配置为1M
    1 {5 A5 h6 N* Q# p4 c9 N) F5 i
  14.     FDCAN1_Handler.Init.NominalPrescaler=10;                        //分频系数
    ; }' J! q( e- _) Q: v. H" p
  15.     FDCAN1_Handler.Init.NominalSyncJumpWidth=8;                     //重新同步跳跃宽度0 i6 L9 g6 ?/ q7 t1 V# [4 F
  16.     FDCAN1_Handler.Init.NominalTimeSeg1=11;                         //tsg1范围:2~256" ]/ L$ g. L% _# v: P' g
  17.     FDCAN1_Handler.Init.NominalTimeSeg2=8;                          //tsg2范围:2~128
    + E7 P- T( t) l) H
  18.         ' R3 {" n6 H1 ^& _+ b: |  A; a5 n
  19.     FDCAN1_Handler.Init.MessageRAMOffset=0;                         //信息RAM偏移,10KB消息RAM共有2560字,故可以偏移0~2560
    6 e5 A2 @1 t" @" I% _/ H4 F
  20.         //使用了多少个滤波器就要设置为多少
    , v0 \' T2 t' u9 a; o; ^3 ^
  21.     FDCAN1_Handler.Init.StdFiltersNbr=3;                            //标准帧滤波器个数,0~128
      @% Y& f) K' |
  22.     FDCAN1_Handler.Init.ExtFiltersNbr=2;                            //扩展帧滤波器个数,0~641 t2 b+ f0 n) E5 u  D( U
  23.         1 m2 T; X2 j, O& P
  24.         //接收FIFO0、FIFO1和buffer配置,此处没有使用FIFO1故个数设置为05 A% J" D+ i% u. W
  25.     FDCAN1_Handler.Init.RxFifo0ElmtsNbr=64;                         //设置接收FIFO0元素个数,0-64
    % q" |! z8 |5 Z, [( z- h0 n* C
  26.     FDCAN1_Handler.Init.RxFifo0ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO0元素的数据域大小:8字节        0 X' _" \; P' |- A& Z
  27.     FDCAN1_Handler.Init.RxFifo1ElmtsNbr=0;                          //设置接收FIFO1元素个数,0-64
    & e/ h( L, v2 `! z! T$ a
  28.     FDCAN1_Handler.Init.RxFifo1ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO1元素的数据域大小:8字节               
    ( |7 _' Q( o! f, X
  29.     FDCAN1_Handler.Init.RxBuffersNbr=64;                            //接收buffer元素个数,0~64  E0 @1 L) R3 E0 J
  30.         FDCAN1_Handler.Init.RxBufferSize=FDCAN_DATA_BYTES_8;            //接收buffer元素的数据域大小:8字节        . i" K8 ]8 I% A' j) _( j! e
  31.         # f) B) n3 f; Q% V" Z1 H
  32.         //没有使用发送事件FIFO功能,故TxEventsNbr设置为0。把发送buffer全部作为专用发送buffer使用,故TxFifoQueueElmtsNbr设为0.
    # d' n8 X& V( w
  33.     FDCAN1_Handler.Init.TxEventsNbr=0;                              //发送事件FIFO元素个数,0~32
    ! {5 O" q/ X& I2 }. u7 z, n
  34.     FDCAN1_Handler.Init.TxBuffersNbr=32;                            //发送buffer元素个数,0~32
    ' [/ u) N' j. }7 c
  35.     FDCAN1_Handler.Init.TxFifoQueueElmtsNbr=0;                      //发送Buffer被用作发送FIFO/队列的元素个数,0~327 o: c" K. Q( t# [1 S
  36.     FDCAN1_Handler.Init.TxFifoQueueMode=FDCAN_TX_FIFO_OPERATION;    //发送FIFO模式选择,可以选择FIFO模式或队列模式
    ; \( ^& Q. F. b3 D) I/ _
  37.     FDCAN1_Handler.Init.TxElmtSize=FDCAN_DATA_BYTES_8;              //发送元素的数据域大小:8字节
    + G, `" e* g4 L, h6 y  N0 e. Z3 E
  38.         8 B" y0 P6 M( l* ?$ {
  39.     if(HAL_FDCAN_Init(&FDCAN1_Handler)!=HAL_OK) return 1;          //初始化FDCAN2 Y2 N1 G5 b3 T+ P8 K

  40. - z1 K. O$ H7 i7 K
  41.     //配置RX滤波器,标准帧   " [6 E0 ?' J& r" u+ h
  42.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    ) J0 e' v# V8 e. l! Z3 [
  43.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                   6 s) C4 r7 }( k/ k1 n9 r9 i
  44.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型, K* E& i: d3 }* i
  45.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    1 S7 f% I4 J' ~  x& J: X' x
  46.     FDCAN1_RXFilter.FilterID1=0x112;                                //11位ID' E6 [) Z9 h7 n' ^* s; V
  47.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码/ Q; y7 y4 F+ z( }" }  D
  48.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化: P  o( d. E/ y+ v
  49. 0 ^, v: E$ H  ~- u+ \* U# m2 W
  50.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    $ _5 m4 r, N, `# Q
  51.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                   8 h/ B5 T! ^7 `
  52.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    + C; a6 W$ w! ?6 r
  53.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  5 a' Y% N& X% T/ V& b0 ?9 v$ a: o6 i5 ?
  54.     FDCAN1_RXFilter.FilterID1=0x113;                                //11位ID+ T' V. I& w# l% v) ?
  55.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
      k7 c, G  Q4 E7 L) o6 F
  56.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化3 n# V+ q0 d/ ^& w% q8 L. f1 ?
  57.         
    " U# S7 q, S0 @* Q4 Y- c
  58.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID9 Y% w6 @2 S8 t1 C1 M9 r" [+ y
  59.     FDCAN1_RXFilter.FilterIndex=2;                                  //滤波器索引                   7 P4 t6 Z5 y# f$ S0 C
  60.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    3 L$ {% P  h0 Z- z3 i% u  T
  61.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  + y; J. U' d# E9 X% }& m
  62.     FDCAN1_RXFilter.FilterID1=0x114;                                //11位ID# U4 b2 `- D5 E
  63.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    9 b# @+ v) b& q! U8 V3 S  H
  64.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    9 ^) S( m* F& |6 g
  65.         
    1 i% u- {; P: S# @! o- F
  66.         //配置RX滤波器,扩展帧。标准帧和扩展帧的滤波器索引是分开的   
    ( v' w* M; z+ _
  67.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID
    # X# R2 H9 j( Y4 e, e
  68.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                   $ e; a- V% L7 ?' V1 S$ R
  69.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    $ G9 T7 k' A8 M7 t* H
  70.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    ! j# ?: j, a' \# @4 n1 k- Q
  71.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(2 << 12);                  //32位ID
    ' L' K+ o* E; c0 t& R' ]0 M
  72.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码( g0 K, C. t) Y4 b/ V1 G
  73.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化7 r  S3 j; E3 q$ s) |  w/ ?
  74. 5 n! @% G' Q" `4 W* ?
  75.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID
    & a! ^' c, y. h9 w
  76.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                  
    6 o" Z) x7 i& D* L
  77.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    ! M+ y9 _3 n# [3 }" d" S" c# e( F# }
  78.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  $ m0 N, L, V- @+ a* n3 L
  79.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(3 << 12);                  //32位ID' r% p+ N- b: R+ p
  80.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码
    4 @" P+ p: I, \6 ^% a8 R7 u7 r5 ?( c
  81.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;    //滤波器初始化
    " o' E2 ^' b3 k" u' p
  82.         + Q( @  i6 d+ D; `) A5 W$ ?
  83.         //滤除的消息直接丢弃
    3 H" W  C# r; D+ L+ L" i. K
  84.         HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler,FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);  //设置被滤除掉的消息的处理方式
      U5 l- z8 [3 h( I; s
  85.         % q: C+ a/ J& K
  86.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);   //使能新消息接收中断9 Q# P5 A# Z6 G- z8 L
  87.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_TX_COMPLETE,0xffffffff);   //使能消息发送中断,0xffffffff表示所有的发送buffer都触发中断        
    9 t: ]4 i& ^8 K1 M3 ~1 m
  88.         
    0 v2 G- y) a0 n! }
  89.     HAL_FDCAN_Start(&FDCAN1_Handler);                               //开启FDCAN, z- C5 W0 M: Z+ Q
  90.     return 0;
    & _& i! [* j0 q' @
  91. }
复制代码
1 Z0 @8 O( v& e2 u

$ O& }" M: C1 d: @3 Y) ~六、CAN发送示例(来自正点原子)5 j1 m! e0 I4 [$ I% _, ?9 `
# V4 F$ ^2 J$ r+ K
  1. //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)        % O, Z' Q4 a3 r$ B& q" t
  2. //len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8                                     # w0 K, N" p7 N9 p1 I- @# G) X- A
  3. //msg:数据指针,最大为8个字节.0 }- Y9 U: U- O3 h* y* V; ^/ h
  4. //返回值:0,成功;
    / {& S& }: ~) ~+ P, b/ d% V
  5. //                 其他,失败;
    + A- Q% l0 L: h8 c3 D
  6. u8 FDCAN1_Send_Msg(u8* msg,u32 len)
    7 m3 P7 L+ J# U5 I
  7. {        3 f1 j; ~/ t: [* N, ~' Q( U6 O  X
  8.     FDCAN1_TxHeader.Identifier=0x12;                           //32位ID( C, V) j2 K' {& \/ c* R1 e
  9.     FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID;                  //标准ID. Z8 D7 @- }  n
  10.     FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME;              //数据帧
    ' @, l" r& @/ E2 G+ v% H! t
  11.     FDCAN1_TxHeader.DataLength=len;                            //数据长度
    1 B" n) |" Y# J9 ~
  12.     FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;            9 G& \, [/ y( K! ], O
  13.     FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF;               //关闭速率切换
    : h2 U( Y# Q8 @$ Y' U" E
  14.     FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN;                //传统的CAN模式/ ^5 B4 B, L( `! j9 g
  15.     FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS;     //无发送事件
    4 w* Z/ s3 |6 Q1 N) M
  16.     FDCAN1_TxHeader.MessageMarker=0;                           
    & a8 H5 c* y9 I+ n2 G' S
  17. " I) x9 j2 b% O9 ?& g% c5 A
  18.     if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送
    6 i1 a+ V# @$ M, t
  19.     return 0;        
    - W. H( [# K1 c* p' N  u: s; `9 B' H
  20. }
复制代码

! A7 H+ c" V' {5 k, E, |七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)# h* s6 B  {( ^4 N

0 ]: n; y0 f1 H- _6 M
  1. //can口接收数据查询
      n: l9 k$ G. C9 ^! s/ N5 i+ j
  2. //buf:数据缓存区;         * F9 j/ B7 d+ q' V" l- t/ g* e& [
  3. //返回值:0,无数据被收到;
    : |  o% m( @5 `1 E- G
  4. //                 其他,接收的数据长度;* u! }  R) _# ^
  5. u8 FDCAN1_Receive_Msg(u8 *buf)
    1 n, s- H: x5 |1 v+ R) f) ~' |8 ^1 O
  6. {        , ^( k# ?. H) f8 \- j5 E7 V
  7.     if(HAL_FDCAN_GetRxMessage(&FDCAN1_Handler,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf)!=HAL_OK)return 0;//接收数据
    " i! ^, _& ?9 @) A- w7 n
  8.         return FDCAN1_RxHeader.DataLength>>16;        
    ' l  x" \7 R0 a
  9. }
复制代码
6 h. K0 ]. d5 @& a/ A5 z
参考:《正点原子》  STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准)
  t8 Y- @) n$ R+ @
  ^. z2 o4 E4 A: n" u3 w' s+ c, X2 g# Z
收藏 评论0 发布时间:2021-12-22 14:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版