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

【经验分享】STM32H7的FDCAN

[复制链接]
STMCU小助手 发布时间:2021-12-22 14:00
一、介绍
0 m  t- W8 V2 n9 H
$ ]4 M0 V( @. ^& @( Q* k  u        FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:( j6 Y3 ~3 A6 ^
2 F8 R( T6 F' ^
       1、每帧数据段最大长度由8字节上升到64字节。) N. n$ |+ k8 ?- Q! |9 ^9 l$ Y" p
: Q! f/ k7 {- a/ W- E# i
        2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。. S4 E/ O$ p5 V" h& \

8 K1 t/ |" K0 i6 [" e* O        3、向下兼容CAN。% G0 M. {; P/ s) ?' ?+ D: {" B
2 ^, H6 v- d9 W8 J4 f# J1 Z1 ~
        H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。
, x$ L/ L' O% h1 v! B& {9 x: l7 G7 U& E+ X; D, r
二、结构& @4 Y) A6 p8 U- G

& v  C: _  D/ k1 g0 K        先看结构框图:% T, J$ Z9 `* n. u, G, Y' U7 j! z

6 U, `5 t" _8 `+ j. E+ W' h
20191121162856859.png

9 l" I( M( t3 @; m
0 p$ Z: f) u+ }" H% [% `% N0 c2 L1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。' W3 }3 z% a8 d- F0 Y# c3 U

' h: {+ B" d& H3 R" L% n
20191127102816991.png
/ y0 u' j: r2 [; o
( O/ P+ A7 W& @- b" J1 m
        可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。
: \1 ^3 C5 K% _# U4 p& P/ T% A% ~+ }" y! x% Z- K
2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。
4 p) n: f  ?) J( }8 ^8 U6 d- ^5 [9 J/ g6 G* N7 e
3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。
, C8 e2 j: p% t& {5 p
3 F4 z/ \# B; H* o4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。
) H9 Y, T4 E5 r. d( O0 k
7 \, ~! M9 c/ k6 U! s5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:, T1 z- X: H% ?; p- c7 B, G
) X# {/ B3 m! T7 K
20191121164211703.png

* t; H# ^  X. {+ e* b( @) Z0 A% a$ P4 o9 ?6 t
        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字。
0 t  U# }% ~+ r5 k. s! B) q
  R- R0 H% J, a& y. b        1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。
8 y' H* V0 r/ j% ~8 o5 _3 T* ^+ T- e1 o9 R1 U
        2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)
% ?: `% Q: B) F( n- J$ P# g6 g' i; _: b0 U; A- ?5 |' _, k
        3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。7 c% ]1 r4 M# ~; ]" y2 ^
2 c( s) T& D: G# V6 a3 U: M/ f6 ~; F5 |
        4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。% _% l. S$ `7 C# i; x

# C. z2 K7 Y* \2 T  i" p6 c        5)、RXBC.RBSA:接收buffer,最多可以有64个元素。+ E  f; U' R5 }) o; z# E) A
; d! b  k2 ~0 J0 v: m. f
        6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)6 Y  |4 p# s$ G5 h

, b) n  P& Z  e" b        7)、TXBC.TBSA:发送buffer,最多可以有32个元素。
$ d1 `( ?  B; \, x2 M8 z; Z: i( m$ Q9 L
        8)、TMC.TMSA:触发存储器,最多可以有64个元素。/ }# J3 q! h1 g4 `3 ~

8 ~! y; V' B3 B" s《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》/ ?1 {0 P: O2 a. u& b, a* @3 ?% N
. s6 z. H  e# c# _
        下面来详细介绍这几个功能区域:2 m' z/ \* L; e4 [9 g
9 D# c. L' A* e3 K% r, M3 j/ Q: Y! Z
1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:1 B5 W9 {% m/ S8 k

* r$ |& f+ V9 Z' _+ G( B7 p
20191121170625565.png

6 H4 o9 B$ ~! U' S2 W+ a1 A% b) I7 r' a5 V$ Y2 U* o
SFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:5 y$ Z% u8 h1 ^6 d$ {
    00:范围过滤,过滤到SFID1到SFID2中的所有信息。
- Y/ L" i3 I3 y; Y0 i    01:双过滤,过滤到SFID1和SFID2中的信息。% g4 v+ R( s: [9 r
    10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。) M: z1 \: s% }) F) f% m* p! J8 L! U
    11:禁止过滤器元素
, R4 n$ g3 \9 ]: j4 ]' k% cSFEC[2:0](bit29:27):标准过滤配置
) O" E) ^8 W# ]  p6 B  V0 f* {0 y* y    000:禁止过滤器元素7 |) D( X' X4 K$ _2 \/ `7 S
    001:当过滤匹配以后存储在Rx FIFO0中。
) L$ c; |; w$ k! i7 ~# F    010:当过滤匹配以后存储在Rx FIFO1中。* _/ z. O. B, h% N  A
    011:当过滤匹配以后丢弃。
- K- B' i4 @' _- {  w0 V! M' k    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。
) I  K7 E6 m+ H, {    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
3 A5 C) C: M% G. _    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
% V8 m2 k/ }! P7 ~# z$ H7 L1 H1 }) w    111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。
( s0 J$ k3 B1 C5 s; U# }SFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。
* T  J% u% O  vSFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。; w6 n: O# ~" M$ ~
/ Y  r9 {) p3 o& k; U) L# s
SFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。
# H! L1 {0 J0 E    00:将消息存储在Rx Buffer中。. K- P& L9 Z8 C, c/ {$ w
$ a  @6 U$ e& l( h4 ?. O
    01:debug消息A。    ; X+ K3 I% y+ \2 [5 B
& z' t) U' ~+ H0 _( V
    10:debug消息B。
; n! d  z) c' f: X/ W" ~
# N; Y7 l3 Q2 g* M9 _/ W% d    11:debug消息C。
# G8 |/ |9 o8 Z: f; O7 G1 Z) k( H  j) E. c
SFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。
3 @! j5 X* g3 l# J$ ^
$ W0 Z% {* u7 y9 s- BSFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。
5 U  y# B! Z% U1 k" b! w& B# d8 X& P' R9 X" d$ ?* n2 R+ B
2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:
4 X& J1 t2 Q. _8 C7 n/ S$ J7 t8 E2 R, o
20191121171836710.png

7 D# c( O( a& G8 T8 S! F9 w
# h+ R& R6 Y; U: lF0 EFEC[2:0](bit31:29):扩展过滤配置
: o. Q$ @' U4 u# S5 a+ l    000:禁止过滤器元素& _" Z- [  {6 b- \$ p* C
    001:当过滤匹配以后存储在Rx FIFO0中。
! R' N# y$ M4 }* p) T3 N; E    010:当过滤匹配以后存储在Rx FIFO1中6 S# h, ]( f+ j
    011:当过滤匹配以后丢弃。
  B5 D5 v5 M  T- ]# h( F3 ]    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。5 J- T$ w. N0 K
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
8 [* ?- A7 H1 Q: S+ x7 u6 v! t    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
+ Y# @8 P6 [: B/ g! V( I2 K    111:存储到Rx buffer中,EFT[1:0]忽略。7 z/ S- P+ l* U2 A3 h! ?
F0 EFID1[28:0](bit28:0):扩展过滤ID1。( t8 s$ d6 J8 x0 x' P
F1 EFTI[1:0](bit31:30):扩展过滤类型4 @& Q7 b! B! T3 N; ]/ n
    00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。
% V9 D% l# C6 g5 I    01:双过滤,过滤到EF1ID和EF2ID中的信息。
# ]6 n1 x2 G" D    10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。
, x" |6 N$ A: |    11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码% B. m+ z  I+ v9 E8 H, u
F1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。% V6 P7 [5 N% f% Y% t
    00将消息存储在Rx Buffer中。% Q3 T( z) c( E  C7 K; k
    01:debug消息A。: l) Y2 }6 u6 @0 b1 \# k* ]& A3 _1 [
    10:debug消息B。2 v' g( }" G/ q+ b8 w
    11:debug消息C.
9 S& E. K8 ~: A& ]F1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。8 ?/ O& A( w: N+ W5 c+ Q7 j
F1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。
  e+ t. M; i& S9 \5 z  Q% V  S/ L- t9 C& `4 `8 q; K. E, v
3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:
, Q( O9 Z4 d! L' o( s
2 j" a+ r4 n; J
20191121172451477.png
* A8 P% t* G: H/ J' Z4 a2 u

) _3 N" B+ i+ n/ b% J5 HR0 ESI(bit31):错误状态标志# ]! R- m0 x+ K# G+ |" ]+ Y- o
    0:传输节点显性错误。
. J& B. p, {9 O; e6 k+ V: M' }    1:传输节点隐形错误
0 w+ K4 R6 J/ Z( L! K( F0 S  ZR0 XTD(bit30):标记是标准ID还是扩展ID
; J3 T! c' w* K1 l# E    0:11位标准ID( j0 o0 v6 g% z( l) M
$ Q! E7 b9 f  z$ y0 }
    1:29位扩展ID( q% T" o: n6 P. B$ N

$ P/ ?. r& E. e1 q% W) H- rR0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。8 D4 S" k/ i5 Y6 U, f+ N
    0:接收到的帧是数据帧
: p* p# s+ ]$ Y
9 @7 O0 [0 q. D1 f1 A    1:接收到的帧是遥控帧, \/ C) e7 _4 d5 d2 P+ `$ G
R0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
$ d" ?& w- C9 |4 jR1 ANMF(bit31):
8 N/ D: y# B! b5 g" @
" @# e! o& D* V9 N) W- ~0 E& I    0:接收到的帧和FIDX中的过滤帧匹配- }; r5 b9 f8 o+ A. m4 i% P
3 \7 y8 G# R. t& X. g' s3 |
    1:接收到的帧和Rx滤波器中的任何元素都不匹配
# I1 F2 V& V1 p/ u/ @: L# `R1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。7 x; N2 `7 X% |* _5 u% e
R1 FDF(bit21):帧格式% b' K7 }9 u9 T% g0 d
    0:标准帧格式
6 k, o1 g. |/ F8 E- S# g    1:FDCAN帧格式。
  F( K/ m, S8 [, K5 c% o. KR1 BRS(bit20):比特率切换
% s- a4 }( n9 Z$ i    0:接收到的帧没有比特率切换
/ _8 u( Y' D/ J+ L1 A5 b2 C/ X) l1 J
    1:接收到的帧带有比特率切换
6 }, S% j% d4 f" m/ rR1 DLC[3:0](bit):数据长度。+ f6 Y; p( `  w
    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。5 Y5 y# E; r2 ]+ t
    9-15:如果是传统CAN,接收帧长度为8字节。
& {5 Z; M6 V" @2 b( l    9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。
. V2 V) ?/ x. M( v* M! D& w6 VR1 RXTS[15:0](bit15:0):接收时间戳。" ], G  i" f  B
R2-Rn:接收到的数据。3 H* m) x  K( M* g) I& S+ ~

* E4 k( }2 B$ q) G( x9 w+ E4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。$ y+ J) {" h6 y/ G% [7 ^( L6 Z" D" H; B% B
. z' J! x, b& O* \- y& m
20191121174147402.png

: X' w5 c( W4 ?* Q5 h7 }4 B. U6 I% @
E0 ESI (bit31): 错误状态标志' ~8 K  X9 P( |/ j5 S
  q2 Q0 u* ^$ Z( c. `  _% m/ E
    0:传输节点显性错误
8 ^5 |8 W# f- D# W1 M    1:传输节点隐形错误。+ N3 A* j: q$ g. E6 @7 _5 t
E0 XTD(bit30):标记是标准D还是扩展ID
0 Z: \0 S& T' u& o* U    0:11位标准ID.
# M5 E. {; p2 l& L( G$ y# p  q1 P    1:29位扩展ID
# ~! p% ~* b6 x" x  |) z2 H, j* ?' [9 z/ R. k; |. e
E0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
( b% o% ^  J, l$ ?* K    0:发送的帧是数据帧
  l( e% w3 E. H' B    1:发送到的帧是遥控帧
8 m+ Q$ z4 y/ e) V! QE0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。
% h( ]) K6 B5 \% eE1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。, r. J4 g9 M4 \! |
E1 EFC(bit23:22):事件类型
& n+ A9 g; _2 L: x    00:保留。2 b6 Z/ V! g% d
    01:发送事件。5 K' H* Q7 A* Q2 _
    10:发送取消。
8 f6 l3 s) z' p3 z" G1 Z! u0 k    11 :保留。5 t* e' h2 ?: @, i
E1 EDL(bit21):扩展数据长度- W7 `. V% H7 @0 J
    0:标准帧格式
; d2 _- e2 M- g) c( O1 H    1:FDCAN帧格式。
: A' |; G9 [5 C0 k2 Y- d' vE1 BRS(bit20):比特率切换
$ g( Z9 m& [/ Z( C+ J7 @    0:发送的帧没有比特率切换- j' @, R% b& p, D% B

" L) L& c' r1 C* |) G    1:发送的帧带有比特率切换% y+ f) A% _( @% F
E1 DLC[19:16](bit):数据长度。
, t; J; A7 B7 B  K    0-8:传统CAN+CANFD,长度为0~8字节。
4 |( |9 z+ E% g8 C' n8 Z0 Y    9-15:长度为8字节。% r4 g- N: X+ z2 j4 y( O
E1 TXTS[15:0]bit15:0):时间戳。' Y9 N4 |0 _  g. G

/ q# b3 `: C: r% Y3 X9 e# l& s# `5)、TXBC.TBSA:发送buffer,有32个元素。5 O* s$ }3 [- X7 U

) t  [3 Q  f6 _9 o) X+ J' [
20191121175121493.png
6 J' g. L8 K! j6 r/ L+ _5 x

: }  s; \; q) A/ O$ M0 S) A! RT0 ESI(bit31):错误状态标志
( s5 b: v" x/ U! H* N4 q    0:CANFD帧中取决于被动错误标志。: O5 r2 m8 R* p1 }# i" ?9 T
    1:CANFD帧中隐形错误。/ z7 h" z' e. l& G, T3 T
T0 XTD(bit30):标记是标准ID还是扩展ID
/ V# R  G  Z( V- d* T, ]7 b" h    0:11位标准ID
; ]# N7 ]! N& O# _4 X) E& o1 r* y7 L/ m9 @3 y
    1:29位扩展ID6 y0 m) Y) [! \3 m
6 L" T( _# i. y% W% E7 e  }. W! B$ v
T0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
- `0 b* y8 h5 S' [: ]. c/ e  Z, Q    0:发送的帧是数据帧& [0 D3 h& {. O' z6 Z
    1:发送到的帧是遥控帧3 s  {1 o( e% e
T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。$ M5 A6 q( x% A8 p, \
T1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。
& p1 y, s2 e" S# @. z/ O1 D, N* xT1 EFC(bit23):事件FIFO控制
4 v# Q8 `8 [$ v* ]  e    0:不存储发送事件。
  T$ W; F& }/ i; i9 E$ x    1:存储发送事件。7 Z7 Q; m2 F6 T9 A' d4 z7 b* `$ ^
T1 FDF(bit1):帧格式, C( D9 P# k! w4 E  M
    0:标准帧格式
* g  j7 c: a2 r# j; ~6 G    1:FDCAN帧格式。( r+ b+ Q; f4 ?( x
T1 BRS(bit20):比特率切换! D& A$ M# T% ?
    0:发送的帧没有比特率切换, [& w) c; m/ j: M* b, f# i" q0 z5 ?2 k

0 l# x3 O0 N5 }+ b' |- J4 Q    1:发送的帧带有比特率切换
4 M+ a" t. [# y( s7 H0 BT1 DLC[19:16](bit):数据长度。
9 p" R, `, s5 k8 e& l, c    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。
; i* o9 }, a1 ~8 r0 Z- d1 [) L0 Y    9-15:传统CAN,接收帧长度为8字节。
* H5 r! h7 Q$ [% b% ]" x    9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。; Q& G2 G% H7 d# m1 F2 V# o
T2-Tn:发送的数据。/ _7 s! C+ d9 r3 Q# H7 g

, e$ x$ y& g) \& @0 I3 a/ n/ }三、过滤器设置
1 D8 y  {5 x* {, G! T( ~% ~/ X
7 Q( j0 x# `' U% ]. W2 ^        标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:
* K- d8 d4 M- B) y/ Z+ l- Q  `. z" _( b6 E- l$ l( r6 A
        1、指定范围过滤% ?7 ?+ v' ]7 O6 @( v. f
        通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:' I# b+ ~0 x/ G/ e
$ L2 P8 G$ H- c9 |9 U) K
  1. SIDFC.FLSSAn.SFT=0        //范围滤波; w! h  f% ^: L% H3 H" @% i+ c, u
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到RxFIFO中
    2 }% K: [0 E# m1 }
  3. SDIFC.FLSSAn.SFID1=0x123  //ID14 D2 b# ^8 s- V% k8 o9 G
  4. SDIFC.FLSSAn.SFID2=0X321  //ID2
复制代码
# s9 N/ b+ s& \& ^! K, w
        2、指定ID过滤. k9 X; Q: G' ^; N  K. ]$ f4 M1 I

6 e& @5 D# l1 r9 j        我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:
/ O2 ]! o2 ~% Q! z. n( Y
. ]. R/ Q; @2 M$ [
  1. SIDFC.FLSSAn.SFT=1        //指定D过滤) ~! e! N+ `0 S
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到Rx FIFO中4 ?* d: z" j$ W4 k1 }0 S
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    0 _# |2 T% P5 Z
  4. SDIFC.FLSSAn.SFID2=0X123  //ID2
复制代码

' c; [$ J5 o0 }% D1 R* e9 o" x) [+ p        3、传统的位过滤& M4 U6 p/ S6 X% ?
        第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:/ `# n7 W. U) ?' I/ ]9 K: A
, Q5 H3 S& L4 |$ x1 o( ]1 F1 g
  1. SIDFC.FLSSAn.SFT=2          //传统位过滤
    ) n, _6 z) ?5 B3 b5 {0 H  K
  2. SDIFC.FLSSAn.SFEC=1         //如果滤波匹配成功的话将消息保存到Rx FIFO中: `5 L8 y8 V" q, m3 h
  3. SDIFC.FL SSAn.SFID1=0xFF00  //ID1. W5 F* x! [( Q/ s
  4. SDIFC.FLSSAn.SFID2=0xF00    //掩码
复制代码
( X# w+ P2 `7 X
        其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。
( d8 R; _! w) U' P3 `, J
; x8 v: G' F2 l/ d" M. [! ~7 J( k四、CAN的收发过程
, z0 V1 o" \* v6 Y, G" v4 H$ L7 ^7 p" V3 g* R+ {
        1、CAN接收过程
/ O; O6 Y7 t5 ]& ?
8 n& @; I, @/ M        CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。7 f" d4 G8 M$ _/ J; y6 I

, Y$ n6 L: ~( D8 T, k& o        接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。
0 d$ i/ r5 |- x! D0 Y8 s* p8 X+ N# Z% l# H2 s$ h) I" Z! S' j
20191127114550965.png

# i1 E0 q4 j1 [# T) I4 I
+ }) Q$ g6 P+ w6 Y+ p        当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。
% T8 F) Z* g& S3 ?0 L0 ~" j% Z* e4 F( p1 {6 W. n3 `# ]

9 l7 E/ u2 z3 d
20191127111235302.png
2 O1 r& \% c( R7 {8 x& G

, d: c" D' Z0 [' C        为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。
. s6 ]" j3 b8 r% c5 i$ Z; ]        读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):
( S( l8 x  K  u) ]$ R. W9 M. v- I2 V  V+ u
  1. /* Calculate Rx FIFO 0 element address */  |% |4 Z; R6 T
  2. GetIndex = ((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8);
    8 g8 a6 V' H' a1 Q
  3. RxAddress = (uint32_t *)(hfdcan->msgRam.RxFIFO0SA + (GetIndex * hfdcan->Init.RxFifo0ElmtSize * 4));
复制代码

( i- r. z1 Y+ H9 u1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。
. _! [+ \0 {' F3 L* A5 f) N
+ I8 I* g" l" P! G
2019112119095892.png
& R4 I  ~/ ?5 `* Y) w

. B) U& u1 [  {2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。" _8 c. G' l# ~- |9 N; B6 m

/ U+ o! s1 v; M3 b3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同2 {7 b- a! y  K1 ?( x& z! [) |, @( \

0 q9 C0 |9 h5 T0 G

! S! x( a0 N: }( U& Q3 _8 u: u) H7 V. c. s6 l) ]8 c
如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。
6 c+ r3 {4 Q" y) T/ F# U- C. i" F4 c; {( L, s
如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。% I1 ~& ^7 V" ^3 f

1 w/ ], z# z1 @5 H6 N& P% K        计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:* }) A8 Z+ i! m1 R2 p9 y- X% E  Y
: [6 |# x7 V) Y8 ^+ w# r* i3 m
  1. pRxHeader->IdType = *RxAddress & FDCAN_ELEMENT_MASK_XTD;
    / k1 M8 }! K* r; w
  2. pRxHeader->Identifier = ((*RxAddress & FDCAN_ELEMENT_MASK_STDID) >> 18);8 E; ?+ ?& F3 p/ ]! [; n, E6 `
  3. pRxHeader->RxFrameType = (*RxAddress & FDCAN_ELEMENT_MASK_RTR);' F0 c  G- |; }  z
  4. pRxHeader->ErrorStateIndicator = (*RxAddress++ & FDCAN_ELEMENT_MASK_ESI);  //这里地址自加了一次
    # n+ L$ c+ v# H
  5. pRxHeader->RxTimestamp = (*RxAddress & FDCAN_ELEMENT_MASK_TS);
    : B- p; q' Z$ {
  6. pRxHeader->DataLength = (*RxAddress & FDCAN_ELEMENT_MASK_DLC);! p9 |/ B+ n( S! T
  7. pRxHeader->IsFilterMatchingFrame = ((*RxAddress++ & FDCAN_ELEMENT_MASK_ANMF) >> 31); //这里地址又自加了一次5 z2 T: j, F9 g. ?4 p* p( _" C
  8. ......
    ) l: G* V% k9 F3 r( k! |0 f3 }
  9. pData = (uint8_t *)RxAddress;
    * L: N/ c; i& I. w! J, F
  10. for(ByteCounter = 0; ByteCounter < DLCtoBytes[pRxHeader->DataLength >> 16]; ByteCounter++)
    + q1 S6 l; v, q- y4 |
  11. {3 X# e7 z( c8 u' g( n
  12.     *pRxData++ = *pData++;
    8 s0 d7 Y6 J7 T* i( ]/ P$ [/ G
  13. }
复制代码

& }1 q7 V- u$ L" }8 Z8 Z        2、CAN发送流程7 ]5 H! R9 n% J9 O6 {& b9 s6 r

- E. s4 y# {1 o& `/ \        在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:
9 B* H5 w. v' |$ `5 j8 I+ b( f
4 t; }! p. |. ]3 G* \7 H: a( p
20191127150042199.png

4 S% D; ~: A7 |  ~( @# n; v1 ], L5 a- b! B4 A
        因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。$ U2 M: n4 T; T

4 S0 k) \; u, J. n7 L        下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:
' l: [( r* V' O: }
# [: c, `: l) G% T+ g        1)、专用发送buffer
2 K3 l  @7 r7 ~: A) L9 ]$ Y8 j- D! J3 d$ u7 m8 L
         专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。6 R! C! C" _8 e- o
        如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。; ~2 ]3 Y2 P( I% d/ [) E
2 C, e0 t% [( |' r
20191122101555794.png

, X6 a* t$ w0 ]
/ @0 B4 d5 H6 f" n' t$ z: Q2 c0 f        专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:! A+ H& ?8 M/ j) R3 Y% z( L8 C/ D

0 x4 ]- X5 E& l9 Z3 \: q发送buffer索引 (0…31) *4+发送buffer起始地址。1 V- V8 X! Z+ ^: e& ~# P4 e8 F* g
0 C1 T* C; G. r0 F3 x1 {- i
4 S+ r% \" `9 s8 A9 Z
" d( |( O/ m& E1 r) S
        2)、发送FIFO- `) ~9 \+ ]6 B, F/ T

+ F7 e, g% L' F9 |9 _# y" g4 u% Q" S        发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。& `$ i& d' U, |, M
        新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。
3 I1 R4 n# f8 u
2 \& _, e- s! r0 G/ y
20191127160804244.png

: Z3 T9 [- F1 [) L0 \6 ?  |
9 |$ @& d3 w/ I' b3 W1 {        发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。
* I- R6 M1 m. s) T3 W; W. W
! \. j; v. ^( x. h! |- j        如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。
/ p" ^- O: X+ [, T+ O        如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。" K7 `) Q0 s* O, Q7 [# X
        如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。, f6 s8 D' e  J; h/ p
        发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:4 x$ h/ R5 u- p7 J+ h$ _
3 ~4 v7 Q* {( }, t) M7 O
放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。( q3 q. J+ D# t9 c$ ]1 j* \

% ?+ f+ h% C- d& j        3)、发送队列% C( X! F+ d. v: x( y5 H  [! I8 p( R; M
        发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。
; O" I- j* q! w2 [4 n        新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。( r, \# F! {  Z' S
        应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。
& C: N* Q1 J, W; W) M$ X        发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是:
* \' L4 U. O; }- l" s" s
# }# g. B. M! x6 n% s- |0 L发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。6 N, g  C& ~* {0 j
7 H$ E5 X# x; K# p; k$ v
        因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:
/ C+ q9 P" j7 g7 l9 q4 V0 N5 S% v- \* P; w
        1)、发送buffer全部配置为专用发送buffer7 y. l; F* q9 `2 x& |" W1 Q, v/ \

/ a: @- C/ ^! X: `        每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。& Y; E1 c* Z$ o1 \  P% A- ~
* i+ \& s# f0 a- y/ D7 U
        2)、混合专用发送buffer和发送FIFO( {1 g6 ?2 {8 n
" z* \2 l+ F! }+ {2 ^( }( c
        在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。" u) C" ]" \9 h* F3 P) g
1 i' S. s/ P7 j9 z
20191127162943182.png

, M1 R2 Y1 d. O
8 W' F+ }; |. E' Q        发送优先次序:
- i" T, Y& S$ G) \8 nA、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)
' @+ E2 A% J2 D, G4 HB、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据% S2 t. t8 \/ m1 M( s

5 }( E9 `% Q' y& A. l% w        3)、混合专用发送buffer和发送队列
0 @3 ^1 ?9 F; O
+ {9 I. r( Z$ }: b2 V# e8 T! R        在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。7 V+ [1 |) C. W, `/ \

3 O9 y7 Q% s, {' Q! l8 _, q
20191127163202212.png

+ K9 J- X" `2 C
7 ~2 J6 b( l- i; [3 r        发送优先级设置:  @: k: h! O3 p7 O$ u" f
A、扫描所有激活了发送请求的发送缓冲区2 n3 G8 e% u4 p( s
B、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据5 L* k* O  b; [" g. R4 ]) q/ u

4 W0 T8 k9 V8 M: g5 E        再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。
) o2 |& V2 U0 D7 {+ _) ~9 }
0 y3 j$ r4 f/ X7 |3 H        FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。
; I: {! e3 }! M8 b        发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。
- y/ e4 `1 }, R, a3 _        发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。
( h( s+ i# J- x/ u' P: R. L; M. }# E- ^9 G) v
        为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。
. I* C' b6 \& e* p4 a/ S6 H' F' o3 V* J* }2 P$ c0 H
        CAN发送消息的代码实现:
; k: L4 m8 W: \# C8 s2 Y6 e  f
! B. j  t8 r  M0 _+ {. c5 R        发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:
7 s- S: [0 Z, `, \' y( U% w9 B3 T( ]6 m& P
  1. PutIndex = ((hfdcan->Instance->TXFQS & FDCAN_TXFQS_TFQPI) >> 16); //获取元素编号 5 A2 K1 u( i4 ?6 W4 o6 g* B( C* k
  2. FDCAN_CopyMessageToRAM(hfdcan, pTxHeader, pTxData, PutIndex); //复制消息到Tx buffer 1 v; ]1 a6 g7 r" ^0 C
  3. hfdcan->Instance->TXBAR = (1 << PutIndex); //发出传输请求
复制代码

0 Z4 K) U; C8 p! z: E5 p, f        发送buffer请求寄存器FDCAN_TXBAR,描述为:
) ~+ e# B* W  N* L+ H' x5 k
- `; @# ^) _4 Q& u  _$ C+ `

% h0 O9 q% a7 ?. c9 J9 [
% h: C! F  \8 f2 Q6 q' H        此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。( V; g% U% c7 }* `/ ~
7 U; g  p! g+ \) {

0 }- B* G3 L: }, z6 }5 {7 |7 E4 R2 |9 R  G) e( ^; D- {8 p% Q' F
五、初始化: |& j  _# H) t

; C2 B& T+ f% q& b6 T8 h% A  o& J# E        初始化可以直接使用HAL库,+ ~" x7 U" j: U! D9 z

, z, }/ n. ~! _) g! }
  1. u8 FDCAN1_Mode_Init(void)
    + C4 |6 J+ m  G0 `; b7 o
  2. {( Y* w7 T- w7 x3 N# X8 ?
  3.     FDCAN_FilterTypeDef FDCAN1_RXFilter;
    4 `" g! z" N- d; @0 b- ^

  4. ' ]5 g; W  @6 V1 r
  5.     HAL_FDCAN_DeInit(&FDCAN1_Handler);                              //先清除以前的设置
    3 E' N8 {8 [; ^2 N
  6.     FDCAN1_Handler.Instance=FDCAN1;
    , I8 s3 D4 g  A. U6 o
  7.     FDCAN1_Handler.Init.FrameFormat=FDCAN_FRAME_CLASSIC;            //传统模式
    2 |( k4 \& R4 X) {4 b
  8.     FDCAN1_Handler.Init.Mode=FDCAN_MODE_NORMAL;                     //正常模式
    ) G+ B" P0 l3 T6 O; _
  9.     FDCAN1_Handler.Init.AutoRetransmission=DISABLE;                 //关闭自动重传! f4 e4 p% P5 p. y  U2 _. Q
  10.     FDCAN1_Handler.Init.TransmitPause=DISABLE;                      //关闭传输暂停           
    & O6 X' x1 x$ |8 o. b: ~9 b6 `0 ]* }
  11.     FDCAN1_Handler.Init.ProtocolException=DISABLE;                  //关闭协议异常处理
    . t. h4 \6 D# l
  12.         
    # v: [1 P. H& f$ V9 f# i+ r" u/ ^
  13.         //时钟为200M,baudrate=200M/(NominalTimeSeg1+NominalTimeSeg2+1)/NominalPrescaler,这里配置为1M
    7 _# z7 P/ u7 ]8 |* f- k
  14.     FDCAN1_Handler.Init.NominalPrescaler=10;                        //分频系数
    ! [0 ]2 L6 E, m) N( d" L1 _! ~& y% ^4 o
  15.     FDCAN1_Handler.Init.NominalSyncJumpWidth=8;                     //重新同步跳跃宽度$ f  I! M  E5 s9 `# a2 f
  16.     FDCAN1_Handler.Init.NominalTimeSeg1=11;                         //tsg1范围:2~256
    ! z- w) G- s# `8 l2 B1 f1 p
  17.     FDCAN1_Handler.Init.NominalTimeSeg2=8;                          //tsg2范围:2~1282 y. ?2 g1 ^6 c+ d$ m8 R
  18.         
    1 \1 U3 v# w2 J+ p3 u" \: Q8 @
  19.     FDCAN1_Handler.Init.MessageRAMOffset=0;                         //信息RAM偏移,10KB消息RAM共有2560字,故可以偏移0~2560$ U$ P+ I) [" I1 c- D" G& P
  20.         //使用了多少个滤波器就要设置为多少
    ' @5 B8 {* U: D: d
  21.     FDCAN1_Handler.Init.StdFiltersNbr=3;                            //标准帧滤波器个数,0~1280 q' z9 E  M' C  t
  22.     FDCAN1_Handler.Init.ExtFiltersNbr=2;                            //扩展帧滤波器个数,0~64
    $ Y. l3 W$ c" C2 t" R" u& g3 b' H0 V
  23.         
    ; b, J: H% k# p' R& z$ F- w
  24.         //接收FIFO0、FIFO1和buffer配置,此处没有使用FIFO1故个数设置为07 O$ R" |1 B: U2 S4 Z; {, O4 g. u% Y
  25.     FDCAN1_Handler.Init.RxFifo0ElmtsNbr=64;                         //设置接收FIFO0元素个数,0-64
    7 d9 A+ I1 m; V0 a
  26.     FDCAN1_Handler.Init.RxFifo0ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO0元素的数据域大小:8字节        
    6 D) U; u, c) g/ c# c. {& L
  27.     FDCAN1_Handler.Init.RxFifo1ElmtsNbr=0;                          //设置接收FIFO1元素个数,0-64
    ) i+ I( k0 d; C5 ]' ^
  28.     FDCAN1_Handler.Init.RxFifo1ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO1元素的数据域大小:8字节                ! W% Y; }' i0 G2 `* V
  29.     FDCAN1_Handler.Init.RxBuffersNbr=64;                            //接收buffer元素个数,0~64. V- r( ]+ D- v8 I' V- b/ R
  30.         FDCAN1_Handler.Init.RxBufferSize=FDCAN_DATA_BYTES_8;            //接收buffer元素的数据域大小:8字节        
    + i0 w- {- L( q6 W9 z2 h
  31.         1 ^" b4 ?9 N6 L6 M3 j" D. A
  32.         //没有使用发送事件FIFO功能,故TxEventsNbr设置为0。把发送buffer全部作为专用发送buffer使用,故TxFifoQueueElmtsNbr设为0.4 Q2 p6 V% x/ A+ n3 S7 E
  33.     FDCAN1_Handler.Init.TxEventsNbr=0;                              //发送事件FIFO元素个数,0~32" L" q! f" z% G& H. z
  34.     FDCAN1_Handler.Init.TxBuffersNbr=32;                            //发送buffer元素个数,0~32- U. G2 Z' }* Z- v1 i+ i
  35.     FDCAN1_Handler.Init.TxFifoQueueElmtsNbr=0;                      //发送Buffer被用作发送FIFO/队列的元素个数,0~32
    ! d5 O+ |- x! ~( A
  36.     FDCAN1_Handler.Init.TxFifoQueueMode=FDCAN_TX_FIFO_OPERATION;    //发送FIFO模式选择,可以选择FIFO模式或队列模式
    , @$ X  a% p: Q
  37.     FDCAN1_Handler.Init.TxElmtSize=FDCAN_DATA_BYTES_8;              //发送元素的数据域大小:8字节" ]3 n* J3 u3 j! x7 u2 ^1 g
  38.         " e, }* |' f. L. Z4 F
  39.     if(HAL_FDCAN_Init(&FDCAN1_Handler)!=HAL_OK) return 1;          //初始化FDCAN
    ) s' u( }; ~8 n. {4 J+ \4 e( O
  40. # B9 a- I5 j! Y1 x
  41.     //配置RX滤波器,标准帧   6 J+ @4 M5 x  y; x
  42.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID) }9 X( I( H; [& d# e* c7 A
  43.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                   / W6 G2 D5 K( n& Z
  44.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    9 J/ @  a- x! ^) B- y
  45.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    ) @8 v. n6 U; Z9 J
  46.     FDCAN1_RXFilter.FilterID1=0x112;                                //11位ID6 Y$ E4 J( S+ x; x4 O9 b
  47.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码; U3 a4 o8 z& N; e+ ~. |) @
  48.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化8 {1 N# L% h/ Y4 [
  49. + ~+ |- @  u4 S/ W
  50.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    + e5 x) Z: x, E! J6 Q& E( W
  51.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                   ) w% u, V* }& D# H5 X# _
  52.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    ) Q2 H& n) X9 l5 |4 [% D
  53.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    9 ?3 B1 L. A5 K- {/ o, {) k( e
  54.     FDCAN1_RXFilter.FilterID1=0x113;                                //11位ID
    4 j. ]7 [7 P/ K& Y
  55.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    , t6 @7 y7 G7 f5 G* ]# k
  56.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化/ a8 M5 E3 A$ D1 s" {  r; h! f6 P
  57.         
    $ |) |7 P9 ^3 X  o
  58.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID8 v1 E6 W6 y% _% U, [% ~
  59.     FDCAN1_RXFilter.FilterIndex=2;                                  //滤波器索引                   - L  N; ~# S4 ~$ _
  60.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型; l; B# o# b: {+ W9 Z3 l
  61.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    2 E, E; C- M% a0 B: l% o
  62.     FDCAN1_RXFilter.FilterID1=0x114;                                //11位ID5 C" [4 e0 f/ a- ~8 W
  63.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    ( _! C7 [  Z, C# t; Z
  64.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    ! r$ H4 M$ n; h: H
  65.         
    / n+ G( m% L5 o2 i5 Y( n# ~5 J2 O
  66.         //配置RX滤波器,扩展帧。标准帧和扩展帧的滤波器索引是分开的     C+ J- K% ?3 K% d) z8 u$ T
  67.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID; a, F4 i4 b% |3 ]- `3 {/ z) L0 p
  68.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
    9 R3 g! m# d7 Z* a
  69.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    - a+ [4 Z$ @9 ^9 x' Y$ n) n
  70.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    3 U- ?. J6 o$ O$ n& Q# A
  71.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(2 << 12);                  //32位ID+ r. {4 [" e6 u* A
  72.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码
    4 f9 O1 x" I; a- {1 \1 M
  73.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    3 U) S  p! G# h( T6 H* a- \

  74. 2 }! G$ _& Q5 W5 [: B0 u& \% Z! @8 F- Z
  75.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID
    6 F4 b/ e1 P/ \# ]" x5 D
  76.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                  
    , W. S; D5 H) O1 `; m4 \, ^7 B
  77.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    , J: |0 v( l% E3 A: _
  78.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  * |6 }6 D1 R  z+ h5 }
  79.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(3 << 12);                  //32位ID
    4 f1 ~+ l+ ^$ \; \" ^9 Q4 G
  80.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码
    5 d4 e; t  }. A
  81.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;    //滤波器初始化
    $ I# x- r9 t6 `
  82.         
    ) Y0 j9 H) `  R2 \2 h
  83.         //滤除的消息直接丢弃5 T" s# r1 D0 Z* u3 k  \/ Z
  84.         HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler,FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);  //设置被滤除掉的消息的处理方式! i; ~7 f* o: B+ q& E
  85.         
    ( M7 i$ ^# r$ C0 Q
  86.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);   //使能新消息接收中断8 E# t8 T0 I+ |
  87.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_TX_COMPLETE,0xffffffff);   //使能消息发送中断,0xffffffff表示所有的发送buffer都触发中断        " T: \% m7 M: j+ e) a! {
  88.         + j4 d& c, `7 H/ u' P+ A
  89.     HAL_FDCAN_Start(&FDCAN1_Handler);                               //开启FDCAN, J9 [, Y. ]2 ]$ l% g2 h& k
  90.     return 0;
    0 _( X& c3 L( m; R+ T3 \' U% l
  91. }
复制代码
- e( h1 a5 j6 n) d

3 I$ r& z1 \) D3 \/ [. |9 G$ y六、CAN发送示例(来自正点原子)
& x4 b2 T0 g8 u2 e0 L" m3 j* D5 c( U, M% A8 x/ J) c
  1. //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)        9 c8 ^4 W2 m) O2 K
  2. //len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8                                     & Z' ?5 `' L7 O
  3. //msg:数据指针,最大为8个字节./ z' V0 i( X% y3 X) P1 q' _$ [+ M
  4. //返回值:0,成功;+ e+ I. S- D0 M6 r6 z" f
  5. //                 其他,失败;
    : y) G" h- P) w( R2 Y
  6. u8 FDCAN1_Send_Msg(u8* msg,u32 len)7 |) `* f: c3 f. U- o8 c' |! r+ a$ }
  7. {        4 b/ }* S3 Y5 Z5 A8 y0 |
  8.     FDCAN1_TxHeader.Identifier=0x12;                           //32位ID
    . i  C3 K! ]6 W
  9.     FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID;                  //标准ID/ M% K, K5 F; r' N9 e1 J+ E: Q
  10.     FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME;              //数据帧+ _+ A- B6 W2 o
  11.     FDCAN1_TxHeader.DataLength=len;                            //数据长度
    6 d2 ~5 X2 o" B3 Y3 W( }+ O% `
  12.     FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;            
    : K- T/ u" A$ N) Y( X4 w
  13.     FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF;               //关闭速率切换
    # ]& n# T+ t9 D
  14.     FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN;                //传统的CAN模式4 v1 G5 U7 A  u) v+ T& N; p6 _
  15.     FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS;     //无发送事件
    , q. W. }' g9 t& n
  16.     FDCAN1_TxHeader.MessageMarker=0;                           ! y8 b1 V; r, M( m7 ~( n

  17. 2 m  |2 K4 \: a) F2 G
  18.     if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送, c% @1 |7 ^" F6 M9 O3 f# E
  19.     return 0;        
    0 G$ _- i9 w0 J+ R! K
  20. }
复制代码
$ n; ~# j, b7 d6 a8 D. C
七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)* d' }! `& Z$ [, D3 C* T; a

1 }  D& M% E8 l* D2 ?6 e
  1. //can口接收数据查询
    / t" ~/ s, V& q. ?
  2. //buf:数据缓存区;         
    - ~+ U# I! e, {1 ^
  3. //返回值:0,无数据被收到;6 q) T+ ]3 r4 O. W; K+ a, }
  4. //                 其他,接收的数据长度;
    4 }, z2 ^  W7 I) [
  5. u8 FDCAN1_Receive_Msg(u8 *buf)* w$ w, [9 l8 r
  6. {        . {; u3 V2 a, M  {# `, Q
  7.     if(HAL_FDCAN_GetRxMessage(&FDCAN1_Handler,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf)!=HAL_OK)return 0;//接收数据
    4 f$ p! k2 U1 Z' D- n( I0 W
  8.         return FDCAN1_RxHeader.DataLength>>16;        
    9 U+ ^$ T0 U& y8 b. l3 h; u- \
  9. }
复制代码

0 n1 G. x+ Z4 }参考:《正点原子》  STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准)
* T- A. V3 d" N- G' c, l! t2 s1 s0 A8 f  [# `# |; z

  J" F8 _' ^9 i" `6 f( w5 ^
收藏 评论0 发布时间:2021-12-22 14:00

举报

0个回答

所属标签

相似分享

官网相关资源

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