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

【经验分享】STM32H7的FDCAN

[复制链接]
STMCU小助手 发布时间:2021-12-22 14:00
一、介绍
3 R" M0 N: G5 u7 [" K6 A
8 z! t* u$ W) `. V        FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:* o6 _* `6 F, m& t5 ^# m+ X

* Y% L* z2 @' V/ g3 r       1、每帧数据段最大长度由8字节上升到64字节。
# r. H4 c$ H) X7 X' S7 Y
: R: {, v& A# O3 h: P        2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。' C( y$ `, ]& M5 p+ G4 j
# z% y3 _4 \8 ~; a  s( I7 G
        3、向下兼容CAN。
1 {0 F6 k1 a- r) g; G6 o- K0 A0 T3 H5 y- H3 U9 F, o
        H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。+ E+ X- H( y. {2 `. |3 g

0 z$ U' y# ]$ X7 o' x二、结构
* @( n  @# k% w& Y' t+ i. k' q# D' x; d
        先看结构框图:/ P6 b' x2 S. N) T; W9 K

) p3 `1 U$ F) ~& g1 c
20191121162856859.png

2 ~; W5 k* [& n) z/ ]
( S( S+ p5 c; z1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。  X: [! v2 r- ~

7 r4 x& E3 T) Q
20191127102816991.png
- W& G1 [8 t; ~  J5 ]7 l9 M) H* D* `

. m' O6 E% [% h% w6 l# q! C, {  @        可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。
9 p& m9 a% i, ?7 T! @3 P+ c+ f. r$ |: Y1 S  l+ V
2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。
+ g: c8 ~' |! d% o1 s/ x5 H; f% A5 J! @0 N: P. \3 B4 v
3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。$ I2 A" o/ [! b4 E* C- k

' _2 D' ]* N( o3 E, w2 i4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。8 N  F5 c  L" Y% N/ a
) |/ c6 j& {& U! z) t
5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:$ H; f6 M7 [, s% O' X( O/ _

2 N! e- S# `- E& k* w# S  q
20191121164211703.png

7 X6 D. T& W* L( p$ q. ]0 \9 V. B' E' n# N% J: B' G4 `9 A9 q  {- C
        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字。
7 {) m3 L' X. R( {; D  w: [. T
7 f, I, n, e- F2 W  M0 i9 J        1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。
/ o) S3 {5 c% I0 o, a4 ^& u9 L
% n& e7 z7 [3 A: y        2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)
& A- E2 q/ M7 T) t/ e0 Z; |
% _2 |  U% i+ g' C, J        3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。
  I3 O8 x+ _! c/ m: X" b; q+ k: m: M0 E0 ~) c
        4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。
6 K: O, t  e. L+ |) q
$ a' d5 }3 S) Z& `  M3 q4 D        5)、RXBC.RBSA:接收buffer,最多可以有64个元素。3 F8 ?4 o" ^; ?5 `
9 ~; D' h8 h: z4 Z* M$ x
        6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)5 Z6 s" \" h% |
/ L! I5 a2 g3 v; _% c  d
        7)、TXBC.TBSA:发送buffer,最多可以有32个元素。9 b& W( [- T. J
1 e  ~; W1 e* B  I( t/ I0 h( Q
        8)、TMC.TMSA:触发存储器,最多可以有64个元素。
, S! t7 Q! p4 Q2 k0 ~3 A* i8 M; j. X6 L, P& L7 w# O/ h
《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》
0 r$ E; y, d3 g+ ]
" t" W: G5 A9 \        下面来详细介绍这几个功能区域:
7 D" q* _+ b# `% Y+ J; t7 j0 g" `6 q5 a5 D# ?/ Q5 _6 f+ p3 J1 k6 [
1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:
4 w+ Y3 w4 p! L" R: `" ^7 I1 g. H5 e' ~/ [' G
20191121170625565.png
$ v5 v+ c* z4 c% O# Q. `* r5 B

/ \% G0 d+ ^( B& c2 ^# r  uSFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:9 ~. e  Q) }8 b
    00:范围过滤,过滤到SFID1到SFID2中的所有信息。
. k$ [8 O  V0 V4 e% [    01:双过滤,过滤到SFID1和SFID2中的信息。
# {( ?% _2 }) n/ @# D& W( `8 u    10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。# Z/ _. i1 l  q0 E
    11:禁止过滤器元素) Z6 E7 ?' Y1 o/ v  W
SFEC[2:0](bit29:27):标准过滤配置
7 Y8 }: R! H% {- f" o. r, Z8 e    000:禁止过滤器元素
8 C  v: U( T+ s: g1 w7 k' l    001:当过滤匹配以后存储在Rx FIFO0中。4 u: L" c# z( g- u6 i
    010:当过滤匹配以后存储在Rx FIFO1中。
( o$ d& y2 t7 m( _' X    011:当过滤匹配以后丢弃。8 X0 ?( Y8 P' O- d; m
    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。. d. Q/ k) t4 @5 `
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
8 V' P$ T$ ]. b0 d& _    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
9 H+ i/ _. s7 v* C- ^    111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。
6 X9 s9 _* W# i: oSFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。
: w# I8 J( Y" e& g! o5 wSFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。% F+ a5 z2 t; M' x

5 V% y: F+ P# k7 d& e6 gSFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。
+ l/ a& O7 B& E% D, F6 l4 z    00:将消息存储在Rx Buffer中。
& D) S' e5 V  F  I; p+ e. \, I5 w
9 R% k( I+ v. m; I& ?    01:debug消息A。   
' p9 D. M% }' z2 H; D. J) Q4 T+ T- A, \% d" r8 T$ G& Z
    10:debug消息B。
$ J, R4 c$ k6 {! D! P9 e" K: F; ]+ g* `* Q* ?* G5 K
    11:debug消息C。9 q8 D. B/ T9 L. {8 x* C! j% D

( q8 r) h2 S2 c4 _SFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。8 ]7 |4 c5 Z9 n! N4 s1 O+ R

& T( a( O7 c! Q# }5 C: I. @SFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。
" K& l7 K) Z# T7 C$ U) U
/ |- S6 @8 o' t- C& M2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:
) j8 K$ [) v$ i: I1 _* l
. i: B8 ~9 L7 C1 J1 M! }
20191121171836710.png
; Y1 ^5 P4 g; U) @! e8 U
  B8 k2 q( l6 C- s, m8 t
F0 EFEC[2:0](bit31:29):扩展过滤配置
- D! \2 F( }3 _' N$ X: q    000:禁止过滤器元素
! U- J$ t6 R* U. o" x9 M6 r    001:当过滤匹配以后存储在Rx FIFO0中。- O. [& z" W) S9 q, T
    010:当过滤匹配以后存储在Rx FIFO1中
: j& R. C' E& m( z8 ~5 A( t, B- V    011:当过滤匹配以后丢弃。+ j1 s- g) ^$ X& w& u1 K/ ?
    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。  d2 j: a) k5 S
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。$ q0 I8 w$ a) k9 k
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
% }+ U7 ]; @4 c) `    111:存储到Rx buffer中,EFT[1:0]忽略。
) n8 H! u* ~& B- x) W/ w5 BF0 EFID1[28:0](bit28:0):扩展过滤ID1。
$ }+ j* q4 ^4 \$ S9 |: MF1 EFTI[1:0](bit31:30):扩展过滤类型' T9 r8 e1 o. V; x# y! ~8 S1 s
    00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。
2 {# Z# [+ w  N    01:双过滤,过滤到EF1ID和EF2ID中的信息。
6 u# U* y! F# r9 \4 [* ]" s    10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。5 G3 J, Y! F* s
    11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码
+ b$ c8 z7 j% b8 ^! dF1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。7 x, s( N8 Y- D% b: L+ i; Z1 n
    00将消息存储在Rx Buffer中。/ z& R+ _: `9 N1 y4 q2 @
    01:debug消息A。; w- r; e, x, O+ S3 W
    10:debug消息B。5 Z% ~7 ^, j3 b: A; ?" K$ M
    11:debug消息C.
# r3 ?; t! u4 `7 f4 YF1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。
1 k4 c( v. u4 {& @7 KF1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。
9 e& J6 k4 `: X' l) A" T, c- e6 j; z) J" C3 C
3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:
$ D  [) r( V: h+ X3 _& |8 I  U- n" ?; L& B. R4 E0 E3 V$ S
20191121172451477.png
2 m9 h9 c0 V" Y# |' c5 q* `2 `

: V) L) u1 D8 g$ K2 i( ?R0 ESI(bit31):错误状态标志8 Z* T% S3 c" h0 q
    0:传输节点显性错误。
! I6 l8 ~4 i- p5 z    1:传输节点隐形错误
0 c3 k* r; A1 ]" l% eR0 XTD(bit30):标记是标准ID还是扩展ID' X3 x' _, I. v9 |
    0:11位标准ID
% X4 y: h  \/ {/ o3 l
& A2 m- Q- q2 L    1:29位扩展ID. T: I" n  t* T6 H5 X8 [0 d

4 h  W4 b; [# N. pR0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
5 i- p% x3 d% Y& O8 m    0:接收到的帧是数据帧
$ x9 k: t, A" ~4 {: E* o) v' t1 ^. t3 e& D9 ~/ Y  j
    1:接收到的帧是遥控帧
0 ?$ @' S( ?/ CR0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
- x: P0 i% d2 Z% }5 [- w8 m, hR1 ANMF(bit31):- Y6 ?* j% d" E7 F
$ `. p- [6 q7 U  D1 ]
    0:接收到的帧和FIDX中的过滤帧匹配
$ Y# e; u5 K5 q/ N: o
/ ^! q, u6 q# z1 W9 H    1:接收到的帧和Rx滤波器中的任何元素都不匹配
. X' j7 Z* g% n/ U5 u0 {9 ZR1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。
& @' I( V1 N, V! NR1 FDF(bit21):帧格式% I: p1 x* b; @9 ?' W, J
    0:标准帧格式
- [4 a* c( }9 a4 I! ?$ f$ z$ V    1:FDCAN帧格式。
8 \& S# W2 Y1 h) QR1 BRS(bit20):比特率切换
7 M! x6 z" T0 J    0:接收到的帧没有比特率切换. f. w/ T( Q" k, v, Z4 X% }6 n7 L! c
) k% c3 W5 @" r  F/ T5 U5 d
    1:接收到的帧带有比特率切换
* S- ?3 }. `0 K0 J  xR1 DLC[3:0](bit):数据长度。
2 b# i) {4 B: S    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。2 l: m; Q; `' B( i
    9-15:如果是传统CAN,接收帧长度为8字节。
  i2 ?9 @6 l" N: E% ~9 g- p    9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。  @3 k8 V, P) b; V4 b
R1 RXTS[15:0](bit15:0):接收时间戳。& R/ |+ `" H7 e/ @
R2-Rn:接收到的数据。. [  b+ a5 @" b5 Z+ N

/ S) X! U8 Q7 r. ^' x( c4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。
0 y( w9 h6 R8 `. i: X/ S0 y# F/ g2 @" h& E
20191121174147402.png
9 l- p) W6 V. w/ c1 M* z

8 c# }$ s: g$ l' q% b8 g6 Y" D" hE0 ESI (bit31): 错误状态标志6 D& M  H( f4 \

% j! l( K4 T2 C5 ?  L) V9 c    0:传输节点显性错误( k; ]0 i* V/ `
    1:传输节点隐形错误。
) d* `) l" c9 j$ Y' ?4 h( zE0 XTD(bit30):标记是标准D还是扩展ID
2 C$ g; U8 a" P2 V! X3 _( e% r    0:11位标准ID.
1 x' r! @1 W4 Z" ^& J  p    1:29位扩展ID4 Y( r+ H; h! o; S/ s
! G- ^; L% D: Z
E0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。7 c+ t8 y6 f3 M! q* {4 Q
    0:发送的帧是数据帧
, a. |1 v$ ?3 C6 Y* d) C    1:发送到的帧是遥控帧% S+ S( ]8 U) r% U$ l4 N: Q
E0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。; Q, F$ G0 A. e3 w5 V( V
E1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。; `& r, l2 `0 P" Z2 `
E1 EFC(bit23:22):事件类型
  V' C2 t! s5 Z/ t- Y, A5 M    00:保留。
6 G' m. D3 l* o$ @4 h    01:发送事件。
3 A) I7 q3 t! A" e$ t9 S    10:发送取消。, O0 s2 `1 ^) u- g- p3 ~: n
    11 :保留。
* y0 A, ]* l/ A2 K6 LE1 EDL(bit21):扩展数据长度
$ }9 T  V. `: ]    0:标准帧格式
& ]* Z* X& `6 s& a    1:FDCAN帧格式。0 o  S, l, c. q' t/ s' [8 O; f7 Q
E1 BRS(bit20):比特率切换
, X2 b9 }1 t' A* L& c8 C$ [0 c    0:发送的帧没有比特率切换
! J1 h, _7 u$ D' N. g* _0 A4 S6 s# Y7 ^) ~2 g. ?
    1:发送的帧带有比特率切换
! f- l4 Y' D4 P: R+ w5 J$ r# X1 DE1 DLC[19:16](bit):数据长度。" y% X4 E: L/ C$ |' a" G6 X
    0-8:传统CAN+CANFD,长度为0~8字节。! q/ P% p9 k& m
    9-15:长度为8字节。& E% U" D4 N- m, M& s; S; y6 O
E1 TXTS[15:0]bit15:0):时间戳。
* |2 t2 O" L/ {9 B, o6 I4 Y5 I1 p/ C6 K" g' L
5)、TXBC.TBSA:发送buffer,有32个元素。
3 T5 k* F3 p) K: v: F* G: w2 j0 `
1 k9 Z( H4 |% p; X, W& d- q
20191121175121493.png

! e- ]5 {: Z4 r2 O6 o! j
+ s% s. |. ?" d1 H& W& z5 [T0 ESI(bit31):错误状态标志9 h' x. E) [, P2 h
    0:CANFD帧中取决于被动错误标志。
$ D1 l0 V3 s1 s# h  ?& Q* b* |    1:CANFD帧中隐形错误。
& r- c' ?( j4 [: f) `T0 XTD(bit30):标记是标准ID还是扩展ID* p, d9 D$ B, |4 [! S& T( L
    0:11位标准ID- |- g  j. b% R# T% t
* ?* M/ ?; \1 p/ h) |0 @
    1:29位扩展ID" R7 [2 C% Z$ ]( g6 U
. l( Z! `4 d) m# ]; Y/ `) Y0 q8 `3 M
T0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
  i. `/ c7 R. R3 m    0:发送的帧是数据帧: D& V& l3 s- R% [
    1:发送到的帧是遥控帧
$ Z* _; e- S8 ?8 _; OT0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
& Z$ f' y6 C) c8 }T1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。' {" G  r2 d; M
T1 EFC(bit23):事件FIFO控制3 ]+ j) A5 Q; v% i
    0:不存储发送事件。: _' T4 g3 d) @' D
    1:存储发送事件。7 ~9 J* [' b" N" p: r
T1 FDF(bit1):帧格式" S- H2 D% B" U5 b
    0:标准帧格式4 c5 r+ Q$ _/ x( c
    1:FDCAN帧格式。
: x+ E- h2 _3 L0 w$ @) v6 W% g; mT1 BRS(bit20):比特率切换' T6 I) ~- y9 N( D) X
    0:发送的帧没有比特率切换. z7 w  k$ G- V5 _. X! h: S  m
5 S# ]; p8 B% J
    1:发送的帧带有比特率切换& P! J8 q) @* O8 Z
T1 DLC[19:16](bit):数据长度。2 @! z& P$ p; n8 A: C% v
    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。
# [, i) t% G4 E# a    9-15:传统CAN,接收帧长度为8字节。
, ~. G! K  W: m4 d    9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。- F: T. d5 q1 u7 H# P# r
T2-Tn:发送的数据。( b) V1 r6 l3 J; H" T$ K/ Y2 A3 z

4 b) G/ N. Z. p: l3 s三、过滤器设置  O" H: y& H, S& H! h0 M
! {* b3 l7 I. O
        标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:
1 Z: u) ^8 H7 _8 B0 u. o3 ^1 U9 o
        1、指定范围过滤& d7 x6 \  f/ M  a
        通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:5 B6 W7 A" O9 ]2 t+ O, ?

. l/ t6 b- C5 q1 ]' {
  1. SIDFC.FLSSAn.SFT=0        //范围滤波
    7 |1 |0 ?+ v# T; K4 Y' R
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到RxFIFO中( g- T7 s& c1 ^& g1 N! E! [
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    0 s& e& G* S6 z7 J6 h9 {" E! R+ _; W% O; H
  4. SDIFC.FLSSAn.SFID2=0X321  //ID2
复制代码
- e5 f! c& J- n: ^
        2、指定ID过滤, G- T5 A7 C5 u! }' p
0 z5 q3 u5 D" Z1 P' w
        我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:; {- H. F6 R9 e) k
' g' [: j/ U% ~3 q( s
  1. SIDFC.FLSSAn.SFT=1        //指定D过滤% o$ o1 r, V$ y4 k2 I
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到Rx FIFO中
    0 Z7 t/ `5 j: t! y/ c* l) o. R
  3. SDIFC.FLSSAn.SFID1=0x123  //ID11 a0 m# T% |, p% o
  4. SDIFC.FLSSAn.SFID2=0X123  //ID2
复制代码

1 X: g( [* J. \2 W' D        3、传统的位过滤4 J' x$ }; z$ H
        第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:
5 \, n& G7 P% o+ ~4 W. `) ~" M9 r. |" C
  1. SIDFC.FLSSAn.SFT=2          //传统位过滤  W" o$ ]) y& u+ v
  2. SDIFC.FLSSAn.SFEC=1         //如果滤波匹配成功的话将消息保存到Rx FIFO中8 E; I% I- q( n- j4 ^, @+ m
  3. SDIFC.FL SSAn.SFID1=0xFF00  //ID1
    , b! u( b- ]5 V3 r0 |& o  E
  4. SDIFC.FLSSAn.SFID2=0xF00    //掩码
复制代码
) J7 P6 n0 B! G/ h) _
        其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。6 Y: B- C, C! O; a. e

6 \- {2 _5 d4 H( `: Y, J; k$ t四、CAN的收发过程
! y; e3 o4 Z9 u" l, |* c& i
7 W/ B" T/ |2 E4 w) q7 y        1、CAN接收过程
) F3 ~& k% L6 P/ o- ]; w
+ @& d+ H. q8 ^        CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。
* R" k- H/ h$ j6 a! b6 o( z* p1 G# ~3 A' U; E* R9 F
        接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。
1 i0 {# Y* Q( P
, _+ {1 ?2 |( m. L+ Q
20191127114550965.png
0 ?2 p0 c( N- ^; p

5 B6 i  k) x5 C2 d        当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。
% r* M0 x3 C. |: T# }" b5 Z0 q, S/ Y" c

) i) Y6 [+ T6 A) Z
20191127111235302.png
7 ?$ U+ M( a! e! `) z3 v, V+ ?

$ L- V! k; Y, x9 u; s1 ?& k' a        为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。4 y7 C2 ^: @3 s4 h
        读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):
; P! b' n) ]# V" F% [: r& K( K5 d. ~. S8 d7 u. a7 o, f4 @
  1. /* Calculate Rx FIFO 0 element address */
    5 T1 a; q' {3 H
  2. GetIndex = ((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8);2 M) W5 b0 k4 r. I, }  M& w& F. q
  3. RxAddress = (uint32_t *)(hfdcan->msgRam.RxFIFO0SA + (GetIndex * hfdcan->Init.RxFifo0ElmtSize * 4));
复制代码
2 S! l: ^0 o1 m! P- k) o; u
1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。
' L+ |4 Y2 h7 Q9 e* T8 r2 R9 B* n0 F1 [0 l3 l* C6 v$ s
2019112119095892.png
( A" H; f3 C: t& {' e6 A0 k
( p$ o& A- H" ^7 H) z: {
2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。/ B0 h  t8 u( n
' r! n5 S2 ]0 b- x; m
3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同
" z# C7 I0 v7 o1 |7 E  p7 I& G) J' I1 t* B& ?( z" V; p& ^  b$ q
# g, t+ t" n5 ?$ x* T
+ o  E% }, a6 l; P& @2 K
如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。
/ ^# H7 a# T( I- Q% ~3 f  V3 e& K$ M1 p
如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。
1 R# n; [3 M! ]" k8 Y0 N! u7 s3 f% M/ T, `( u: H# M$ K
        计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:# W$ ~8 K0 M8 w3 P
  q" R* W2 K8 V* N$ d5 k
  1. pRxHeader->IdType = *RxAddress & FDCAN_ELEMENT_MASK_XTD;( k7 B; j% Z; r& |2 b9 D
  2. pRxHeader->Identifier = ((*RxAddress & FDCAN_ELEMENT_MASK_STDID) >> 18);
    # t! I+ m* O" C8 o6 s
  3. pRxHeader->RxFrameType = (*RxAddress & FDCAN_ELEMENT_MASK_RTR);( c9 X/ `) |: Z; g& `
  4. pRxHeader->ErrorStateIndicator = (*RxAddress++ & FDCAN_ELEMENT_MASK_ESI);  //这里地址自加了一次! m/ h& J; N! x* L* E) T  @
  5. pRxHeader->RxTimestamp = (*RxAddress & FDCAN_ELEMENT_MASK_TS);
    0 h! L; v9 W0 T" a8 v
  6. pRxHeader->DataLength = (*RxAddress & FDCAN_ELEMENT_MASK_DLC);
    : r) K: n" {* C( @& }- I7 e
  7. pRxHeader->IsFilterMatchingFrame = ((*RxAddress++ & FDCAN_ELEMENT_MASK_ANMF) >> 31); //这里地址又自加了一次
    / }, [, [( |! c& Z
  8. ......
    * j6 k: V4 h$ V" N6 M
  9. pData = (uint8_t *)RxAddress;
    4 `3 b) ~( X! Q2 H
  10. for(ByteCounter = 0; ByteCounter < DLCtoBytes[pRxHeader->DataLength >> 16]; ByteCounter++)% ]/ p9 t, g7 ?% Q/ R/ d; _
  11. {
    7 U6 ^1 e& ~, W- e1 _. \
  12.     *pRxData++ = *pData++;- l' O/ \. V' {& N! O( u
  13. }
复制代码

7 e4 ~  R) [7 p; D$ ?6 M5 |* z, i        2、CAN发送流程
7 W7 y  t0 t6 f; N% ?
% q/ i6 n# w, D        在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:: D' q! o- Q! c+ G( N

3 E% u9 l$ _5 Z. W3 Y5 C2 q6 N& J) p
20191127150042199.png
7 h" \6 M, R5 ?- X

3 H  H0 f1 e# d        因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。
+ V8 g& _7 n* U/ H% \5 n/ ^  J" f- `
        下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:
( e; B+ M$ l5 e( C/ K
& V% _9 \+ m+ z; F& }: P1 ^( u3 l        1)、专用发送buffer
! o: Z5 F' E: J4 [& g8 t  A# O( E, l  Z) {" p. P+ S: b
         专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。6 ^3 }$ Q" F2 x4 K" b& B
        如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。
7 X% Q$ @# C, h$ [
% ?. I, e) N7 ^7 ?5 T
20191122101555794.png

3 n- T# A# L1 L3 o+ k
1 X+ B$ N$ l& w. l+ [, U5 q7 g0 H        专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:, w6 r! ?! V2 A0 y
/ g2 x; \2 U# [: ^
发送buffer索引 (0…31) *4+发送buffer起始地址。) A, C3 ?: h6 t+ {1 I4 Z
  g6 _* t7 P* `. U8 M! O+ e  Z

& m( u) O- o# I% @, h9 g( B% X' s( a
        2)、发送FIFO4 `7 S( w0 ]/ h% `  H
# d8 K; R0 x3 f/ v1 Q8 u5 @
        发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。
, _1 K  L8 G  @/ [+ |1 c6 g        新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。
; \2 i5 i! }6 R3 e- o# D( k+ V$ k% Z! c, u$ q9 n
20191127160804244.png

) I& I$ Z0 ]* O, c% e; k4 j8 r: Q1 M; d
        发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。
; t. C& h, a. D1 A) z$ ]# D$ F, O. d0 B0 x
        如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。2 l4 |# R- C6 h$ y, X$ k, B
        如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。! q# i1 B6 T4 n5 r
        如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。
! q& a. m# J! A) E: U- E        发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:
6 H8 ?0 _! W7 y9 _7 C7 w% x4 I9 |1 S; L  b! v" Z+ e5 O
放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。/ e& F4 D' ]' P4 H
. t+ C: C1 ?# Y8 \
        3)、发送队列
1 U( \/ ]( }, I% j/ Z9 V        发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。8 W+ d3 [6 F& }/ C: c  ]$ @
        新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。
( a* z/ C) V5 ~2 A- F2 G! o. ^        应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。
. R/ l+ n! F. k) T' d5 p, x        发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是:. E/ Y0 k; v9 T+ y$ d# j; l+ y/ T
  O* p+ e. Q/ d
发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。
; ~1 h, J7 j: X% \; l6 |, N  T. j+ T" X7 Z; `/ x% Y
        因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:% q5 Q: |2 g8 c4 ]

& D# A, O- U* p) A3 K3 b        1)、发送buffer全部配置为专用发送buffer$ e4 O" q: _' d* C
0 X' ~4 ]+ o/ s+ U* l& s% u
        每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。
' ^9 t; M! H: e" ?
) T. Y6 o+ f! o* Z# r: p8 p        2)、混合专用发送buffer和发送FIFO
1 i- O$ O, H- Q/ g9 M. Y* w# I0 A: y  k; j
        在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。
2 y1 K1 n8 o0 A& p+ l# L. \
. X2 J- j2 V! b8 `1 K7 U" E
20191127162943182.png
5 c4 i6 Y3 C0 w

2 Q7 `9 Z. f% d) x$ Q. z        发送优先次序:( ?' v) d) C) N& `1 c  ?2 X
A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)# W3 c, H0 M3 ^" ?2 N& V& [% P
B、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据& Z: |( m# R" z6 }* v9 w* H$ P

& e( y4 ~" Q& R; P& J0 u        3)、混合专用发送buffer和发送队列+ R; _: M5 n7 A7 k) E

2 y% S* }& @7 D3 {& Y4 [- ^+ a        在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。8 L" f" M4 X" ^  U
2 D7 W. l, k8 e: F
20191127163202212.png
6 o# b7 ~9 ?, E! J" u$ l* j

3 g* {8 m  W) b2 P* x, }& @        发送优先级设置:
$ Q) J# }' u0 m! r. v) oA、扫描所有激活了发送请求的发送缓冲区
+ E" K* V. C& F! `& {B、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据
$ [, e+ x& [; U$ q2 e5 ^8 U/ Z9 v& _2 A7 m( C7 ~! R: i, Z- Q% Z
        再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。
0 B! W( B, G! x4 _7 p- B
) P" M( \6 D% M# f. H0 c: g        FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。
/ s) \  H# _4 p: ]" ?8 s" E% B        发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。
& [; a0 L: p3 f/ C        发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。' U/ V' w8 d" P" J$ V
2 P, k! @1 \4 K9 ~7 C0 ]! Q  K
        为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。8 C/ B, V7 P0 ^3 k4 l

9 V( y# E" a2 m' D        CAN发送消息的代码实现:
# u: f+ U+ h+ K4 N& f. j9 c9 A3 I7 j$ N# ^
        发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:
% v5 L* V8 M, q+ F6 ]7 }% R7 G9 `; @7 r6 I/ g* l/ o
  1. PutIndex = ((hfdcan->Instance->TXFQS & FDCAN_TXFQS_TFQPI) >> 16); //获取元素编号 2 ]3 D  ^- I2 G* O2 ]' X$ V
  2. FDCAN_CopyMessageToRAM(hfdcan, pTxHeader, pTxData, PutIndex); //复制消息到Tx buffer 4 w3 q# I, R, d8 A7 @) M
  3. hfdcan->Instance->TXBAR = (1 << PutIndex); //发出传输请求
复制代码
- n# }) o) A* W! a5 j0 o8 t. _
        发送buffer请求寄存器FDCAN_TXBAR,描述为:6 ?- `+ W2 ~7 S
& G# s  V( m1 z' C* J
% F  ]& a% W5 D6 _9 t
3 g+ n8 M9 Y* ^9 E4 G/ S' e
        此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。4 \' X5 q2 `; t1 h; r+ X
3 p& x1 {& R) i2 [
( A. x$ O( o% i+ H

* Q( z6 y: l0 r; ^, x6 ?五、初始化$ y/ D; o' i* Q* n: ^

1 h1 w" @2 z9 ~) `3 _. {6 {: P        初始化可以直接使用HAL库,/ a+ L. x% \" u9 I5 c, v) B) \' a: |9 ~

7 I" W& I( w/ c: q/ h
  1. u8 FDCAN1_Mode_Init(void)- a: T+ N+ Y, ]; u8 U0 y$ o$ q/ {
  2. {
    8 t; z' z) g* U% @
  3.     FDCAN_FilterTypeDef FDCAN1_RXFilter;- s" W7 A' y5 v3 M

  4. 0 c: Y) p9 Q- {
  5.     HAL_FDCAN_DeInit(&FDCAN1_Handler);                              //先清除以前的设置
    % R2 O% ~' ?% B; N* C# T4 B% |
  6.     FDCAN1_Handler.Instance=FDCAN1;
    8 D9 O* Z. X/ `7 T+ |" g! Z1 _( p6 I
  7.     FDCAN1_Handler.Init.FrameFormat=FDCAN_FRAME_CLASSIC;            //传统模式
    ; }' {3 a; K8 B' j/ S9 N0 H% W9 |
  8.     FDCAN1_Handler.Init.Mode=FDCAN_MODE_NORMAL;                     //正常模式
    7 E2 Z" `- B4 O( `' [
  9.     FDCAN1_Handler.Init.AutoRetransmission=DISABLE;                 //关闭自动重传
    9 {* g. h4 N6 w! S& z, [2 F! q
  10.     FDCAN1_Handler.Init.TransmitPause=DISABLE;                      //关闭传输暂停           + K  Z* j" D2 P' Y
  11.     FDCAN1_Handler.Init.ProtocolException=DISABLE;                  //关闭协议异常处理
    & A" ~+ S& m5 p
  12.         
    ) G8 D4 _; k& K7 N$ _
  13.         //时钟为200M,baudrate=200M/(NominalTimeSeg1+NominalTimeSeg2+1)/NominalPrescaler,这里配置为1M
      ~5 n  P7 u0 `- |" e
  14.     FDCAN1_Handler.Init.NominalPrescaler=10;                        //分频系数
    * E+ Z6 ^% c# |: `
  15.     FDCAN1_Handler.Init.NominalSyncJumpWidth=8;                     //重新同步跳跃宽度( f# v; C" j$ h( M! p7 K3 n
  16.     FDCAN1_Handler.Init.NominalTimeSeg1=11;                         //tsg1范围:2~256+ d) |. w5 I+ b$ a8 H2 f0 y
  17.     FDCAN1_Handler.Init.NominalTimeSeg2=8;                          //tsg2范围:2~128( s  ]% p6 z9 s* ?/ k
  18.         
    + v$ ?! \  K3 s
  19.     FDCAN1_Handler.Init.MessageRAMOffset=0;                         //信息RAM偏移,10KB消息RAM共有2560字,故可以偏移0~25607 m4 w+ _4 X8 V  K' Z- k3 \
  20.         //使用了多少个滤波器就要设置为多少( G0 v) j1 ~& Z7 Y
  21.     FDCAN1_Handler.Init.StdFiltersNbr=3;                            //标准帧滤波器个数,0~128
    . t, W, i3 N5 {+ X6 U% ]0 L7 u" g
  22.     FDCAN1_Handler.Init.ExtFiltersNbr=2;                            //扩展帧滤波器个数,0~64
    0 ?$ J7 E2 P- p5 [( l  k
  23.         4 R3 x* X/ z8 K6 I
  24.         //接收FIFO0、FIFO1和buffer配置,此处没有使用FIFO1故个数设置为0% U$ o! Q6 p. [6 I4 t( |( U# _$ l
  25.     FDCAN1_Handler.Init.RxFifo0ElmtsNbr=64;                         //设置接收FIFO0元素个数,0-640 H3 |5 ^3 t5 |9 F6 B2 q
  26.     FDCAN1_Handler.Init.RxFifo0ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO0元素的数据域大小:8字节        
    ) n  A! S: Y+ r; {6 }
  27.     FDCAN1_Handler.Init.RxFifo1ElmtsNbr=0;                          //设置接收FIFO1元素个数,0-64! B( G3 B7 K8 J, j; C( @3 n
  28.     FDCAN1_Handler.Init.RxFifo1ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO1元素的数据域大小:8字节               
    3 d3 G8 W7 w/ r( B) c; C4 u! o
  29.     FDCAN1_Handler.Init.RxBuffersNbr=64;                            //接收buffer元素个数,0~64
    5 S" \, h' J# D. |0 _# F% F  Q
  30.         FDCAN1_Handler.Init.RxBufferSize=FDCAN_DATA_BYTES_8;            //接收buffer元素的数据域大小:8字节        6 e4 C6 b0 Q5 i' J% w% b# u/ S5 O+ y2 z
  31.         
    - Z; X( u2 E6 x  v( B& \. j9 ^
  32.         //没有使用发送事件FIFO功能,故TxEventsNbr设置为0。把发送buffer全部作为专用发送buffer使用,故TxFifoQueueElmtsNbr设为0., m4 [2 \5 R9 V. x
  33.     FDCAN1_Handler.Init.TxEventsNbr=0;                              //发送事件FIFO元素个数,0~32. N7 A2 q% G6 H
  34.     FDCAN1_Handler.Init.TxBuffersNbr=32;                            //发送buffer元素个数,0~32
    ! Z+ Z5 g, ?5 ]4 {  P
  35.     FDCAN1_Handler.Init.TxFifoQueueElmtsNbr=0;                      //发送Buffer被用作发送FIFO/队列的元素个数,0~32
    ! _7 Y2 _8 g4 Q7 t% |
  36.     FDCAN1_Handler.Init.TxFifoQueueMode=FDCAN_TX_FIFO_OPERATION;    //发送FIFO模式选择,可以选择FIFO模式或队列模式( r: k9 \, J- n! U+ J" {4 P( L
  37.     FDCAN1_Handler.Init.TxElmtSize=FDCAN_DATA_BYTES_8;              //发送元素的数据域大小:8字节# k$ E! P" w, T
  38.         
    7 d3 K2 x' p; Y$ c# ]
  39.     if(HAL_FDCAN_Init(&FDCAN1_Handler)!=HAL_OK) return 1;          //初始化FDCAN* \/ L/ v* u2 h; T) u5 s& n4 Q% T

  40. ' T+ j. Q% H- l1 o, j+ N7 S
  41.     //配置RX滤波器,标准帧   
    2 I5 n0 K% e) h. k# H
  42.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID  F' P3 Y/ G' T. N% N9 d! I: f6 x
  43.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
    # ?) p( b; Y" R5 y
  44.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型& i* U! r- x  |8 |0 `- X0 V: v
  45.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  - v2 H: ^* N4 o. H" {# {4 b5 m
  46.     FDCAN1_RXFilter.FilterID1=0x112;                                //11位ID! o; }9 [' z3 C: D
  47.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    2 B7 v+ Z+ @) U0 c; x
  48.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化1 e  Q+ q4 C4 l% G+ S
  49. : G# }; g( u% H0 t0 ~! J- N
  50.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID& [$ ?- [  u0 C
  51.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                  
    0 i) K. L# `2 @2 S# Q, n( k$ [
  52.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    , t2 a: N. U+ c* \) e" m
  53.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    0 O: @( Z1 ]- f' \( E" @' k
  54.     FDCAN1_RXFilter.FilterID1=0x113;                                //11位ID" V3 P+ J2 Q+ V
  55.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码9 O  ]6 q! I( s8 u& Y2 J, Q4 i. V
  56.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化# X4 _: h6 g3 u+ }
  57.         + y$ b/ e( ?: J- N7 k
  58.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    2 z; `/ u/ n7 c' s+ Y
  59.     FDCAN1_RXFilter.FilterIndex=2;                                  //滤波器索引                   ( P) z- p. |% C: u! e
  60.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    ) u" u0 f* ]; @: c8 ]
  61.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    ; V" Z3 T0 l  g& D7 `
  62.     FDCAN1_RXFilter.FilterID1=0x114;                                //11位ID
    5 R: f4 s' Q; S2 R7 M
  63.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    ! H' s- Y  E  j2 X
  64.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化! m$ P8 l# N3 M+ D
  65.         
    - Z7 [* p/ |. U: w1 r  w$ Q4 r
  66.         //配置RX滤波器,扩展帧。标准帧和扩展帧的滤波器索引是分开的   * W4 S, |. k  S2 }. f: A
  67.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID" u& m- R' Y  K
  68.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                   ' n' q8 _. R) `2 x1 e3 b0 P! f. M; }
  69.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    ( D4 E( q' F3 y$ I
  70.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    $ Z& ~6 G" a- X0 V
  71.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(2 << 12);                  //32位ID
    7 U/ i2 ^: q0 g
  72.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码% s0 P$ t: i1 ^2 m' H
  73.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    ; K; G; a/ q! w) N
  74. . G' b3 g& N; N5 n
  75.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID
    * C* u' y7 d9 v- R7 ~2 O
  76.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                  
    1 P" p% T2 e1 a* V3 B5 o
  77.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型  H0 _6 b; G: o5 \. R9 d3 Y
  78.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  9 Y4 d9 v$ f: ?) Y  k0 `$ j
  79.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(3 << 12);                  //32位ID5 b% u. z, n. o  L/ @
  80.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码; g: b, y, _  c$ Q. O0 M6 a+ S) G
  81.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;    //滤波器初始化
    1 }3 p8 g: `: C7 q- R
  82.         # G2 u0 {- D% h, V1 ]1 {& j
  83.         //滤除的消息直接丢弃
      M! L: Z# x! o( t, q1 n
  84.         HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler,FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);  //设置被滤除掉的消息的处理方式
    3 i$ a$ z& f9 `
  85.         
    , v  x$ G) ^% i; `: o
  86.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);   //使能新消息接收中断
    / `3 r* D* o, g* x, k- |$ I" }
  87.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_TX_COMPLETE,0xffffffff);   //使能消息发送中断,0xffffffff表示所有的发送buffer都触发中断          _% l! a  `# O( U; a$ ~3 i
  88.         - f" G9 y$ {! Q1 D% L4 K4 p- L
  89.     HAL_FDCAN_Start(&FDCAN1_Handler);                               //开启FDCAN3 E3 C$ n& s1 [* F7 [; f+ d' A; l
  90.     return 0;1 [9 y: ^2 r4 ]$ y3 M( D
  91. }
复制代码
* A' d; @; z6 ^1 |* i
+ k! d5 O; x$ K9 _( o  a
六、CAN发送示例(来自正点原子)9 a& C! K' F' Y/ u. ?

, m' J; l3 @, I/ ?
  1. //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)        ; O" E( j+ x* j' U' ^* b- v
  2. //len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8                                    
    1 m( b1 r/ |& I0 x* S. I2 |: a6 N
  3. //msg:数据指针,最大为8个字节.
    & P* X' |& Q& {# Y& n4 D
  4. //返回值:0,成功;$ u5 |5 k/ w6 P4 o1 t
  5. //                 其他,失败;' A# [: `& w! Z8 {% `" M+ M0 n0 z
  6. u8 FDCAN1_Send_Msg(u8* msg,u32 len)
    ; Z" j! N5 r4 l! `
  7. {        
    1 S. O. H( Z1 W9 b. `
  8.     FDCAN1_TxHeader.Identifier=0x12;                           //32位ID
    ! q  [( G; M; }
  9.     FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID;                  //标准ID
    % o- K: z8 U- k
  10.     FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME;              //数据帧
    & z/ U9 j% c6 u; k2 e' J
  11.     FDCAN1_TxHeader.DataLength=len;                            //数据长度
    5 ~  x/ P: Y9 K0 h& L2 h
  12.     FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;            ! t( d' ]+ X4 q& P- k* ?
  13.     FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF;               //关闭速率切换' K0 @$ C( p( g! ?8 C3 ~7 D
  14.     FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN;                //传统的CAN模式1 L( U* U  Y$ l  P0 X# ~* S2 k
  15.     FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS;     //无发送事件# y: h! o! G/ g  K5 {5 D
  16.     FDCAN1_TxHeader.MessageMarker=0;                             y7 H; I% v4 u: Y* G; X
  17. 9 \0 K1 C% H  I3 H9 F" m6 M
  18.     if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送1 K' x8 I9 J, e
  19.     return 0;        
    - F2 b" {2 S# f, j; v4 r
  20. }
复制代码
1 M# ], O' F3 L% I
七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)
+ W% S1 Z2 z- q
; l* r1 I: Q! h" C5 v# u
  1. //can口接收数据查询- \+ Z& H! d" e0 Q( _- M' t
  2. //buf:数据缓存区;         ) l; h# ~% y9 {  I0 m, d  N
  3. //返回值:0,无数据被收到;6 y. g/ p/ L7 |6 c# \& w
  4. //                 其他,接收的数据长度;
    : a6 f. b* j# ^( H& p) a' o
  5. u8 FDCAN1_Receive_Msg(u8 *buf); Q, L+ P, Z0 ~0 P' z9 N
  6. {        
    0 G. z; K' o! r, v6 U; S
  7.     if(HAL_FDCAN_GetRxMessage(&FDCAN1_Handler,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf)!=HAL_OK)return 0;//接收数据& ?# }5 l' m8 j8 Z
  8.         return FDCAN1_RxHeader.DataLength>>16;        
    / U$ `( C6 B4 i3 L
  9. }
复制代码

7 _* E* d9 z0 S! z9 L参考:《正点原子》  STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准)
1 Q$ A% S  `2 |5 {2 d2 `) H( d6 f) I" s2 W( V
- t+ o- z4 l4 w8 R0 |- e% c" h
收藏 评论0 发布时间:2021-12-22 14:00

举报

0个回答

所属标签

相似分享

官网相关资源

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