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

【经验分享】STM32H7的FDCAN

[复制链接]
STMCU小助手 发布时间:2021-12-22 14:00
一、介绍
% ~1 l' n# n; S  S) n% s1 |* x
5 v( x  N9 P) n2 r1 ]# z* U/ Q8 U7 B        FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:
6 I( k; d) I! F2 G! `
" k+ K% u* w) O$ {: l' T       1、每帧数据段最大长度由8字节上升到64字节。( S8 [8 ^' J6 s( N+ G8 N3 N
( {2 Z% ?+ |2 L2 t/ l1 h
        2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。1 u. n% E1 Q0 [" B; l- c

! ^. F& m( C0 q3 Q) Y0 R. Q8 ]        3、向下兼容CAN。
0 Q* z0 u3 G. n7 X. p3 K: q+ A( C
        H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。
$ s2 g8 W) J+ c0 B6 L/ m" H$ z, x
6 L$ ^5 E4 m' W! C0 W' ]二、结构% N- z1 t9 p9 W
3 }/ N/ {) P3 e  w
        先看结构框图:9 C: O6 ~0 I' o( f/ z
1 g% K2 l, Y+ O" p8 g0 v* \% a3 n" A. X
20191121162856859.png

" H+ U* `: Q7 P3 X; Q# I$ N0 K8 Z+ M5 D( j0 N( l$ q/ J# o% h$ T7 z
1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。: ~4 E6 w: ^* z; b- K
2 M/ [) @% d0 V+ _% r
20191127102816991.png
, e; m* i: j4 a( W( R+ l( a9 K

3 m8 D5 N3 J# R5 _4 |  L        可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。. q. `. V' [" \
$ f7 v/ ~" Y$ D2 L% i* o% Y
2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。' w8 t; G' D, D9 L: Q
7 m$ s7 L3 c. N
3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。; v/ ^" k& X& e1 d
/ b7 n3 r% V% F7 r
4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。
; Z. e9 l7 i. e& t$ r4 k
. \0 i" t" t9 k3 q! s5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:
+ v; M7 x3 W# r0 Y6 \3 H  w; g1 A# O0 N
20191121164211703.png
% S* J* m/ Y( O2 c. ]* h
# |3 t5 E2 L% L2 Z" V, ]
        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字。1 o0 B/ T. h2 J6 U: o% Y
. U0 o0 S/ u& z" s
        1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。
: R+ c( ~0 q* w5 t$ @& a- W
" K8 a) U: c! R( r: [/ y& V$ [        2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID); \, k: R* l4 D, q

+ q; {) j' ?1 Y8 W' ~8 v* X        3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。6 X6 ?" L9 c" Y# }. d

( q7 K" w' W% ~, d, f        4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。
. @: P- y- N. ]+ _( B8 x& I/ O' e6 J0 R
        5)、RXBC.RBSA:接收buffer,最多可以有64个元素。  t- e( t; C4 c2 a! [% P7 ?

+ |6 U! z' f4 T( m        6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)
6 a( @9 M! G( a4 z0 J3 i+ I" e$ G% Z. r. ?  i# s* }% b& L$ K, E
        7)、TXBC.TBSA:发送buffer,最多可以有32个元素。
  E8 ]: A* S% Z6 r- {
% T% X& h: k, ?7 H. `        8)、TMC.TMSA:触发存储器,最多可以有64个元素。1 F% [" D4 y5 {& x3 \3 [9 H, G
9 p& j$ ^& U% [7 y1 O9 s8 l* u: a  F6 K
《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》
- I; Z' ?9 ?) c/ |( D9 X+ }" O6 A0 J- F' L
        下面来详细介绍这几个功能区域:
% Y6 E, K; I6 S. R/ U. d2 C, k, N
1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:
- y$ v( X, G6 c; |* I" \  L- }6 i
20191121170625565.png
1 \' ?6 ?" P" H& q  W, {. a  e

& {' G! s  o7 C. z" c7 |9 USFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:
# v& e1 m+ ]( O& C, q    00:范围过滤,过滤到SFID1到SFID2中的所有信息。# s0 p& ?) F9 ?* q
    01:双过滤,过滤到SFID1和SFID2中的信息。
! ]3 w4 ?$ Q! ~, p9 w( [    10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。
8 L" D( ~" N- d. }5 |7 g0 v    11:禁止过滤器元素  ^! ^% Q8 C, K: U) x- c
SFEC[2:0](bit29:27):标准过滤配置) \+ _2 w. j$ ^1 Q" Q! `3 k7 r
    000:禁止过滤器元素
. h; \- H" z  X' ~    001:当过滤匹配以后存储在Rx FIFO0中。! _3 I0 V, t6 \6 e
    010:当过滤匹配以后存储在Rx FIFO1中。6 y  I8 ]$ X* Z1 R  E; |/ Z
    011:当过滤匹配以后丢弃。
# ?& Y8 q2 k4 f( v7 f    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。; \7 c& x+ q3 {; D( q" P+ v
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。7 v6 y0 Y1 W2 f4 B
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
9 s3 }6 ~3 r  O3 q0 [    111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。
  G( H5 r" M3 d" W* cSFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。" n& e7 A% P% F4 k; w
SFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。
3 f7 ?9 e- [7 y! c# M
, }4 W; F: _# U" USFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。
6 [9 x# H9 B" G& K8 [& v    00:将消息存储在Rx Buffer中。, u: r5 z3 y1 a9 e

" g$ w( q' [! E" C3 n* E* I    01:debug消息A。   
0 ]% f8 |1 ]  q! b3 q* G! X9 @( V2 c# N+ t& Z* E6 P
    10:debug消息B。
2 A8 ]' ~# @$ ^( [& e" ^- I+ S3 Y' u+ t- Z6 t' n/ q
    11:debug消息C。
; Q; W3 c: r& P1 X3 w& d( w& d6 m! j5 J; f( q2 H
SFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。
0 Y0 K  b% T$ {  I) x: [1 H
8 w8 _5 f& O+ `0 m( N% o$ q5 O5 JSFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。
3 r+ f: y7 x' r' d$ m  F+ J! s  `' n% I1 n4 T3 O- n
2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:/ h6 m2 r/ ]7 l: f8 E
. p. w7 X+ ?' v2 E! z: Q9 g
20191121171836710.png
8 t5 ^& H4 O6 V; u3 L

* M: G+ {& p" k& g! t- tF0 EFEC[2:0](bit31:29):扩展过滤配置
" J% T6 U8 w! e    000:禁止过滤器元素/ e" b: X/ a9 C/ X0 K  O" b
    001:当过滤匹配以后存储在Rx FIFO0中。
/ C% m5 D' D$ V) I% Y    010:当过滤匹配以后存储在Rx FIFO1中2 ^0 L6 j, r4 m( {& |/ P  ^: T
    011:当过滤匹配以后丢弃。
/ g6 m3 J7 r3 x8 U5 p6 ?    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。% [3 ?( k1 ^9 L# N
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
7 T& b2 P- w1 T5 u; L    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
2 U  `4 z! b8 e    111:存储到Rx buffer中,EFT[1:0]忽略。, }2 h+ |. A+ @* A) Y
F0 EFID1[28:0](bit28:0):扩展过滤ID1。) O7 l7 \1 x6 P' b" z9 J" t" ?
F1 EFTI[1:0](bit31:30):扩展过滤类型
7 ]0 S! }! |8 P- c: w+ }    00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。
9 N) P* _! k$ X- P8 {3 }    01:双过滤,过滤到EF1ID和EF2ID中的信息。
- R0 t6 i) ^3 l: Q; i. z    10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。3 f7 [  n& x1 C" j( u# H
    11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码
3 b; Z( |1 v" U; d0 HF1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。2 L+ {# m9 ~, {- m
    00将消息存储在Rx Buffer中。
* q: O2 p; L0 G( f  Z# t/ D' h9 S    01:debug消息A。
$ P6 K4 K% B: }% w" N    10:debug消息B。9 _( {  \# l, w# K5 v
    11:debug消息C.
; G5 E1 E% }' d* r/ f" UF1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。
0 [. |/ R0 p4 DF1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。
2 F7 f1 E5 \; B7 K. r2 W, ~" ~  E& ~+ g- e
3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:
% ^: _% {% ^5 I( G3 L) J
) v8 u8 {2 ]. o. M7 `7 ]! L5 @
20191121172451477.png
8 d: J/ m5 J4 {
/ u* y" X" p2 J- O! J
R0 ESI(bit31):错误状态标志9 @9 m5 ?- R9 z7 Y
    0:传输节点显性错误。6 C: X* g& a4 l
    1:传输节点隐形错误
( \4 T/ E0 e$ i3 b$ mR0 XTD(bit30):标记是标准ID还是扩展ID
& T4 E6 M- L' R  P( Y% a3 y1 d    0:11位标准ID9 K0 \( ]; u0 \. {8 `

- L2 L0 `8 y) j4 S  {6 `9 i    1:29位扩展ID
" n( ?+ I  d# n* ?1 M/ L; ^2 U! `
R0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
' w4 f4 g$ M& w& t' L    0:接收到的帧是数据帧
: d# a5 S. Q. M2 [+ u  q- O
' ~2 i3 j! [5 G0 p- ?    1:接收到的帧是遥控帧
9 f$ P) g2 |/ z) ]R0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
' Q& g- n! p: r7 j' l: X0 `9 ~& J- ~R1 ANMF(bit31):
  P0 i' T2 [- T% |
% t8 X* F! R+ W6 A6 m( z7 K4 ~    0:接收到的帧和FIDX中的过滤帧匹配
, p5 {. a7 @: ]7 M5 H8 n5 C+ W" a
' G) K* _8 d/ J$ N5 n4 r. P    1:接收到的帧和Rx滤波器中的任何元素都不匹配1 m1 G- n; r+ j; K9 Z3 p
R1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。
- t. _! i0 q( J9 x) eR1 FDF(bit21):帧格式4 A, K+ Q: Q0 O9 ~. S3 c( }9 K
    0:标准帧格式2 l2 l- b) ^; H* K* P
    1:FDCAN帧格式。! s- v5 z) l6 B9 L! X+ C$ m& t* z
R1 BRS(bit20):比特率切换( z# L- n6 s3 t( s3 Y. i" P7 t
    0:接收到的帧没有比特率切换
5 Z$ s- B- o. v1 v/ @) j+ e9 L) Q0 g6 u' N- h" G- z  v
    1:接收到的帧带有比特率切换) I+ w/ y# H5 L1 O& Q% M+ O2 O
R1 DLC[3:0](bit):数据长度。
: }( X: M0 f; E3 P6 e  u    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。" E9 O/ }0 H$ m; @
    9-15:如果是传统CAN,接收帧长度为8字节。
' B7 Q5 F& V8 `. ^& V' a    9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。
  p* D& [$ Q2 B3 [* e) xR1 RXTS[15:0](bit15:0):接收时间戳。
+ W) S- r+ h& {R2-Rn:接收到的数据。
  V" q. _  A. q$ F, q3 W4 ]+ L2 d0 ^' j/ g! G7 C# a
4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。0 G: y' y4 b8 _) K! i( Y

( D5 O9 L+ c  `; T
20191121174147402.png

' X, ?( P, G$ D0 t3 T; J; g1 H6 m6 \! p/ L9 Z5 M/ V( E3 o# u
E0 ESI (bit31): 错误状态标志
; b% J& i- d4 r" J8 v; p$ ~+ g+ ^3 L- G
    0:传输节点显性错误4 }. k5 Z5 C8 \% j( s- K
    1:传输节点隐形错误。* V7 K" e4 W  e9 P0 p/ `1 R% M8 Y6 L
E0 XTD(bit30):标记是标准D还是扩展ID1 G# p% r  B' x. u. m4 P
    0:11位标准ID.
! `6 {3 z8 i9 G  e) P; o    1:29位扩展ID
. a1 H% J$ i* k7 s+ S6 X* C# Z4 d6 T; X! x0 H
E0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
5 H; ?. P, T- _6 f- J    0:发送的帧是数据帧
4 H) D2 @5 A3 ?    1:发送到的帧是遥控帧$ o8 a9 z6 q  l: P' {
E0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。
7 U9 A) D4 Z7 y0 O8 I# J( ]E1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。
0 u5 J* z9 x; z* {E1 EFC(bit23:22):事件类型3 O$ k9 {9 y+ s
    00:保留。7 K  G3 i% C0 I; Y
    01:发送事件。5 N/ v* s& u7 M$ Z
    10:发送取消。' h) _% }, k: n! W2 [  r
    11 :保留。
0 p: ^5 u3 Z3 w* a" D1 k+ u% VE1 EDL(bit21):扩展数据长度5 z/ {) T- g) r5 h
    0:标准帧格式" y! a. I  b5 J
    1:FDCAN帧格式。
# y6 k. G  _/ J, s; ]  v1 KE1 BRS(bit20):比特率切换- c8 m6 E, t6 D: @. o
    0:发送的帧没有比特率切换
; C  S; b, S) x: u; l: |. k) K* i  U* a# G' P# V1 I3 x( z" r% c# n
    1:发送的帧带有比特率切换6 J/ |0 ~$ `4 {0 a6 G  Y
E1 DLC[19:16](bit):数据长度。
# s! c7 t/ y4 _* P! Z6 y: L( |    0-8:传统CAN+CANFD,长度为0~8字节。" a  j- e! p" @) E
    9-15:长度为8字节。
- T+ ~: r0 {4 e8 W9 g: E6 z' VE1 TXTS[15:0]bit15:0):时间戳。' ?, e+ u3 i; M% F: H
3 N3 h2 T1 x9 @. b7 j$ P' T
5)、TXBC.TBSA:发送buffer,有32个元素。. t* S0 t7 L2 I9 X4 d
) j  n, A) ]7 R- X  y
20191121175121493.png

# @" d& t6 `3 |4 E" t" ~
2 x0 F& p7 X) U7 l) \' LT0 ESI(bit31):错误状态标志
! h- b0 ?! p  ]8 M1 j# d2 F    0:CANFD帧中取决于被动错误标志。
, X1 \; z3 S- `" E1 z5 t    1:CANFD帧中隐形错误。- D# L+ v8 C8 I# k: Q. L* _1 y+ x; W
T0 XTD(bit30):标记是标准ID还是扩展ID# q% K9 ~6 V0 H' Z5 g, M4 G
    0:11位标准ID0 N0 y% H2 w/ C, B8 z7 ^

2 ^$ r% }6 p, d0 F7 B/ [- o1 K# H6 v    1:29位扩展ID
& o0 ]% o- D7 H; v2 \9 O0 R" c  v, ]+ i  _; J
T0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
( b$ [3 n# y* L5 J8 `    0:发送的帧是数据帧+ f+ ^$ k( Y- _+ @. ?. L
    1:发送到的帧是遥控帧
, b! c+ B' b& x- T' t  n. r4 YT0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
$ x0 J& {" }" u( }9 q/ jT1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。- Q8 R7 _, q$ z: H
T1 EFC(bit23):事件FIFO控制1 c4 u, T) T4 t: v
    0:不存储发送事件。" `  j1 s/ z: M
    1:存储发送事件。
3 ^* \+ W0 S3 t1 B% S- Z2 @: T% ?: jT1 FDF(bit1):帧格式# a- q0 @6 i6 M! I4 c
    0:标准帧格式
3 {+ V: _  w/ i5 f! T: N8 W; K    1:FDCAN帧格式。, Y+ X0 f/ H' S  k
T1 BRS(bit20):比特率切换, Y  `3 ]* D7 a0 D
    0:发送的帧没有比特率切换
9 ~- ~" t5 t2 w. u/ F9 F9 `, z: q  A2 }2 Z1 {( T7 Q' E4 A
    1:发送的帧带有比特率切换
- n: \' b# d* o. NT1 DLC[19:16](bit):数据长度。
" \  ~2 y1 G( d) V    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。  A6 I7 d4 p1 t3 \0 q% s. @
    9-15:传统CAN,接收帧长度为8字节。3 S  i( A& A! B9 H, N
    9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。
0 k$ b2 J/ @' [T2-Tn:发送的数据。
: }. w. V" B; r, g
* U' ~. c3 U' p/ U三、过滤器设置7 Y# R2 N4 O8 P% ?9 Y
( H2 u) N. Q+ c* \9 g$ X3 H
        标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:
, ?. i1 A. K& v4 E4 N* ~( b/ s; ?8 P8 V6 n# V2 ^
        1、指定范围过滤
/ H! A0 K( Y8 c% \        通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:, k4 s* B: P7 [& _3 G( A- c
0 W0 _; i% {& S1 p" Y2 f3 v& Q) \
  1. SIDFC.FLSSAn.SFT=0        //范围滤波: {7 l7 L( m" f# Q! k
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到RxFIFO中% H; S$ R4 @* z4 d8 x- t9 n3 Z
  3. SDIFC.FLSSAn.SFID1=0x123  //ID18 ]0 ]2 H( J: U0 B  j( S  L5 q
  4. SDIFC.FLSSAn.SFID2=0X321  //ID2
复制代码

. t; _& t/ q* K. P        2、指定ID过滤
) k* }9 f6 F7 l, H& w9 j
6 y) k; W; J$ A2 x2 I        我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:
. {! D& {. }+ U! t+ @2 v$ Y
% R$ O0 g5 N( g
  1. SIDFC.FLSSAn.SFT=1        //指定D过滤
    * H6 d( N. h# c  q
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到Rx FIFO中# q3 U3 ]! R! ]9 ?
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    ( @; _4 O: Y, F1 W  \' J
  4. SDIFC.FLSSAn.SFID2=0X123  //ID2
复制代码
! @: H3 d7 l  \, o, y
        3、传统的位过滤/ @. z. h: r5 ?9 K  c4 N  f
        第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:( U+ O/ T' o6 H( V2 v& @7 g9 Y# P3 C( n
' Q$ b( L8 b/ F, A7 T+ J3 _
  1. SIDFC.FLSSAn.SFT=2          //传统位过滤/ l: G9 p+ `; V( r) c+ A
  2. SDIFC.FLSSAn.SFEC=1         //如果滤波匹配成功的话将消息保存到Rx FIFO中" R$ x6 V# ?3 w, I$ b
  3. SDIFC.FL SSAn.SFID1=0xFF00  //ID1
    0 R2 g" \9 |* ]* x2 Z# D$ w4 {
  4. SDIFC.FLSSAn.SFID2=0xF00    //掩码
复制代码

( C( U, i" K1 G% ~        其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。
* a' e: E; r* c- d! G. q0 D* s  l9 v4 q* R' s
四、CAN的收发过程
. M: L8 s* {" _" ?6 x0 l# j3 E* n1 K' ^
        1、CAN接收过程7 Z, N0 o* A3 e+ P; e  Z& p' H8 Y/ _9 f
& g, O, B: C( @" v
        CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。6 g+ I! A3 k- Y. X+ P' }& `

# `) r& c4 w- C. i, }9 k, u9 X. P        接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。% x& d' g: H: E2 A, K

5 `6 l1 A& j, A0 e  E* z% W
20191127114550965.png
2 p  i0 V( N" m5 d7 H. ]

# w" }) ?& I, b3 ?        当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。
: N1 T7 f+ W3 Z8 W$ T
2 l' S7 h/ e+ |* A9 X2 @5 M8 ^  G$ @- |
20191127111235302.png
/ a; g2 Z9 _+ W1 Q; b( y+ [& w9 ]! `

. n5 p, F$ l/ g4 k& R' n! t        为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。* z- @- S0 O- b
        读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):% S. _5 M& y* B+ R7 c3 T& ?+ r7 V8 g

+ C( ]) ~  I! J7 h! l
  1. /* Calculate Rx FIFO 0 element address */+ n2 k0 r$ W" {% H. P
  2. GetIndex = ((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8);( ~+ ~2 y3 p: B" c/ F& J4 x; w& U
  3. RxAddress = (uint32_t *)(hfdcan->msgRam.RxFIFO0SA + (GetIndex * hfdcan->Init.RxFifo0ElmtSize * 4));
复制代码

7 _; o. Y9 [& q0 S1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。
9 B( W  q+ j7 W4 H1 W2 V0 z0 F' o8 ~
2019112119095892.png
( e2 g/ U% h6 M% n

3 j1 ?5 A$ o& n3 `0 m" z, G6 h! M2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。
& p! J/ F8 c/ n' n0 u2 p8 O9 x& a6 I. L% f* u
3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同
' z! P  e/ E" ~3 }# b  P. y* a( C1 ^1 m) g$ g3 _

9 k9 x. v4 ?- W" L/ y( `
) {2 ]5 r1 m! L( I# b如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。
6 L+ H9 u3 |8 o7 N3 z5 H
  s: |. t. ?# m如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。1 ^  ?7 |+ U6 c$ d
, l( Q2 e5 w, I( I" e5 P
        计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:
6 I2 v+ t8 x  J5 q: Y& r2 E4 r4 g: r0 E# H+ m
  1. pRxHeader->IdType = *RxAddress & FDCAN_ELEMENT_MASK_XTD;
    / Y) L4 w) N' B+ |" a! e: n, s8 c
  2. pRxHeader->Identifier = ((*RxAddress & FDCAN_ELEMENT_MASK_STDID) >> 18);
    7 ?) H0 s4 l6 r  i+ ]
  3. pRxHeader->RxFrameType = (*RxAddress & FDCAN_ELEMENT_MASK_RTR);
    7 x8 w" u& ]$ K
  4. pRxHeader->ErrorStateIndicator = (*RxAddress++ & FDCAN_ELEMENT_MASK_ESI);  //这里地址自加了一次
    . t% A+ P  l' P" }
  5. pRxHeader->RxTimestamp = (*RxAddress & FDCAN_ELEMENT_MASK_TS);
    4 e. }* Z& r0 o  n. k
  6. pRxHeader->DataLength = (*RxAddress & FDCAN_ELEMENT_MASK_DLC);
      ^8 E6 T; b) \3 n3 H
  7. pRxHeader->IsFilterMatchingFrame = ((*RxAddress++ & FDCAN_ELEMENT_MASK_ANMF) >> 31); //这里地址又自加了一次
      V" j" K5 U' S6 H( m+ O
  8. ......2 j9 p  \! B+ F3 X) d4 L
  9. pData = (uint8_t *)RxAddress;6 S) a! n- Z: R+ h6 O2 J# K
  10. for(ByteCounter = 0; ByteCounter < DLCtoBytes[pRxHeader->DataLength >> 16]; ByteCounter++)
    $ I- b/ z. ?5 P5 ]; b' M& @
  11. {) S  V% e. M/ o" s& X
  12.     *pRxData++ = *pData++;4 o6 L3 A' ?2 F% g+ w  a, H% n
  13. }
复制代码

, L6 o6 V5 V0 T        2、CAN发送流程
# r0 J) m# ~9 o7 W7 v. D9 N
# \; e' x/ I3 y4 l" O        在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:
: F$ @% d: a8 J
7 g0 ^' l3 [& u3 P% J" ]* D+ H; X
20191127150042199.png
5 H' I  v0 i6 j
) k6 @# v% X3 a6 v) D1 b
        因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。+ h6 `- ~9 g! y' X

* ~# j/ a( X6 L        下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:& o; G& }; f+ y2 X
4 @  K* |$ Y& R& e. s0 m4 P8 o# ]
        1)、专用发送buffer6 B5 L0 w" i* U7 ^4 `

! F) s) l! [1 L2 n         专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。1 O, E" y+ c( {
        如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。
" P# s" }, h4 P6 @+ N- m7 d/ N+ ~4 ]/ u' j8 E; A" x& q
20191122101555794.png

+ t, V6 M: c% o$ ?6 y1 P: E3 k, j  E$ b. _+ ]7 E
        专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:, M8 S" W0 a, E
2 u8 E2 }- i  U( J
发送buffer索引 (0…31) *4+发送buffer起始地址。
' Z$ f+ L9 \+ Z  N# C+ c# ^" g0 k* U( K5 |5 G& @

  o1 _0 F2 ]+ @* F9 A
* J1 L5 o- o. b$ G3 |! l! J4 K& r        2)、发送FIFO7 i7 g! A! D/ n
! t( O0 w! `& A9 S- i0 ]
        发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。
0 b7 e" ~6 w8 B: m- k6 q- w# S+ g        新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。# G7 r% z  S+ s8 y4 B! K
7 [/ `5 |& J8 u0 o, |( h" E
20191127160804244.png

% l5 _) R7 m+ A
  o' `9 _' N$ E! @        发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。8 x- a/ f& L/ ?
; g  D& Y; \. c" L
        如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。
' \1 i% y( L' i% P* A" T        如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。$ u& }% b# X6 j- f# u  a2 X
        如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。- y/ v4 Q; T" s8 M8 J
        发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:
$ m" k! U/ d3 _+ G, M) p$ ?7 t3 o: P2 \: e, `
放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。4 Y8 D* X6 Q3 s% d. G0 R
0 C! y9 R) j' \9 l/ }7 n9 x
        3)、发送队列
; N+ X1 [+ G& L8 L. ~, N7 V1 ?        发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。
$ j  S0 y* Q( `: |        新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。
( ^: F: |3 T, n/ s) w  N        应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。
* s$ p% ?5 L' b4 E* p        发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是:6 k9 g( ?! k2 y* O- X- b

6 [; H7 j& K4 K! m+ A5 `发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。0 ~+ m, V0 G' n* l

' g. n3 f, t" R9 }        因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:
& A3 A8 {5 ?8 G' a/ \: Q. l
% ?8 Y, P0 N4 P        1)、发送buffer全部配置为专用发送buffer
! H: e3 Y+ f3 X: `: B& m! M/ q  p3 i, ~; n+ R, k) I  R  k" j
        每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。
- N& {& {. [0 Y! A' s5 i/ N, i$ f, K: E! {
        2)、混合专用发送buffer和发送FIFO
! Y  U4 v* w% [/ K/ ?# i& O. i7 C1 ^& s+ M# N5 K8 a) _$ n
        在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。
+ q) y4 A. F1 O0 I" L, s: l4 s
' L5 _0 W: ^- s0 O- D7 \$ ~
20191127162943182.png
2 s1 F4 T9 R$ H5 P
" C! q. c2 W" b" I7 V+ c% y
        发送优先次序:4 x  s% ^$ b1 U4 }- t! |9 i
A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)
9 |" W) ?3 y6 b( Z! R/ w9 vB、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据3 J6 Z+ W# F  }8 r- j

2 C% ?2 W/ u& j1 v8 {        3)、混合专用发送buffer和发送队列
& F+ @+ ~. L! a2 r; Y8 I& }5 l: u7 E2 x1 [, V5 ~
        在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。
0 @- {0 K' R" L& P# a+ }0 ]) Y7 v2 W, N! ?
20191127163202212.png
# D# s/ x0 N! N! \

# E  s" n) M( A- z1 v! c        发送优先级设置:, g; y- ^! v, V
A、扫描所有激活了发送请求的发送缓冲区
$ Q: t9 w( k" a0 W9 w/ r# e/ T# rB、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据7 _, a7 e6 c' L7 c# k  W

( z6 m. m. `1 u: o        再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。" i1 p' ~6 `8 N6 g6 |

* g( e% r5 k* n- s$ b9 e        FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。: K$ n0 H1 s2 c2 D8 V
        发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。
) W) p; K  u5 L* b        发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。
4 h+ I/ z% {. ^+ f, u) T! V7 J. u0 L7 l2 [' M9 }" ?4 f; ]; s
        为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。
7 u( d$ a1 F3 i7 c% ^
7 d3 M6 O! G) T- V* ^: v$ J        CAN发送消息的代码实现:
. F/ R% d+ T9 O9 J( I6 l0 V, W. j! J" L
        发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:
1 n, w+ O; D) z3 n" A' u& F& D% ~+ C3 t  F4 F  I6 W
  1. PutIndex = ((hfdcan->Instance->TXFQS & FDCAN_TXFQS_TFQPI) >> 16); //获取元素编号
    ! G: x% P& v5 x( z; D: \2 B" i
  2. FDCAN_CopyMessageToRAM(hfdcan, pTxHeader, pTxData, PutIndex); //复制消息到Tx buffer
    / g9 B8 l, w7 U4 V3 n
  3. hfdcan->Instance->TXBAR = (1 << PutIndex); //发出传输请求
复制代码
! F+ k1 W! o7 @, A
        发送buffer请求寄存器FDCAN_TXBAR,描述为:
# \/ \9 o5 E4 m
8 k. e0 c- T  ]+ v* I  K9 E
# C9 z4 y! C+ K0 Q3 A% ?- p
1 O' Q3 d( V" g# T7 C
        此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。
+ d. K* }" @! d1 D, [' r
; m$ D4 u8 S2 {( C# c9 b4 j4 w7 d6 M/ r; {

- y8 M+ g  S0 z1 i. M, s# r$ t五、初始化; C2 k" v3 J7 u& B' L5 m

8 q1 F4 H5 t* R* Y1 L" c        初始化可以直接使用HAL库,* Q7 u% y3 s) b- h1 f8 p+ Q

+ ~$ g) B+ p* l7 y* P
  1. u8 FDCAN1_Mode_Init(void)9 ?6 O$ h$ @% R& @& r$ D4 {3 P0 D
  2. {
    7 ^% i9 {, J- ]9 K
  3.     FDCAN_FilterTypeDef FDCAN1_RXFilter;
    3 @+ ?* [4 ~+ w% E

  4. 8 a+ V6 c5 j; J6 x1 D  o- K3 ~
  5.     HAL_FDCAN_DeInit(&FDCAN1_Handler);                              //先清除以前的设置
    / N9 O! N) c( [) s0 [8 d
  6.     FDCAN1_Handler.Instance=FDCAN1;
    1 l" B4 H8 I% {4 Z* y. \
  7.     FDCAN1_Handler.Init.FrameFormat=FDCAN_FRAME_CLASSIC;            //传统模式' n- {; i' p5 S7 i  Y+ w* S
  8.     FDCAN1_Handler.Init.Mode=FDCAN_MODE_NORMAL;                     //正常模式
    / Y. j  j# O; C( W7 k( l5 {) P
  9.     FDCAN1_Handler.Init.AutoRetransmission=DISABLE;                 //关闭自动重传
    1 }1 o7 ^. w0 u( {
  10.     FDCAN1_Handler.Init.TransmitPause=DISABLE;                      //关闭传输暂停           
    ' K; |# l# c3 S9 J3 \% t: \
  11.     FDCAN1_Handler.Init.ProtocolException=DISABLE;                  //关闭协议异常处理
    & U1 V8 u8 }4 z1 r9 [" [) N: I
  12.         + F* e  E/ J# R
  13.         //时钟为200M,baudrate=200M/(NominalTimeSeg1+NominalTimeSeg2+1)/NominalPrescaler,这里配置为1M
    6 s( X% J3 i/ o4 l7 v
  14.     FDCAN1_Handler.Init.NominalPrescaler=10;                        //分频系数
    % Z' K. w- p) |  z  j! f0 A
  15.     FDCAN1_Handler.Init.NominalSyncJumpWidth=8;                     //重新同步跳跃宽度% b  b+ A0 i  M8 [4 T: ]* q; H1 m
  16.     FDCAN1_Handler.Init.NominalTimeSeg1=11;                         //tsg1范围:2~2565 m4 Z, s1 d9 _8 P' |; q+ o( q
  17.     FDCAN1_Handler.Init.NominalTimeSeg2=8;                          //tsg2范围:2~128
    $ G/ z2 A  Z1 ~. V: O# W7 x5 i
  18.         4 d) c: c3 P& a. C8 U4 f
  19.     FDCAN1_Handler.Init.MessageRAMOffset=0;                         //信息RAM偏移,10KB消息RAM共有2560字,故可以偏移0~2560% u' s6 ~0 l* R5 K
  20.         //使用了多少个滤波器就要设置为多少2 `) s$ k& s9 t1 m' z/ [
  21.     FDCAN1_Handler.Init.StdFiltersNbr=3;                            //标准帧滤波器个数,0~128! K3 J4 d" l7 Q, c1 f
  22.     FDCAN1_Handler.Init.ExtFiltersNbr=2;                            //扩展帧滤波器个数,0~64' O( J3 _2 O! M7 ~
  23.         
    : V' P9 f2 J! i% P5 M
  24.         //接收FIFO0、FIFO1和buffer配置,此处没有使用FIFO1故个数设置为0
    / j& z4 q% X4 U) W8 r% J. O
  25.     FDCAN1_Handler.Init.RxFifo0ElmtsNbr=64;                         //设置接收FIFO0元素个数,0-64& M) r6 Z: v7 t! T3 g' a6 c
  26.     FDCAN1_Handler.Init.RxFifo0ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO0元素的数据域大小:8字节        
    - }. u2 [9 e6 V( s5 G5 @
  27.     FDCAN1_Handler.Init.RxFifo1ElmtsNbr=0;                          //设置接收FIFO1元素个数,0-64- x) d6 v! Y( t8 w. V
  28.     FDCAN1_Handler.Init.RxFifo1ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO1元素的数据域大小:8字节               
    & s1 k- `$ }0 ?+ f" v9 {
  29.     FDCAN1_Handler.Init.RxBuffersNbr=64;                            //接收buffer元素个数,0~64
    # ~% I6 B* G/ y: u9 j) m
  30.         FDCAN1_Handler.Init.RxBufferSize=FDCAN_DATA_BYTES_8;            //接收buffer元素的数据域大小:8字节        ! p( ~$ u, r1 L5 u
  31.         
    ! v. D' @$ E. Y& A. \0 |
  32.         //没有使用发送事件FIFO功能,故TxEventsNbr设置为0。把发送buffer全部作为专用发送buffer使用,故TxFifoQueueElmtsNbr设为0.
    . H4 p* y7 R: {, ?4 c' P
  33.     FDCAN1_Handler.Init.TxEventsNbr=0;                              //发送事件FIFO元素个数,0~32: Z& |2 k( P" }& q( ^* Y- ]
  34.     FDCAN1_Handler.Init.TxBuffersNbr=32;                            //发送buffer元素个数,0~322 Z4 l/ P" q9 g) T4 D
  35.     FDCAN1_Handler.Init.TxFifoQueueElmtsNbr=0;                      //发送Buffer被用作发送FIFO/队列的元素个数,0~32: M# {# ^! T& D  K3 r
  36.     FDCAN1_Handler.Init.TxFifoQueueMode=FDCAN_TX_FIFO_OPERATION;    //发送FIFO模式选择,可以选择FIFO模式或队列模式9 d" q/ `, ]7 H
  37.     FDCAN1_Handler.Init.TxElmtSize=FDCAN_DATA_BYTES_8;              //发送元素的数据域大小:8字节# g" @: R3 S# R5 T( \# F4 O  O9 r
  38.         7 L% T4 O$ R  ~' ~: N3 I
  39.     if(HAL_FDCAN_Init(&FDCAN1_Handler)!=HAL_OK) return 1;          //初始化FDCAN
    , v* M' j, S. k2 v6 Z3 p; v  j

  40. 6 O" J  j3 [# }' e" G, @% ]
  41.     //配置RX滤波器,标准帧   % S) |  _* q6 [; w: F6 z
  42.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    ' i3 @5 y$ @& I2 \( L
  43.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                   , e; x; w0 N. C, `% I+ J% P
  44.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    ( N: v: \+ Y# P9 I
  45.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  ; {" w- X3 M+ N
  46.     FDCAN1_RXFilter.FilterID1=0x112;                                //11位ID3 [8 u) M; ]. X5 c/ ^. q/ h
  47.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码) H5 N8 r2 X' B  @
  48.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化  ^; n9 @: ~/ V
  49. , n, r9 O. m! ]) Q; s8 U
  50.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID1 t6 T( m7 N) e6 }  E' M7 ^
  51.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                   4 R  M- p/ H+ {& H8 v
  52.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    / q( {3 ?/ K  h& D" }
  53.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    % V7 w5 x# Q" t! X/ s/ t2 z5 M
  54.     FDCAN1_RXFilter.FilterID1=0x113;                                //11位ID# `3 s2 j4 R1 F" m
  55.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码* A- P' R$ h; k. v6 v
  56.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    $ i  |2 i! E5 L) C* c
  57.         
    / L+ w2 y# j# F9 g" }! B2 \$ p( `
  58.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    6 ^( K: V& L0 W6 B- f$ t8 r
  59.     FDCAN1_RXFilter.FilterIndex=2;                                  //滤波器索引                  
    5 h% @0 _% ~, c' ~7 j: ]; H; t
  60.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型! q6 E- _# X" @4 b3 J2 h* M  Y
  61.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  ; v3 H2 S1 t. e' N5 M
  62.     FDCAN1_RXFilter.FilterID1=0x114;                                //11位ID# R0 F, b! ]/ F3 U
  63.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    ; r: @) ?/ V- g0 l
  64.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化* V+ v8 ^0 |. R/ d1 ~6 ?
  65.         
    * Q7 Z6 _" c9 `7 i2 a- c
  66.         //配置RX滤波器,扩展帧。标准帧和扩展帧的滤波器索引是分开的   5 M$ T1 W2 p  u! c$ ]2 x$ U& D
  67.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID. s- V, o, K# d# i
  68.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
    2 T. }( o3 h8 ^) R+ n
  69.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    , R6 x4 [) |3 R
  70.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    $ D  b- y. j! h$ j0 ]: @
  71.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(2 << 12);                  //32位ID
    ) i. v1 j! m2 Z# ]$ S0 i2 i
  72.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码. T. k7 {. x1 j5 A& d2 M0 C
  73.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    & ~* t. P! F- K- w. o' h8 d

  74. ; }: [; {& \) e8 ^# @
  75.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID
    & A) n: ~- z8 V5 @2 h
  76.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                   + [) G3 F- D- Y5 Y1 I+ W# U
  77.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型, [9 o1 |1 Q0 `
  78.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  + m! k3 y+ a3 t: N1 o2 H4 h
  79.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(3 << 12);                  //32位ID- U* d$ |* A' D& A9 b8 x
  80.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码) v5 V! ~8 C4 J
  81.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;    //滤波器初始化
    * E" G) o- A/ ^- u4 S& Y
  82.         + G; u5 ?, v' c5 o7 T$ x
  83.         //滤除的消息直接丢弃! h) w3 g4 w: t. }/ O; s8 Y2 T
  84.         HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler,FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);  //设置被滤除掉的消息的处理方式
    ; j* l) Z6 F0 e
  85.         
    % L8 J$ C) w2 j2 Q6 |3 B
  86.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);   //使能新消息接收中断1 ?4 l3 j( x( d2 k( I7 F
  87.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_TX_COMPLETE,0xffffffff);   //使能消息发送中断,0xffffffff表示所有的发送buffer都触发中断        , O8 x* B' B: {8 C" ~; o
  88.         
    ; @+ b2 O/ q! z1 {
  89.     HAL_FDCAN_Start(&FDCAN1_Handler);                               //开启FDCAN
    ! y  O4 I, w/ c
  90.     return 0;
    % a3 K( ^' I; o  z: p8 Q% Z
  91. }
复制代码

6 a8 F! B& ?# N. p& C7 Q+ }( |* `3 ?& z0 ?
六、CAN发送示例(来自正点原子)
7 t1 m" I; M' E& P# j# R5 d
& F; ~5 d5 m% q: G
  1. //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)        
    $ O) B1 X- ?0 L: K2 _: T
  2. //len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8                                       |5 B: e4 G0 d" {9 V9 [. B
  3. //msg:数据指针,最大为8个字节./ m! a" k+ y% z  @1 M* y
  4. //返回值:0,成功;
    + }$ h; f2 ]1 c/ t* u
  5. //                 其他,失败;; u7 ^) I( C$ L" e( D, A8 b$ o' g
  6. u8 FDCAN1_Send_Msg(u8* msg,u32 len): ~  P' f4 L0 n  C4 V
  7. {        6 k6 v* m" i9 o5 }& l- T
  8.     FDCAN1_TxHeader.Identifier=0x12;                           //32位ID
    $ @% c6 Z8 n) e; T* _$ M- k
  9.     FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID;                  //标准ID
    - ~% B$ I1 v2 _0 M! M/ M6 R
  10.     FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME;              //数据帧
    . c; u9 A' z9 ?8 o3 `  V& H# M
  11.     FDCAN1_TxHeader.DataLength=len;                            //数据长度
    1 h6 b; Z' X/ J6 d3 J1 ?
  12.     FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;            ) `2 E4 L9 T1 Y/ Q
  13.     FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF;               //关闭速率切换
    % w! B9 N* O5 u( x
  14.     FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN;                //传统的CAN模式
    8 ~1 z! _1 b1 N  Y/ R' p
  15.     FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS;     //无发送事件
    - {4 T' ?0 L# ?, L! l
  16.     FDCAN1_TxHeader.MessageMarker=0;                           
    ; R- s7 @: _5 k8 |* s, Y

  17. 9 b9 u  v# I$ }0 k! Q# x# ~
  18.     if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送
    ! B; d1 e* F5 o) C& o' E+ R0 \
  19.     return 0;        
    3 F& L# w0 D( \5 p1 P, x
  20. }
复制代码
) J, x. y% |% Q+ I5 n
七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)+ l% t9 G  k# G# a4 f) L) O

1 ]- J0 |+ U3 G
  1. //can口接收数据查询
    $ \0 A, |- N; S3 E
  2. //buf:数据缓存区;         
    2 i& H9 R" u, k. P
  3. //返回值:0,无数据被收到;( @9 s+ o! Z, S0 U0 s5 J  Y
  4. //                 其他,接收的数据长度;
    : Z: n+ m1 ]$ p5 R% R
  5. u8 FDCAN1_Receive_Msg(u8 *buf)% `/ P$ n6 E' O8 k
  6. {        
    6 f+ n6 A/ j: b* p
  7.     if(HAL_FDCAN_GetRxMessage(&FDCAN1_Handler,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf)!=HAL_OK)return 0;//接收数据8 T; c* f( X4 O2 G; u. {9 O
  8.         return FDCAN1_RxHeader.DataLength>>16;        
    ) @$ B, [0 ^$ v+ ]8 \& l
  9. }
复制代码

: b: V% W5 n( E0 d4 u% _4 `参考:《正点原子》  STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准)
6 k7 U7 O0 Q# `( d' ~" k) q9 |( n

( d# H7 Y( A& Y/ |4 @6 L7 W/ c
收藏 评论0 发布时间:2021-12-22 14:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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