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

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

【经验分享】STM32H7的FDCAN

[复制链接]
STMCU小助手 发布时间:2021-12-22 14:00
一、介绍1 r1 U/ G0 t- ^# E& {
7 f7 s/ k: V9 ~) \* T- {; m
        FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:
+ ?! t2 I( A4 h; e& O% ]- S% t. h
- G: n: U5 L# s9 i2 U* m/ P$ F       1、每帧数据段最大长度由8字节上升到64字节。
; l% }3 c. G& F
9 S  |, N- y6 W1 k        2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。* E3 B1 c* u1 e- k. H1 ?, ?
8 V: r  U6 x2 }6 d; }
        3、向下兼容CAN。
7 @# W. e  h% P/ ]/ T5 v3 u. V; }. Q7 S6 j* d/ }3 U  n
        H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。' `( H+ c( e/ g! V; ^4 }$ i* ^
" m! G+ F2 C9 x% F
二、结构% z/ S" |2 ?5 j  V) Z. V

  Q1 Z* _! x% h9 J" [* q3 Q        先看结构框图:  j3 I7 K, X6 M4 F, H6 [' `5 `
2 F- l. h! |' s2 N2 _
20191121162856859.png

. M+ H3 n' x! |
2 m7 i6 M+ l5 k1 S1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。
% ]/ q, P" P0 ~% x0 D  L% T$ L6 P
9 \0 O$ w& k4 h  R% Y; e4 R. a
20191127102816991.png
, C, f6 p6 m- |/ H( h* d( O
! B3 {' B* C0 e8 ?$ I
        可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。
$ ~' p$ k3 j7 v) s" v3 d  Y
( D) ~% e: n5 [% w+ Q. E# R2 t2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。
2 _, J% _( m* |. a3 Y% K! N
+ W9 a# t0 t2 ]0 W3 e7 D: t3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。; R, d6 k3 E3 ^

! l) P0 [, u/ ~" D9 g: N; @4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。
0 }  M) }7 `( ]( _! U  b3 g. D
+ h5 @+ Q$ }, \  ?  O( q, H8 |5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:
. a- n: R+ Q- t
3 x; y8 ?* u: [9 V
20191121164211703.png

- y0 C9 Z) E+ e  g* h; I* h6 j% j7 K: W0 G8 k# U. 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字。
: ^( Y1 W  b" }# E
  h& P  @9 o) T" s2 S6 L        1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。
! Z2 o/ j8 d6 j2 K/ I
4 _9 a: i& C  n        2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)% V  C+ [9 j, l1 [
; z# @( L) z& I% n
        3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。& K! l4 h9 ]8 E" G  s  d* _

& z1 {% w! U2 l% j4 L+ n        4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。" I, S4 O  T1 W0 H

5 T/ k( c' G' R  [& M6 Y. R+ H4 S        5)、RXBC.RBSA:接收buffer,最多可以有64个元素。
  N4 w  I+ d, O
# N8 i4 @( K! z3 L        6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)
. E  V" u2 J( s( O- G4 Y
% U1 n% L, u# `& m        7)、TXBC.TBSA:发送buffer,最多可以有32个元素。
" P* `+ E; _6 Y( u, w4 \$ j2 R/ U2 g6 Q' D  t$ Z
        8)、TMC.TMSA:触发存储器,最多可以有64个元素。
. B4 d* r3 o# `7 |" {" V7 R
. }' f& d) e' Z6 b《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》3 r2 s! z1 E' r- F$ \
6 {  p( \$ [2 g6 p1 L8 C: ]
        下面来详细介绍这几个功能区域:
8 y( A9 k$ w9 Y# z# M1 T: M7 N
& f7 B, V5 q3 N5 T3 Z$ E, ?2 o; Y1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:
9 _9 ]% u% U+ v+ P) g
9 j, x% C8 b9 ]) R6 H
20191121170625565.png

. y! S0 N" t0 D7 U' ^8 l1 M1 g: O' `0 {; H; l
SFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:
; D* c/ `# N. Z    00:范围过滤,过滤到SFID1到SFID2中的所有信息。" X1 P, z0 }" n/ l/ q
    01:双过滤,过滤到SFID1和SFID2中的信息。/ h/ m5 g! ?2 P: R4 c8 u
    10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。
- j" I2 X( g" a5 F    11:禁止过滤器元素3 @" Q- `/ j, B, T9 @  d
SFEC[2:0](bit29:27):标准过滤配置/ F6 Y5 F; H- K/ B
    000:禁止过滤器元素
+ o! L  s  \, @; k    001:当过滤匹配以后存储在Rx FIFO0中。5 B: E( n$ |2 A6 ]3 r3 r3 R
    010:当过滤匹配以后存储在Rx FIFO1中。
2 t) }" z$ [8 u5 h  {    011:当过滤匹配以后丢弃。
+ s+ F( R7 z) c) B1 z    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。/ k) r, W3 C  s( o
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。& D+ p7 v" I! q3 t0 L+ b
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
2 ^6 @( H" V9 H- c9 W" ]    111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。' k1 W; }5 M# y6 d3 d
SFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。# |: v7 V* n: g4 Y3 U  @' X
SFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。  B# y5 j( A: Q) L; l# s8 k( o- Z
/ u; p8 q6 Q/ N% C. Y: |, m
SFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。, @) a3 K2 `, h7 R/ d: l9 y, U4 Y
    00:将消息存储在Rx Buffer中。
% G% Q1 x. ^5 a' R# Q; w& E- p. \3 t3 j0 M, w
    01:debug消息A。   
* O/ D9 M1 i8 P- w% C
/ s5 q8 U; Q. d    10:debug消息B。7 a) I/ g) E6 w) O  ]/ y
4 B6 E1 J! C1 F) l/ V; R
    11:debug消息C。
) X  i: o! A! f" j! |( F% e! @6 D: _
SFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。' P$ r& u! m, L0 }
  Q7 U4 U' H8 {, O) `0 p
SFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。' E4 R9 V+ O0 ?) ^" P" O/ O
/ ]6 Y" S7 N. D# I. }' D- E
2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:
1 s* P' Q3 i7 g4 i# S& L$ K  @& N+ R1 l* J5 s" F2 p5 p
20191121171836710.png
  v+ e" }$ q. o& Y" j
4 k  |% C. `) l
F0 EFEC[2:0](bit31:29):扩展过滤配置
0 w! G% S: U! y' q8 d8 C    000:禁止过滤器元素7 M/ }2 J) p- x4 R2 N1 D7 A
    001:当过滤匹配以后存储在Rx FIFO0中。& N# z5 O: n) [& ]0 F
    010:当过滤匹配以后存储在Rx FIFO1中  h0 k  K* B, @) t8 x
    011:当过滤匹配以后丢弃。/ d( {& k% E6 ?: M) ?+ [
    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。7 w2 o- p- E$ {8 Q- G
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。1 M6 Q7 [  l, ^8 S- r" D
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。. R; _# Q5 R( L' H( Z9 U* b
    111:存储到Rx buffer中,EFT[1:0]忽略。9 c& [: s( \1 W5 w' o& V
F0 EFID1[28:0](bit28:0):扩展过滤ID1。
- g5 T* F# I$ ^  n' B. DF1 EFTI[1:0](bit31:30):扩展过滤类型7 x, e5 y8 \* g: i/ R' H3 ~! ]
    00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。
3 ?/ H% P+ e! ]9 ?3 X    01:双过滤,过滤到EF1ID和EF2ID中的信息。
3 V0 l' D) J4 K% _9 y3 N# ^7 j    10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。
1 o" C3 G) f" @3 O2 m1 }& z6 ]' f3 y" f    11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码& H" k; M* k6 ^1 [: H8 [
F1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。# H9 U; t0 L; K% y" z
    00将消息存储在Rx Buffer中。
- _& g/ v* |, T  r2 Q1 X4 o5 w    01:debug消息A。2 c% ^# Z" e5 U# t3 y* z& E
    10:debug消息B。
& z" l- t' f! |6 S; k& E* c9 [    11:debug消息C.( e# o7 @: K. a* m& ?: e
F1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。
- B4 T+ A) }8 V0 b) E3 HF1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。
' C5 M; q0 T  o- {7 M6 f' \" }+ |# x% Q: x8 U
3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:- d- I( q+ a- F5 |
9 g, O4 B7 c. Z; `8 G9 a( |
20191121172451477.png

8 T, g; ]* j2 w  ^( R; s2 R/ k
- O6 N# q9 P+ g' U: F8 u0 IR0 ESI(bit31):错误状态标志
; u3 H+ Y4 t( t: F# l  Z    0:传输节点显性错误。$ p5 s$ B) J1 O% O3 U
    1:传输节点隐形错误( P& Q; u7 l5 l; C
R0 XTD(bit30):标记是标准ID还是扩展ID
! K# {. P1 m3 `    0:11位标准ID& M) c: }2 _, r- _8 P) M! |

8 G' P" y3 M( t; g2 K- ~( m9 P6 v    1:29位扩展ID
* |0 G1 f7 x7 {; O5 g/ {3 x& @0 S& h1 t  q8 f- @1 N; s
R0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。: Y* s) F/ V9 F& L
    0:接收到的帧是数据帧
( v9 \8 x" C# W2 U# g/ D$ I3 N" b5 t- U& _3 J0 c
    1:接收到的帧是遥控帧
1 r, w1 g- V3 @( C$ cR0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
& M7 x( B; A5 L# c! ~R1 ANMF(bit31):) X) v9 f  {3 ~7 S& \& ?
$ A, m, Y+ b& R6 [) `
    0:接收到的帧和FIDX中的过滤帧匹配$ I* R* K2 m6 o

% Y# N% K  |* _; h: Z' P$ A    1:接收到的帧和Rx滤波器中的任何元素都不匹配
% Y0 G) ]- {2 j. O- wR1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。
: Q  u. k. A+ _+ t$ [R1 FDF(bit21):帧格式
" I1 P' L. _4 ]3 K    0:标准帧格式7 d5 s' I! K" k5 d! z- H
    1:FDCAN帧格式。
0 V% Y  e$ w9 h$ T& ]0 |* jR1 BRS(bit20):比特率切换: C6 N6 }# b& ~) A9 D" v. z
    0:接收到的帧没有比特率切换% T" ?2 h" p4 V3 l# ^

" a  Z- i5 f/ x% R# z& b! Z9 Q    1:接收到的帧带有比特率切换
& _. p% e4 Z* S6 t3 E! yR1 DLC[3:0](bit):数据长度。- v5 @* v; @, {* T" b
    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。
  |# |+ @9 n& u* O& H" m    9-15:如果是传统CAN,接收帧长度为8字节。
, h- W  K5 V+ L/ D, k$ N$ t& U    9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。4 g& W; x" u1 N" z0 s! C! N
R1 RXTS[15:0](bit15:0):接收时间戳。0 B& D( I0 N  U% D
R2-Rn:接收到的数据。
# p" B/ M, q+ K) @7 g
$ S/ a, q8 R4 N4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。
3 a: Q, ?1 u9 T7 J
* M8 @& ^1 D# T
20191121174147402.png

% h2 I4 |, H. w  k1 h1 {2 q5 {
4 b8 b7 M' y$ Z# l1 ~E0 ESI (bit31): 错误状态标志9 v2 u3 j+ D( P% h" P4 h+ \: N

9 Q" n7 I& d; O: R: _" q0 V  I    0:传输节点显性错误) e% x! j. _2 O- w/ h5 u, B) k7 ?/ D
    1:传输节点隐形错误。9 e+ \; ~# y; \. p5 z7 I
E0 XTD(bit30):标记是标准D还是扩展ID/ ?+ p% b/ i& j
    0:11位标准ID.* D3 w, c: a! W3 W! @3 U! t% R" o
    1:29位扩展ID
* Y9 w3 X( a8 a' A/ z' K
  x  p  o4 W4 Y: T: {E0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
! @/ j6 T% l' \( s+ n" S3 l# p9 K, h6 p    0:发送的帧是数据帧
1 c# b# _. |# i. K+ j5 `    1:发送到的帧是遥控帧, ?& Z. r3 X# ~" j, _0 }
E0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。
  }/ K; y7 _* _; K2 tE1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。
2 r6 K  B1 ~6 V! y9 M$ yE1 EFC(bit23:22):事件类型
# ]5 [; Z$ o' p" `    00:保留。
, S6 g: W( ?, N- D4 C1 ?% @4 Y    01:发送事件。
9 ~# U  ]: o2 g* p0 A; i+ D    10:发送取消。9 j  n0 [; x6 [' o2 G9 {# z
    11 :保留。$ y; S# [" a% j! s* f* W
E1 EDL(bit21):扩展数据长度% w  }) \# Z/ c& R( L0 e
    0:标准帧格式
8 ~) R$ w5 t0 W% N0 V/ \; B    1:FDCAN帧格式。# S7 C3 P' a' w4 K2 s
E1 BRS(bit20):比特率切换
2 w7 a' S4 W5 w. R    0:发送的帧没有比特率切换
% N, q& r" W7 Z. d- @
+ \  x/ }. P2 z    1:发送的帧带有比特率切换; S3 k' p$ o9 g! x. z( ~0 O
E1 DLC[19:16](bit):数据长度。& O/ [. h* C. }- T% \. h0 u' z. ~5 n
    0-8:传统CAN+CANFD,长度为0~8字节。
  b; o% U8 J4 w: S* }    9-15:长度为8字节。7 i/ O" _8 ~! \3 o4 d
E1 TXTS[15:0]bit15:0):时间戳。
/ J* p* C1 B1 b/ u0 V; k/ L7 r' }" w7 _9 V7 r
5)、TXBC.TBSA:发送buffer,有32个元素。
- ]8 A% l  S6 }" j0 r( Y
% s$ T) D( S8 y( n: v+ F
20191121175121493.png

7 n' d+ _4 M& I! |0 b9 B" r- }" F9 |) D
T0 ESI(bit31):错误状态标志0 H; ]8 x  @. ^9 o1 S4 H
    0:CANFD帧中取决于被动错误标志。
9 c' d  N, ?( s# |    1:CANFD帧中隐形错误。
7 v6 `1 ~( F5 _! q/ p# V8 GT0 XTD(bit30):标记是标准ID还是扩展ID& s* e7 \3 D1 p# t2 c
    0:11位标准ID
( Y6 R% ], j. c* O, r
4 ^- t* C2 [6 j    1:29位扩展ID
: y4 p0 E2 T4 G: Y4 j
! f5 P3 J3 }5 z5 h/ yT0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。0 P; N  z/ i) V; O9 i7 G
    0:发送的帧是数据帧, p2 d. U( E* d7 J: X, {- g
    1:发送到的帧是遥控帧  p+ K5 i8 r- m
T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。
( ]9 I9 I: _8 i5 F# F3 X- LT1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。' Y& ]. F/ p1 ^2 t# X
T1 EFC(bit23):事件FIFO控制1 @7 _/ W+ I# r- k  F" }
    0:不存储发送事件。4 Q+ y; i; w1 X7 E5 s# ^- C' m
    1:存储发送事件。. x2 q5 H9 k2 x8 G, p
T1 FDF(bit1):帧格式
+ }8 V* G( `7 m: Z    0:标准帧格式
) J( l! D. T; u" L! a& ^0 g    1:FDCAN帧格式。- d" c) b; ^# M. k
T1 BRS(bit20):比特率切换
! \$ R2 ?* X- E# c# l    0:发送的帧没有比特率切换7 @6 _0 [5 \. P: a

. d1 W5 D" x1 j- A* ~    1:发送的帧带有比特率切换
! n3 r( ]& k- o6 ^+ p" J$ [/ i# bT1 DLC[19:16](bit):数据长度。; L. ?& R- p0 b: y# k+ c
    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。
1 D+ F: W/ @, W7 [* |. r0 Y    9-15:传统CAN,接收帧长度为8字节。
( o" W  b* c1 r2 j3 l: [    9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。
/ t8 k5 r2 y* b6 w- P+ W/ R4 AT2-Tn:发送的数据。
1 ?  A: f3 s" X; @( J1 s% g2 i# s3 b- R
三、过滤器设置
% ~9 p6 p+ x+ c) B3 a4 M/ |* I, B# y% R" Y
        标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:! j; I" h. L4 N# }( }( k

2 I6 R5 ]: L. d) f9 z        1、指定范围过滤1 C6 g& D3 ?: N9 q
        通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:
) T6 |- E9 H  Z5 ?5 ]9 e5 ?% {
$ F) ]# _! z" E4 G
  1. SIDFC.FLSSAn.SFT=0        //范围滤波) p% h- j$ y' Z6 E8 E  r
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到RxFIFO中
    - N( i; m9 w& f  D5 c
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    # x7 B4 B8 R* z$ z& X: w
  4. SDIFC.FLSSAn.SFID2=0X321  //ID2
复制代码
" z8 P( e: b+ I: R
        2、指定ID过滤& R$ w0 i4 G3 k4 k

0 L5 V' J7 h' I, c! Y0 E# v        我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:
" P0 E! y9 W# u; Q: |
2 I- j: E% R: Q' r
  1. SIDFC.FLSSAn.SFT=1        //指定D过滤$ o. ^1 a9 E6 S
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到Rx FIFO中* g6 s5 P1 v0 K1 r
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    0 x% R6 N5 n; {1 y0 I
  4. SDIFC.FLSSAn.SFID2=0X123  //ID2
复制代码

: q" @. g6 J& ^0 R% P, G) j; `        3、传统的位过滤
! j" R' i# H6 f8 ]! A! k4 v        第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:9 N9 s6 L& W3 R, ?* Y0 o& v, W
5 }0 ^4 |0 J" ]% Z: Z
  1. SIDFC.FLSSAn.SFT=2          //传统位过滤# {: k+ C/ o0 R8 Q% s
  2. SDIFC.FLSSAn.SFEC=1         //如果滤波匹配成功的话将消息保存到Rx FIFO中
    1 X& [+ ?/ I3 _' C2 X& C
  3. SDIFC.FL SSAn.SFID1=0xFF00  //ID1
    % t5 {( A" \' Q$ x
  4. SDIFC.FLSSAn.SFID2=0xF00    //掩码
复制代码
' `* J! }  Z. j: O- X& Q+ B3 M
        其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。6 L, I/ A9 z5 y% d& v$ [

' p9 C5 }# r1 w6 ]9 V$ w' }四、CAN的收发过程, g' j- U6 q  s8 W! E

/ p  t, }, o7 Y  a        1、CAN接收过程
/ j9 V' V' O* W" l+ ~7 m/ \: S
3 Q2 e7 W2 [7 B5 K. U- V4 j        CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。7 K2 t9 T( ?& a4 k: t5 u! _# N

( L1 O+ K, @- X! k        接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。, N0 R/ t. v' a3 B% q& c
. _1 M$ o  j+ |8 X, s
20191127114550965.png
3 H6 a: E- A3 n. ]' T, K) ?
2 e: K: X% Z0 N- D- k3 N
        当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。% L8 @8 M; {) l" d5 ~# M# V
, {7 \# N% I. M/ V/ W5 L7 U

( N9 [1 v# S+ j2 A7 u
20191127111235302.png
8 O2 D' z: ?. n' }
+ l6 ]* u( ^1 ?2 c( O- L5 A* o
        为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。# n$ l5 x: }0 _: n6 G0 a( F# W0 o
        读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):
' m  F- `1 Z5 h
! O- [+ g! R! ?0 e
  1. /* Calculate Rx FIFO 0 element address */! W. H$ A: \3 |
  2. GetIndex = ((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8);" n' S8 d- B: k# M- S7 u5 }; C
  3. RxAddress = (uint32_t *)(hfdcan->msgRam.RxFIFO0SA + (GetIndex * hfdcan->Init.RxFifo0ElmtSize * 4));
复制代码
" w& C. T8 T1 }# h$ q" I
1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。/ `) W$ X% r" @1 j6 T2 c  i
! `0 U' W4 w. L7 {% c; x
2019112119095892.png
+ R4 B. m8 T1 r: G1 ?/ {

, c6 p3 d. _4 r( J( u% q) v2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。
: Y1 r8 P" M* i3 Y6 I: A3 G+ I3 z: |/ b3 p" Q
3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同2 V7 J+ t9 t+ a' e& ]

9 y3 X" k9 c0 L
" E& n( i% E% W  R

4 M% d; ?* I; U$ O6 c如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。
/ ^1 z) y+ T- h% g# j* U% g  K0 S3 l
" e" L/ u2 a' T* _1 K- }/ ?如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。, M: v) {0 B# _" \8 W

6 V; i7 ~2 m, L! ^        计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:( s! L5 s3 c2 r2 y$ s! r

/ V6 q& W" ~6 Y% {& W7 ^0 A
  1. pRxHeader->IdType = *RxAddress & FDCAN_ELEMENT_MASK_XTD;4 d4 c; R( U6 K8 }
  2. pRxHeader->Identifier = ((*RxAddress & FDCAN_ELEMENT_MASK_STDID) >> 18);% i) ~  d8 s% s- g
  3. pRxHeader->RxFrameType = (*RxAddress & FDCAN_ELEMENT_MASK_RTR);
    8 E/ Y; N' z: N/ |
  4. pRxHeader->ErrorStateIndicator = (*RxAddress++ & FDCAN_ELEMENT_MASK_ESI);  //这里地址自加了一次
    * v2 N: u  |2 K" R( J  N# ?4 e
  5. pRxHeader->RxTimestamp = (*RxAddress & FDCAN_ELEMENT_MASK_TS);$ H: e" ^: g  e: {& r
  6. pRxHeader->DataLength = (*RxAddress & FDCAN_ELEMENT_MASK_DLC);  ^) Y; E4 ^# d6 }; D8 w$ H
  7. pRxHeader->IsFilterMatchingFrame = ((*RxAddress++ & FDCAN_ELEMENT_MASK_ANMF) >> 31); //这里地址又自加了一次
    # T' S8 N7 _/ k3 ]
  8. ......
    " `( R; X! P  A' \4 u+ T# j( o6 `
  9. pData = (uint8_t *)RxAddress;9 |- z9 Q4 N1 v6 J3 W; s. x" |
  10. for(ByteCounter = 0; ByteCounter < DLCtoBytes[pRxHeader->DataLength >> 16]; ByteCounter++)
    , p) ~( d- M7 p4 @3 Z; }+ u; v
  11. {
    # g$ Y: e% T+ T1 l* A% y1 p: z& l# v
  12.     *pRxData++ = *pData++;
    7 \* j+ l9 @( T* S
  13. }
复制代码
; s0 T/ v' \! D2 C7 \" Z4 W
        2、CAN发送流程# [; l: M7 _. n- I4 m
5 C4 \6 ?5 c+ z3 o9 N6 t
        在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:) f- ?$ t( V' e! t* @
1 V5 K/ P/ P! x# \) s7 O
20191127150042199.png

, H% C: A3 ]0 Q6 k% \" U3 D6 q0 B( V: i( }: j# f' a
        因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。) P/ Q8 L9 Q/ S2 r# ?3 j

6 L3 z" p. p( e/ V* b        下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:
1 w5 Y) X/ Q" |- @6 j- k
. O! u3 P' H1 W1 j1 ]& s" M        1)、专用发送buffer
: w# a1 n" X; t2 P5 K% t  Z* D$ e9 `) |' \
         专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。
. {3 }7 l9 D' R8 R- v        如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。+ h/ @& [2 g9 z+ f* \
* U% Z2 S, R7 n
20191122101555794.png

/ j( B9 \  b2 \8 D- e2 {. u# H4 d' c% @0 x. Z4 V) b
        专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:
( o6 D6 z" n1 ]" D& a& w2 \% T* m5 o
发送buffer索引 (0…31) *4+发送buffer起始地址。
8 r3 X! A2 a3 H, x: v
% U. z9 ]. ^; H* X- V0 v& N5 C" c
( \# O1 y8 }# R% D3 A; g8 U/ _3 R3 {, v! Q4 Y2 z0 A
        2)、发送FIFO
/ Z0 r0 i" a/ e8 U$ K+ M" \# Z, W
& f0 Q& a  @1 t        发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。
: L& s" |2 f. e' O        新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。
9 m/ E; ^- S8 R9 r! S$ k+ A5 a  d& D: t
20191127160804244.png
/ l3 N: \+ Q- N; m9 h: k

" n$ E2 o2 ^; j        发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。
! i( p. j+ M6 K) e- ^& z) v
$ C9 o" E  S" @; ?% j" ~7 u        如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。& X4 X. X9 k8 n6 e8 k& y/ ~
        如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。
$ @5 p* r2 [4 p0 U        如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。6 P8 @5 ^) O* ^% x
        发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:
1 i: V' l7 A& X* r7 a" k2 n# `5 I& A, n( T/ l+ K: k$ b# q: {
放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。/ V! q/ ]! a7 O) C* M/ C! w

) F( U) v2 [9 A( H1 A        3)、发送队列
& e' [2 ~$ ?% q2 h& h        发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。
9 i* M% ]3 E/ B- n! c1 l        新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。
. m& K( L1 u$ N. h) r* ?) {' q        应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。
/ \2 z. x% g) k6 K) q/ {        发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是:
. S/ u" p0 |! w3 T# k3 G( L0 [( x
发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。
+ g4 J  Z, |/ h; D3 m1 m3 ?. v0 P9 @- r; G  E" A
        因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:2 B- D9 _) Y' o. q
- v1 q7 M; e) v1 }
        1)、发送buffer全部配置为专用发送buffer% i- Z1 [! T: a; T
/ E$ S# ]& ]( {- Y, r6 k- I
        每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。
% M- E5 A7 {: _& G: z/ U' m  `; p3 J- y, Z  S& _: @# H# I( l; M4 v( z
        2)、混合专用发送buffer和发送FIFO+ p" K) Z( s$ J( w* [

1 E' E0 b" E) E  p4 E4 a        在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。
6 _; R2 ]- n/ `4 C# J+ v
: o( X8 x1 r8 J
20191127162943182.png

+ J- r# ]' T, D( U$ u* Y* s9 w4 |/ F9 u3 \9 ~9 n
        发送优先次序:0 `2 v" C8 C0 A. `: ]: R; S
A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)& N4 C+ P! @9 p
B、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据
' D1 l% `: H0 m4 r6 [: K+ a# Z& |7 r, C' x0 W$ U9 S3 k9 c
        3)、混合专用发送buffer和发送队列
! ~' o5 O. F! g( \8 g8 b0 _
1 n6 b. }& o, \( x        在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。
: ?0 c1 u( Z2 R1 u8 ]" `$ }* [% B; j/ I3 F
20191127163202212.png

- H  c: r* T" D/ J  r% v
" B8 L9 Z# \! L) n: w        发送优先级设置:+ o/ {! U6 s" P- c* a9 N
A、扫描所有激活了发送请求的发送缓冲区
! _+ F  V) _1 V; l9 q* zB、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据; Q. |7 z+ L3 A% [( o$ S  k
; _( S$ B0 N' g: T' M
        再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。) }* G+ l# q! U) x
" j7 E( t' z& F& y/ z" s3 d
        FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。% Z: N$ ~+ \5 X- S& x
        发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。
. R" O& t) Y  l3 U" n, B; U        发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。
, f8 w- }: U: e# `. u% F" d  S4 |1 ?* S
        为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。
: N, c* b. a* g+ I1 `8 a0 s7 m
! T) h" i% y& B4 d8 h+ h        CAN发送消息的代码实现:
$ O+ s$ m* ^6 J( V. [
: M* A. l' N) v8 d! |1 \: k        发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:1 q6 z# l, @  C, ^; S- @
  E; B6 w9 q: Z& W' W$ w
  1. PutIndex = ((hfdcan->Instance->TXFQS & FDCAN_TXFQS_TFQPI) >> 16); //获取元素编号
    ) v; [) ^, Z& ?8 f4 l$ M! q
  2. FDCAN_CopyMessageToRAM(hfdcan, pTxHeader, pTxData, PutIndex); //复制消息到Tx buffer
    - O1 J4 ?+ W  h4 V, }  x* H
  3. hfdcan->Instance->TXBAR = (1 << PutIndex); //发出传输请求
复制代码

7 R7 j7 R/ v5 {3 Y  ?2 h        发送buffer请求寄存器FDCAN_TXBAR,描述为:# B7 a$ d1 j2 @7 J9 ?: L; G* p0 {4 ^
) `: m2 t/ L  g, [0 i, E8 f

3 ~+ {6 A2 B$ z* I2 }
( G* w7 o/ K( v" K% d/ p        此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。
2 M% t, v+ |/ f8 W9 F) A" Z5 j' T! g5 {

3 p* K8 S/ i7 A! t. G. c' p' J' q; B) ]5 Y2 W
五、初始化6 {# f3 T  u7 }

8 K; G% j2 F8 v8 L  J        初始化可以直接使用HAL库,) b! Y6 C( W  a: r( M
0 [! i! T$ ]$ d8 x  o
  1. u8 FDCAN1_Mode_Init(void)! e& k( T$ a) m
  2. {
    ' a2 J( s' }( }- h/ e% v4 \
  3.     FDCAN_FilterTypeDef FDCAN1_RXFilter;9 ^% l  M: ~4 }* c, @1 s( a

  4. : O) ~# E" Y) f8 K) A, w
  5.     HAL_FDCAN_DeInit(&FDCAN1_Handler);                              //先清除以前的设置
    $ O& d2 ^1 L2 Y# ]% Z
  6.     FDCAN1_Handler.Instance=FDCAN1;
      @( N! \5 n3 P: t" d! i
  7.     FDCAN1_Handler.Init.FrameFormat=FDCAN_FRAME_CLASSIC;            //传统模式
    # V3 Z2 `1 ?8 S0 a! H3 y8 c
  8.     FDCAN1_Handler.Init.Mode=FDCAN_MODE_NORMAL;                     //正常模式
      ~- D$ u6 U9 \; {- W
  9.     FDCAN1_Handler.Init.AutoRetransmission=DISABLE;                 //关闭自动重传  C9 X' }; N+ ?3 \  v% r4 v
  10.     FDCAN1_Handler.Init.TransmitPause=DISABLE;                      //关闭传输暂停           % t9 Q; M; G$ Z, p" J& X" i5 w8 j
  11.     FDCAN1_Handler.Init.ProtocolException=DISABLE;                  //关闭协议异常处理3 Q& `" Y. {3 e% k
  12.         
    2 n4 `& o  J9 J$ @6 I* J# s
  13.         //时钟为200M,baudrate=200M/(NominalTimeSeg1+NominalTimeSeg2+1)/NominalPrescaler,这里配置为1M4 O" M+ a  D5 `$ x
  14.     FDCAN1_Handler.Init.NominalPrescaler=10;                        //分频系数  k0 M0 c4 H) W6 r- t0 C+ d
  15.     FDCAN1_Handler.Init.NominalSyncJumpWidth=8;                     //重新同步跳跃宽度  y7 ^# U2 M9 l
  16.     FDCAN1_Handler.Init.NominalTimeSeg1=11;                         //tsg1范围:2~2569 q, i8 `5 }; W5 O  c0 V% ?
  17.     FDCAN1_Handler.Init.NominalTimeSeg2=8;                          //tsg2范围:2~128
    0 }  n" z) j+ F/ s) i2 v; C! [
  18.         
    $ ]5 B5 M  O9 s3 F) `0 T/ E
  19.     FDCAN1_Handler.Init.MessageRAMOffset=0;                         //信息RAM偏移,10KB消息RAM共有2560字,故可以偏移0~2560* I/ M, }3 \7 b& c6 ^" l
  20.         //使用了多少个滤波器就要设置为多少
    , l$ n  G. ~/ p6 U8 `$ }
  21.     FDCAN1_Handler.Init.StdFiltersNbr=3;                            //标准帧滤波器个数,0~128
    ; ]  o. S" p1 z6 a
  22.     FDCAN1_Handler.Init.ExtFiltersNbr=2;                            //扩展帧滤波器个数,0~64( ?5 Q* e( z1 X' P
  23.         
    9 S# p- W& I2 W2 ^
  24.         //接收FIFO0、FIFO1和buffer配置,此处没有使用FIFO1故个数设置为06 b6 J5 A- k  B% K3 E
  25.     FDCAN1_Handler.Init.RxFifo0ElmtsNbr=64;                         //设置接收FIFO0元素个数,0-64& f& M1 E1 a3 F$ t& |
  26.     FDCAN1_Handler.Init.RxFifo0ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO0元素的数据域大小:8字节        
    4 U( l) o, a  K, ^' h5 F+ e. }
  27.     FDCAN1_Handler.Init.RxFifo1ElmtsNbr=0;                          //设置接收FIFO1元素个数,0-64
    3 a$ W% w, L7 M0 V  F
  28.     FDCAN1_Handler.Init.RxFifo1ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO1元素的数据域大小:8字节                + ]2 u: N3 c" d* r3 o2 {2 [
  29.     FDCAN1_Handler.Init.RxBuffersNbr=64;                            //接收buffer元素个数,0~64
    : D7 b; x8 f5 U. L1 z% g
  30.         FDCAN1_Handler.Init.RxBufferSize=FDCAN_DATA_BYTES_8;            //接收buffer元素的数据域大小:8字节        
      F3 {8 j6 c, N7 F6 O) A! W
  31.         0 g  J; d% s5 Y  k  J$ r4 {6 }
  32.         //没有使用发送事件FIFO功能,故TxEventsNbr设置为0。把发送buffer全部作为专用发送buffer使用,故TxFifoQueueElmtsNbr设为0.% q3 j. s" A# h6 O
  33.     FDCAN1_Handler.Init.TxEventsNbr=0;                              //发送事件FIFO元素个数,0~32
    " c; ?2 P1 j% q
  34.     FDCAN1_Handler.Init.TxBuffersNbr=32;                            //发送buffer元素个数,0~32
    ; I+ y3 \5 s" F5 e! ^! @  h7 }3 S0 e2 R
  35.     FDCAN1_Handler.Init.TxFifoQueueElmtsNbr=0;                      //发送Buffer被用作发送FIFO/队列的元素个数,0~32  o4 P+ D9 m7 @+ S8 L7 P& b6 j* ^
  36.     FDCAN1_Handler.Init.TxFifoQueueMode=FDCAN_TX_FIFO_OPERATION;    //发送FIFO模式选择,可以选择FIFO模式或队列模式
    0 Q6 ]6 d8 a! f7 A5 z2 B
  37.     FDCAN1_Handler.Init.TxElmtSize=FDCAN_DATA_BYTES_8;              //发送元素的数据域大小:8字节
    : M7 L% C" N1 t8 ~% z
  38.         
    - \4 c# U" S- h$ m; }; t4 Y
  39.     if(HAL_FDCAN_Init(&FDCAN1_Handler)!=HAL_OK) return 1;          //初始化FDCAN' p& k; u4 q& s3 `: F2 {
  40. 2 Y1 p) M0 Y5 J6 h# t5 S& ~
  41.     //配置RX滤波器,标准帧   ; Y2 y9 C. X( A% O4 t7 A# ?  L! W
  42.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID9 c6 G4 }, E6 e3 C
  43.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
    + u6 q( H5 y' ?9 a
  44.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型! S; B% K6 v9 q% k
  45.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  - x9 X0 [& K6 R. r9 K: q
  46.     FDCAN1_RXFilter.FilterID1=0x112;                                //11位ID
    , O4 H' |6 T" Z) N5 j  F
  47.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码7 Q$ `) J8 m5 Y7 H7 }8 _$ y
  48.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化) I& Z' Q/ N. l) N  [5 n
  49. % T3 Y  z# @. ~/ Z0 x6 v% G* F
  50.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    - ~! q! \/ C4 Z. b- C* o1 m% E
  51.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                   , P$ o2 a7 N8 w6 G* e; _
  52.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    / y4 b- Y( m8 V- T' \
  53.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  % o+ b+ W/ D0 r; M
  54.     FDCAN1_RXFilter.FilterID1=0x113;                                //11位ID  [* L4 C+ o9 G+ q/ ?, S( Q: H
  55.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    : j8 |# _6 r9 H8 A0 M4 Y
  56.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化8 S( n* u0 b$ q& [' [3 S8 q& W
  57.         & @1 k: Q5 b4 G# J
  58.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID+ E9 j9 o( O$ W% e
  59.     FDCAN1_RXFilter.FilterIndex=2;                                  //滤波器索引                   # Z7 m" ^; ~/ ~6 ^1 E; u7 I
  60.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型9 P0 _0 s8 v( \$ e
  61.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    ' E( B. {/ L3 s* @9 j( j9 z& Z# b  q
  62.     FDCAN1_RXFilter.FilterID1=0x114;                                //11位ID$ k, v* h! b& Q
  63.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码3 P" P1 D9 y! ~, h! p1 T- Q
  64.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化: {1 @% i  ^5 u+ E
  65.         
    0 F$ X+ J1 v' {& P; y& X6 b& p+ p, l
  66.         //配置RX滤波器,扩展帧。标准帧和扩展帧的滤波器索引是分开的   
    4 K# `+ W8 ~/ H! v! S
  67.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID
    6 {2 i5 N$ n; ?
  68.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
    1 F4 F+ j5 s- Z
  69.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    & t, X$ P* f, r, _  ?# V
  70.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    . @& I2 z/ L  \9 h9 \! z
  71.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(2 << 12);                  //32位ID
    , K& W$ v5 D: h, M9 ]
  72.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码' z% i) p) O. |2 w( u
  73.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    ) B  K2 ?- c. }! S  x6 Q7 \& C# b

  74. " W+ F7 u# c3 ^! u; A" w
  75.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID6 o" ~) S( K: a$ u* V- p
  76.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                  
    ! Q* k, b4 ]. F! ]8 }  V
  77.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型* k3 N% }# k, F
  78.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    6 q! W( Z5 [! @0 m$ a1 N+ o
  79.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(3 << 12);                  //32位ID
    0 P$ G* @  C1 c7 J
  80.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码" q% l2 u  a; N* K7 f7 n, ^! }% f
  81.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;    //滤波器初始化) f; X5 R2 U9 d! b4 X% `+ F7 b
  82.         
    % q7 H4 E! b) z4 U1 ?
  83.         //滤除的消息直接丢弃
    , K: |; L  Z$ ^9 H1 @6 H
  84.         HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler,FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);  //设置被滤除掉的消息的处理方式
    6 x' Y& i/ [: _7 E8 G' W$ G
  85.         ' V8 l, ^8 p& ]0 j, x; e3 C
  86.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);   //使能新消息接收中断. x, I. g( F2 m; r/ D' ~
  87.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_TX_COMPLETE,0xffffffff);   //使能消息发送中断,0xffffffff表示所有的发送buffer都触发中断        ' B3 ?0 J7 `. ^1 F9 Y# Z
  88.         
    # ~- e9 T1 w9 [' _1 r* A
  89.     HAL_FDCAN_Start(&FDCAN1_Handler);                               //开启FDCAN) p7 S. B4 \0 u6 o" m
  90.     return 0;5 r+ U( {8 o; Q5 L; U! v
  91. }
复制代码
; b" @7 y( W, P) g8 t
- O7 h" U5 Z' w) g. C
六、CAN发送示例(来自正点原子)
0 Y6 J% o6 ^$ I( j% K
3 d* x4 ]/ J) \0 \
  1. //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)        # H' J( Y1 L/ C( Y( j
  2. //len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8                                    
    " s  T# V: D! {* y
  3. //msg:数据指针,最大为8个字节.: B0 y+ t8 m4 e. r& M3 q1 q
  4. //返回值:0,成功;
    , U1 l4 L# `1 O7 b  }
  5. //                 其他,失败;, a6 |0 ]0 y7 B. W* Z4 Y; W
  6. u8 FDCAN1_Send_Msg(u8* msg,u32 len)
    + U6 c  j$ l& y3 {- x. w* _
  7. {        - n& D, W% ~- i% `
  8.     FDCAN1_TxHeader.Identifier=0x12;                           //32位ID
    5 C  B: r4 y; E9 ~4 e+ d0 }& [* ~2 Q) R
  9.     FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID;                  //标准ID/ h. k* _: G& u- B/ ?
  10.     FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME;              //数据帧
    6 I, u9 D8 y5 ^
  11.     FDCAN1_TxHeader.DataLength=len;                            //数据长度+ I" X1 u; O# C' {5 u
  12.     FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;            
    2 c8 N7 f8 f" N' @' b
  13.     FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF;               //关闭速率切换( L' X7 L$ x! g( y/ z' @& k% A  A
  14.     FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN;                //传统的CAN模式
    # |0 O. D1 u2 M, n
  15.     FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS;     //无发送事件
    " X/ }5 M& @9 J) F0 ?
  16.     FDCAN1_TxHeader.MessageMarker=0;                           
    1 H3 m" l( {7 J! J

  17. + ]6 }+ P. ~9 x, F
  18.     if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送
    + f* y9 k' T" {  Y1 g
  19.     return 0;        # q! r' Q/ `3 R; I/ ^  {+ I
  20. }
复制代码
$ g9 K0 y6 F9 _0 Z- I. J! {
七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)
, m, h  O$ f8 L1 y: i$ b  D
' T2 G* X. C; l# S/ M( B
  1. //can口接收数据查询
    0 ]9 ~- ~" K, ?. Y9 H
  2. //buf:数据缓存区;         
    9 D  z' X+ a4 @1 v6 J0 F) O
  3. //返回值:0,无数据被收到;, |# Q0 d8 w  {: m2 Q% `) X+ F
  4. //                 其他,接收的数据长度;  W9 U+ \4 Q+ t  [; s/ k
  5. u8 FDCAN1_Receive_Msg(u8 *buf)
    5 ^/ U8 m3 u* I' V
  6. {        8 ]' B6 R9 _0 C1 P9 [$ U- _
  7.     if(HAL_FDCAN_GetRxMessage(&FDCAN1_Handler,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf)!=HAL_OK)return 0;//接收数据
    2 u; p# Q9 u6 Q- C% Q+ v' L
  8.         return FDCAN1_RxHeader.DataLength>>16;        * ~' H& x  u2 w+ h6 }8 ~6 h
  9. }
复制代码
2 e4 T& S3 G1 c* H3 Z9 t9 v2 P3 L/ C. W
参考:《正点原子》  STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准)' Y% e+ f5 n8 e6 I5 O, p: a! V

( o+ [. [  ~7 k. j4 q( ?' }( b/ _6 A9 r
收藏 评论0 发布时间:2021-12-22 14:00

举报

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