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

【经验分享】STM32H7的FDCAN

[复制链接]
STMCU小助手 发布时间:2021-12-22 14:00
一、介绍
4 P% `, z2 e/ _1 V6 d' A
2 d$ @0 a; f1 {# h- i        FDCAN(Flexible Data-Rate CAN)是CAN的升级版。特点包括:/ J0 v! S" Z6 M  ^' d3 @8 x

4 v7 j+ l; L2 ?; R# t3 Q3 G       1、每帧数据段最大长度由8字节上升到64字节。
: z* P* D6 E9 X9 A4 _! z2 b
& W: ~$ X% H$ P* K) W+ l        2、速度由1Mbps上升到5Mbps,甚至还可以更高。在一个数据帧中仲裁段(ID和ACK)的速率和CAN一样最高1Mbps,这样可以保证总线的健壮可靠,但是数据段可以5Mbps甚至更高,一个数据帧中使用不同的波特率,这就是FD(Flexible Data-Rate)的由来。
0 O0 M0 u: H8 J5 z  o/ |
; `6 A4 R$ }9 D" t/ M        3、向下兼容CAN。
, E4 [2 A# ~$ C4 ~* w% K
* x2 c$ t/ P' ~& e; T% D  G        H7的FDCAN包含2个可配置接收FIFO。多达64个专用接收buffer,多达32个专用发送buffer。可配置发送FIFO/队列,可配置发送事件FIFO。2 F( k" ~- h/ ]; j8 M! {
) q2 u& |3 p: K! p
二、结构$ N) s- }) o; p6 u. o

7 Z- h2 [# ~; U  X( ?9 Q6 \        先看结构框图:
! C% O0 K+ A; J% d) b
" m: O; {. ~9 [  W$ l
20191121162856859.png
' h8 Q3 U. G( o0 s. R1 K: h( H

8 F+ o8 g, c# |2 c1、两条中断线:fdcan_intr0_it和fdcan_intr1_it。可以通过FDCAN_ILE寄存器的 EINT0和 EINT1这两个位来使能或者关闭。; ]9 B$ O& H. s
% Y, s9 L9 S2 P2 l' s& ~7 X. }
20191127102816991.png
( D: j: Y) }4 k/ N0 P
$ R2 o1 n# J7 v' W' V
        可以通过FDCAN_ILS寄存器来选择FDCAN的中断是在fdcan_intr0_it上触发,还是在fdcan_intr1_it上触发,默认所有中断都在fdcan_intr0_it上触发,没有特殊的要求,只需要用一条中断线就可以了。$ l1 t, j5 B) a: e3 c# z4 Y% i0 Y$ T

4 V  k% S& _2 q+ u- M2 ~, N2、TX Handler:负责将消息RAM中的数据发送到CAN内核,最多可配置32个发送buffer进行发送。发送buffer可用作专用发送buffer、发送FIFO(发送队列的组成部分)或二者的组合。发送事件FIFO会将发送时间戳与相应的消息ID存储在一起,另外还支持取消发送。
/ d3 p' N+ W+ s  H. x( a+ `$ M# k! q/ H
3、RX Handler:负责将CAN内核的数据传输到消息RAM,支持两个接收FIFO(每个FIFO的大小均可配置)以及最多64个专用接收buffer(用于存储所有通过验收过滤的消息)。专用接收buffer仅用于存储具有特定标识符的消息,与接收FIFO有所不同。每条消息均与其接收时间戳存储在一起。
3 _* Y1 p/ W% y2 ?" [5 G
+ W. K& K% b& a6 V4 r( Y4、CAN core:CAN内核,读RX引脚数据处理后给RX Handler,接收TX Handler处理后控制TX引脚。9 H- d& M3 o8 t+ q

) V* J( x/ _& D* x6 S7 R5、Message RAM interface:消息RAM接口,外面连接着消息RAM。消息RAM是FDCAN的核心,本质是一段最大10KB的内存,把这段内存分成不同的区域,每个区域的作用不同,可以实现:过滤器、接收FIFO、接收buffer、发送事件FIFO、发送buffer。内存分配如下:- B3 c& Y; x; d  }0 P+ R, r( C+ W
. U+ F4 C) m6 L7 L2 ~7 _
20191121164211703.png

5 d+ v0 q& k0 r3 @4 S) n6 j1 A: X, f5 A1 Q% H# l5 k& m
        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字。
8 q9 x, z! Z0 a
" |, Z' t7 p* k4 R; P, @" F( f        1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,最多可以有128个元素。
4 L/ l5 R4 `( Z- d" X. o2 _3 m3 }* i3 W8 a# b7 }! i" T8 f& G
        2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,最多可以有64个元素。(F1配置寄存器来设置过滤ID,H7直接划了个内存区来设置过滤ID)
' L; P6 F0 a% R- B" \8 _- x6 a  s2 f6 O
        3)、RXF0C.F0SA:接收FIFO0,最多可以有64个元素,可以接收64条消息。
# s5 j& G) I5 k$ _  S% `$ F4 T: y3 ~! C4 n7 s8 ]  a. ~8 y* e
        4)、RXF1C.F1SA:接收FIFO1,最多可以有64个元素,可以接收64条消息。+ ^1 A* [. Y8 r( K- R, \

' W) ?- p. x4 G% F2 a: O        5)、RXBC.RBSA:接收buffer,最多可以有64个元素。+ t$ {, L+ a: f. x

- W, E/ t* B9 d. b' b+ T6 \" p2 n        6)、TXEFC.EFSA:发送事件FIFO,最多可以有32个元素。(不是发送FIFO)0 Y' z5 y3 u5 Y& v) y7 n/ W
8 O( r( Y. W7 u5 h% T3 _% A! W9 F
        7)、TXBC.TBSA:发送buffer,最多可以有32个元素。
/ I) t: H0 E' {2 E% x% k# E5 \( _1 |# ?+ d
        8)、TMC.TMSA:触发存储器,最多可以有64个元素。4 F& s9 R' e2 M4 `6 n/ Z& y$ @& o" J

2 Y7 n9 G5 q, S6 V4 ~1 c6 E; K% ~《注:这8个区域的元素个数和元素大小都是可以配置的,但是这8个区域的地址又是连续的。因此在初始化的时候,会根据每个区域的元素个数和大小来计算下一个区域的起始地址,并保存到相应的寄存器中》
5 W9 v! t7 z6 A4 w' X- x' C; Q" t0 Z& q
        下面来详细介绍这几个功能区域:: C$ r. \  Z+ P3 O" L6 p; d. F

' V/ S% o: K* n1)、SIDFC.FLSSA:这个区域用来存放11位过滤ID,有128个元素,每个元素占一个字,定义为:5 E5 B9 o' G* U" g# f
/ N) X3 p  J8 x9 f. m  [
20191121170625565.png

9 i/ d; G, S" {* p
. E; b0 Y/ O1 I: h  H0 }2 S6 USFT[1:0](bit31:30):这两位用来表示过滤类型,一共有四种:
3 F  R0 {- F' R* b& n1 c, g6 ~    00:范围过滤,过滤到SFID1到SFID2中的所有信息。
$ e" S* ?8 ]. I$ U2 Y' q    01:双过滤,过滤到SFID1和SFID2中的信息。) `, Q' b5 H8 |3 k' L
    10:传统位过滤,SFID1 是过滤值,SFID2 是掩码。
2 N( v: g& ?4 Y9 H# f. M6 F3 h9 j    11:禁止过滤器元素( \! j% \) c+ l. W, A
SFEC[2:0](bit29:27):标准过滤配置! [9 J; g" D# \; ]
    000:禁止过滤器元素* d$ ~3 |" M) \0 |, U) Q" ?7 V: \
    001:当过滤匹配以后存储在Rx FIFO0中。
2 u/ z5 H: R: f$ Q; a    010:当过滤匹配以后存储在Rx FIFO1中。( [+ H9 E& @! q- b( o: P( E
    011:当过滤匹配以后丢弃。5 \! t$ D# h' ?' v2 s$ }- _- {
    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。' @. m$ w8 {! A7 a
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。3 k2 v/ g) q& A$ }
    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。
) i$ }# P( A( P& C) x# E: H* r    111:存储到Rx buffer中或者作为debug消息,FDCAN SFT[1:0]忽略。1 ]; e2 t# P. N, X0 j1 Q
SFID1[10:0](bit26:16):标准过滤ID1,第一个要过滤的ID。! |9 Q, o6 a( F2 N9 b; x; @+ e6 z
SFID2[15:10](bit15:10):标准过滤ID2,此位根据SFEC的配置不同而不同,当SFEC=001~110的时候此位域表示标准过滤ID。当SFEC=111的时候此位域表示Rx buffer或者debug消息。' H# ^; t$ ?( _+ j+ T; l
" L2 j7 v7 Q' K! I# o6 R
SFID2[10:9](bit10:9):设置接收到的消息是存放在Rxbuffer中还是处理为debug消息的消息A、B或C。
( d# t* B7 C( o! z8 X# O/ }& b    00:将消息存储在Rx Buffer中。
, Y! |4 F4 B+ d
6 H* _6 L2 R" g  x    01:debug消息A。   
8 f+ y9 o+ j+ y7 W" D
# _' C) T8 E; a+ }8 }% n7 z    10:debug消息B。
* i* j/ {% ^  \; X3 y; }, G0 z4 o& ?/ r' Z  v
    11:debug消息C。7 W* g; N: `! Y; x

  q; l" M4 Q( e, E8 b8 O, ESFID2[8:6](bit8:6):确定是否将滤波器事件引脚作为外部接口。
( s: E! K  X7 o0 v- p# u' x7 f  m8 d3 {2 d1 H: f% d# l. X# E
SFID2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer 的开始地址RXBC.RBSA的偏移。; _  _  c# `" ?$ U, [
! v  g  K1 ?% q+ K7 n% J
2)、XIDFC.FLESA:这个区域用来存放29位过滤ID,有64个元素,每个元素占2个字,定义为:8 _1 ~. z3 U/ q+ t3 `; S  U! k' I

: x. [4 s2 f! s6 _" R
20191121171836710.png

$ x, u1 d, Z8 K# ?
0 l% c* n! [  X  i6 g/ E. n$ GF0 EFEC[2:0](bit31:29):扩展过滤配置
. |& r7 {9 q6 a/ u5 l$ v4 h    000:禁止过滤器元素# A! j' P4 p$ ]1 ^$ a
    001:当过滤匹配以后存储在Rx FIFO0中。
( q" p: J1 p4 L' _4 M1 k7 b  k    010:当过滤匹配以后存储在Rx FIFO1中
9 ]3 S/ U) v( ]! g7 d3 f- t0 E5 r9 {    011:当过滤匹配以后丢弃。/ M0 b% M7 z# l3 G! O, {( F
    100:当过滤匹配以后设置IR寄存器的HPM位,触发高优先级中断。9 [$ y, q2 h1 j# @: S8 w
    101:当过滤匹配以后存储在Rx FIFO0中并设置IR寄存器的HPM位,触发高优先级中断。
: M3 X  Y# x# s! X    110:当过滤匹配以后存储在Rx FIFO1中并设置IR寄存器的HPM位,触发高优先级中断。8 k; C0 B! m* f4 j
    111:存储到Rx buffer中,EFT[1:0]忽略。$ Z" ?, o- |1 g1 B/ [& p
F0 EFID1[28:0](bit28:0):扩展过滤ID1。
: ^, s5 l1 i2 K+ g$ L) KF1 EFTI[1:0](bit31:30):扩展过滤类型2 B' Z& O+ O* Q% T% ~
    00:范围过滤,过滤到EF1ID到EF2ID中的所有信息。% P" I+ O! ^5 d* ^
    01:双过滤,过滤到EF1ID和EF2ID中的信息。
8 o6 }6 i2 z+ ~. n" }8 V& v    10:传统位过滤,EF1ID 是过滤值,EF2ID 是掩码。
+ F; I  a# M& m! h) `; G$ k: L    11:范围过滤,过滤到ED1ID到ED2ID中的所有信息,没有使用FDCAN XIDAM的掩码
) e, B2 a9 M% d( Z+ gF1 EFID2[10:9](bit10:9):设置接收到的消息是存放在Rx buffer中还是处理为debug消息的消息A、B或C。1 s: N+ b( O9 y* l
    00将消息存储在Rx Buffer中。! J: p3 L: c* R5 m
    01:debug消息A。, [  I  H# s/ x8 a6 o. A( {- r
    10:debug消息B。
- S: X  P2 x7 k4 v) p* T    11:debug消息C.
) _- t: V4 o: m+ W- \F1 EFD2[8:6]bit8:6):确定是否将滤波器事件引脚作为外部接口。
% }- T8 j, u, F5 h) fF1EDIF2[5:0](bit5:0):定义当匹配以后存储消息的区域相对于Rx buffer的开始地址RXBC.RBSA的偏移。
$ }7 \! s8 o' X3 G4 m+ a5 W* o) Y6 f! l  K
3)、RXF0C.F0SA、RXF1C.F1SA和 RXBC.RBSA分别为Rx FIFO0、Rx FIFO1、Rx buffer,它们都有64个元素,元素的大小根据数据长度不同而不同,范围为4-18字,它们的位定义相同:
# S' F, r* \5 [# ~) H( d
& e+ R, t* ^' B7 @- R
20191121172451477.png

4 i2 t4 X, ^# y
& N) V! j; G8 L9 I) W9 q- wR0 ESI(bit31):错误状态标志: o! c% \7 o: [& Z' l' ^9 o" e
    0:传输节点显性错误。
# A/ J0 \! E  _) [6 Z+ |! m' b    1:传输节点隐形错误
1 r4 W1 H: n$ m3 y7 y* ER0 XTD(bit30):标记是标准ID还是扩展ID
. D, t4 {& O; y- w8 L" L  e9 B    0:11位标准ID
4 V/ J  X0 k( }0 v; V6 c9 T: [
+ _4 Y) V- ?) R$ D! K$ c% x    1:29位扩展ID
0 Q$ C4 E4 Z1 V5 `0 }1 c3 w! V9 T
' I# c( I6 W0 o8 s& r% x' AR0 RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。
1 i0 D' {% I9 g) C3 e0 t    0:接收到的帧是数据帧
9 F( S6 y. a( o: L% q+ w9 F6 \
4 `/ L2 G2 v" N2 `$ B' {    1:接收到的帧是遥控帧, T/ ~4 v: @7 u+ r+ X
R0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。* w1 m6 h) S' Y* n  O
R1 ANMF(bit31):6 t6 I6 G7 Q9 W+ i. q

0 d* l5 Z6 n  X5 X, G) ]    0:接收到的帧和FIDX中的过滤帧匹配
$ V; b& s3 b& Y: r( |3 p
4 s, Q" y7 J5 W( G5 O    1:接收到的帧和Rx滤波器中的任何元素都不匹配3 z* L; T/ n6 E9 k9 Y8 a/ ]
R1 FIDX[6:0]bit30:24):过滤器索引,0~127,表示和Rx滤波器中的哪个元素匹配。
" |- V# Z  M2 N, v1 ZR1 FDF(bit21):帧格式
* X# X* a6 Q* T4 r2 e$ v    0:标准帧格式
  t6 k4 I2 U. @# G+ P6 A" c7 k    1:FDCAN帧格式。, G4 p3 t3 q  C) x" T* X: e+ O
R1 BRS(bit20):比特率切换: r3 p$ @7 g5 K( U* s
    0:接收到的帧没有比特率切换8 ?4 ?7 Y9 Y, |8 P

3 ^5 ]# P& ^4 {- S8 h" v) j/ [    1:接收到的帧带有比特率切换. |- ?/ M9 c' |* M
R1 DLC[3:0](bit):数据长度。
7 o  q1 L0 h0 G" M1 y    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。7 ~2 k5 q* W6 B# o  _9 t/ |8 J6 E
    9-15:如果是传统CAN,接收帧长度为8字节。$ P9 y! M) y, O+ P* f4 [
    9-15:如果是 CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64 字节。( S' v$ l) Z# E. r6 _& k6 E0 J
R1 RXTS[15:0](bit15:0):接收时间戳。
! i: ?) n0 g5 J2 L0 q" _4 R0 p3 s) mR2-Rn:接收到的数据。6 G9 m% B$ S# J( p8 b3 n

3 D/ `" E) `9 |& U4)、TXEFC.EFSA:发送事件FIFO,有32个元素,每个元素两个字。
% ~7 c2 h, {' o  O. c* R( Q& F! _" v
( x2 m  [2 y) L$ S0 N
20191121174147402.png

8 v1 }6 p. [  ]) e: V) v! A
! e$ h9 s9 Q' CE0 ESI (bit31): 错误状态标志
2 H$ o( M3 y2 Y* d% P+ s5 ]' j8 N; B8 k! N; j
    0:传输节点显性错误
# Y* z9 A1 x$ b" T" R    1:传输节点隐形错误。
+ t; D" q" c/ m4 }$ BE0 XTD(bit30):标记是标准D还是扩展ID0 \2 ^# H. B5 _: l: E: J
    0:11位标准ID.3 {- s" n4 ?+ I& z
    1:29位扩展ID2 |6 C9 l- k& N# T% R

+ v4 z3 w8 Q. h, ?2 kE0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。( y+ [# N* N6 ~, K% T
    0:发送的帧是数据帧* @" y. {) M) I6 w8 f
    1:发送到的帧是遥控帧6 W. t# u: y" I% G  P1 ~
E0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11 位ID还是29位ID。
0 w1 h0 J* F0 j/ rE1 MM[7:0](bit31:24):消息掩码,从Tx buffer拷贝到Tx Event FIFO中。
2 A7 W5 ^' a2 B# C/ C8 KE1 EFC(bit23:22):事件类型+ V! z$ d  b# P
    00:保留。* K# t( c4 |) v6 i4 R
    01:发送事件。
. z6 U8 S3 x# T! ]- n6 G    10:发送取消。# d! l& o9 c* {' c
    11 :保留。
. c. c* J/ L0 I  ~% n; E' c1 D4 FE1 EDL(bit21):扩展数据长度
% F( g# N& B' V) |5 X# g    0:标准帧格式$ X9 X4 j" Y& q, L9 o
    1:FDCAN帧格式。: y8 L' K/ X7 Z! ^
E1 BRS(bit20):比特率切换
; N8 c6 z5 F  V* ?8 E    0:发送的帧没有比特率切换
% j0 R5 ?& K& i+ _. Y; [- {; x* R! O, m9 c- X6 e/ H
    1:发送的帧带有比特率切换! @- W  Y2 [" U- s9 N6 i9 H* `
E1 DLC[19:16](bit):数据长度。5 N3 r* M. p; O: Y4 M* K8 `4 }" z$ M* s
    0-8:传统CAN+CANFD,长度为0~8字节。1 E1 k: @( |5 C( W4 x% k- l1 Y) V
    9-15:长度为8字节。
; z; s" p1 }& _* sE1 TXTS[15:0]bit15:0):时间戳。! D) i6 F6 r* p  O, z& `" ?

/ ?( W# a* f* w& G9 v5)、TXBC.TBSA:发送buffer,有32个元素。( G- }' Z  I! H: |" f  ?2 r& |( I2 f
1 j: W. s+ B# i3 U2 I/ e
20191121175121493.png
$ f1 j; Y, V/ F

8 v+ c" ]7 ]0 L* t& ]2 F% ET0 ESI(bit31):错误状态标志) G. E7 I  B) u# c( V# F
    0:CANFD帧中取决于被动错误标志。
$ `3 I5 f1 l% M! t    1:CANFD帧中隐形错误。
3 U8 V  Z1 J* @5 |6 pT0 XTD(bit30):标记是标准ID还是扩展ID( s' x9 d% P0 ]) U" f  Z
    0:11位标准ID
( ]! r# d% e) V7 J* N$ f0 L$ m: p( t. A
    1:29位扩展ID
; e9 R7 r* m0 P* k6 U& x9 U3 |! C, D5 J! v
T0RTR(bit29):遥控传输请求,标记当前帧是数据帧还是遥控帧。$ {% o& r% Q# @( i6 d# }( m; p
    0:发送的帧是数据帧
! [; }. f$ X! {: o$ y    1:发送到的帧是遥控帧; L. k8 [" \' b
T0 ID[28:0](bit28:0):帧ID,根据XTD位决定是11位ID还是29位ID。! @& ?' X* O% U! A
T1 MM[7:0](bit31:24):消息掩码,配置Tx buffer的时候由CPU写入。
5 t4 {% c8 x: T0 b4 [; DT1 EFC(bit23):事件FIFO控制7 T4 N9 z/ ^2 m6 t0 d4 Z9 n
    0:不存储发送事件。
5 a) J# A+ @7 f0 E    1:存储发送事件。
. z6 o) L( `3 {4 ET1 FDF(bit1):帧格式7 k2 D, Z) C7 V
    0:标准帧格式
7 u1 s0 |4 G5 `& ?    1:FDCAN帧格式。! q# ^( G4 |8 a7 t7 X& ]
T1 BRS(bit20):比特率切换
' ^1 G( ^% w/ ]" b8 n0 y    0:发送的帧没有比特率切换3 W- b$ v; x  D: C- c+ U0 U. x

' N9 f0 u1 a( q* r, E% ^    1:发送的帧带有比特率切换3 P% y: ?7 z& r. @% O; s. u3 C
T1 DLC[19:16](bit):数据长度。
' Q3 L# h1 y' t: s( C    0-8:传统CAN+CAN FD,接收帧长度为0~8字节。. Y  C, `. Y) J1 X6 r$ a
    9-15:传统CAN,接收帧长度为8字节。7 C, {0 K0 n( Y8 X
    9-15:CAN FD模式的话表示接收到的长度12/16/20/24/32/48/64字节。
' f! Z  i& [# ^1 m* f1 v( V' ^T2-Tn:发送的数据。
1 x* V& G$ y. i. [: e5 q7 {% n( ]& Y9 Z5 s7 u/ h  X
三、过滤器设置, z3 c0 p# f' ?) U: Y  S

2 {. L) ?8 f, j4 X        标准帧和扩展帧过滤器的设置分别往SIDFC.FLSSA和 XIDFC.FLESA区域写数据即可,下面以标准帧为例,标准帧过滤器共有3种过滤模式:
" L6 Q4 `% U. ?; A2 |7 G. j2 E9 O8 L. b: x, s  r" \
        1、指定范围过滤
. o: a( d3 X5 h6 q( b" A        通过SIDFC.FLSSA的SFID1和SFID2来设置需要过滤的ID范围,其中SFID2的值要大于SFID1,这样只有ID值在SFID1~SFID2之内的消息才能被接收到。比如我们现在要设置只接收ID在0X123~0X321范围内的消息,使用标准滤波器n,SIDFC.FLSSAn(n=0~128)的各个位设置如下:
, s/ [% o! L" E# o, j
+ ?" h4 |; y( Z
  1. SIDFC.FLSSAn.SFT=0        //范围滤波' j. V4 g, ?7 f9 H/ N3 R8 s  R( @
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到RxFIFO中+ ~7 i9 q" K2 `$ {* A! s8 N
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    3 d+ {* K- \8 l/ {$ h
  4. SDIFC.FLSSAn.SFID2=0X321  //ID2
复制代码
5 b: j7 p: E: Q( r3 z
        2、指定ID过滤
8 Y, N2 T) M8 m/ r: I
( w# l: I$ L" t  @6 i! Y        我们也可以设置只接收指定的一个或者两个ID的消息,如果只接收指定的一个ID消息的话SFID1=SFID2。比如我们要设置只接收ID为0X123的消息,设置如下:
; F( u% h3 q) E; o2 O" W; }8 w7 i# d  l
  1. SIDFC.FLSSAn.SFT=1        //指定D过滤" v6 y" e8 _2 i# I+ f! T6 w
  2. SDIFC.FLSSAn.SFEC=1       //如果滤波匹配成功的话将消息保存到Rx FIFO中+ [2 Q0 k1 ?/ A0 p* D& p8 d8 C4 Z* o
  3. SDIFC.FLSSAn.SFID1=0x123  //ID1
    0 L$ c, s5 e) V6 J
  4. SDIFC.FLSSAn.SFID2=0X123  //ID2
复制代码
) q/ e: O) n! [# G! R
        3、传统的位过滤
) Y& P( J. E* y0 S( y, K3 G) O( Q. P        第3种过滤模式就是以前STM32的CAN上存在的位过滤模式,在屏蔽位模式下,过滤消息D和过滤掩码一起工作决定接收哪些消息,其中SFID1为过滤的消息ID,SFID2 为过滤掩码。举个简单的例子,我们设置过滤器SIDFC.FLSSAn工作在:传统位过滤模式,然后设置如下:3 U& s$ k+ ?2 M
, S" a4 Y+ ^; U8 e. `' X
  1. SIDFC.FLSSAn.SFT=2          //传统位过滤
    4 h% h; {6 g/ W- b$ ?
  2. SDIFC.FLSSAn.SFEC=1         //如果滤波匹配成功的话将消息保存到Rx FIFO中
    ) l& e1 S0 {, J) S2 @
  3. SDIFC.FL SSAn.SFID1=0xFF00  //ID1( j7 e2 T) V# z2 o
  4. SDIFC.FLSSAn.SFID2=0xF00    //掩码
复制代码

, D0 T  s1 n' q; E% g        其中SFID1是我们期望接收到的消息ID,我们希望最好接收到ID=0xFF00的消息。SFID2的0xF000规定了我们必须关心的ID,也就是接收到的消息ID其位[15:12]必须和SFID1中的位[15:12]完全一样,其他的位不关心。也即是说接收到的消息ID必须是0XxFxx这样的才算正确(x表示不关心)。
& S; h* T1 n! D" p7 h, H; Q  z3 Q
$ M' @/ x! o. o2 G四、CAN的收发过程
: j  U  ^! E& V. g
, T; y8 @7 B" b6 U7 J        1、CAN接收过程; _9 @- O! m3 K  o% l5 n1 Z$ P! o0 s
8 x: B1 U/ n- w8 L% ^3 A
        CAN接收到消息后会进行过滤,过滤时会从SIDFC.FLSSA或XIDFC.FLESA区域的第0个元素开始匹配,直至符合过滤器中的某个元素的规则,则过滤完成,过滤完成的消息会按照过滤器元素的配置进行处理。比如过滤器元素的SFEC/EFEC位决定过滤后的消息是放入接收FIFO0、接收FIFO1还是专用的接收buffer中。% r2 v: d! D$ ^% q

4 L# G7 R* M* C" ~( S6 G" q& ]        接收FIFO 0 和接收FIFO 1 最多可分别保存64个元素。两个接收FIFO的配置是通过寄存器RXF0C和RXF1C完成的。
; P$ N2 ]- F. ~' C! d  Y
( d7 x2 J7 `8 E1 \
20191127114550965.png

+ E& h/ {' H9 p) q9 ^! O- F; K  A& R: m+ H
        当IR寄存器的RFnF(n为0或1,代表接收FIFO0或接收FIFO1)指示接收FIFO已满条件时,在至少已读取一条消息且接收FIFO 获取索引递增之前,不会继续向相应的接收 FIFO 写入消息。如果在相应的接收 FIFO 已满时收到消息, 此消息会被丢弃,中断标志IR[RFnL]会置 1。也就是说,接收FIFO满了后会触发RFnF中断,如果继续接收,消息会被丢弃,并触发RFnL中断。7 o/ x2 {  m- ?! U% W
  L9 y  Q9 B0 a0 i
* O& m; [, T0 M* \1 F# i
20191127111235302.png

, p: S8 C  _' R1 d4 e4 h) H0 W3 z! T) C8 M  A
        为了避免接收FIFO溢出,可使用接收FIFO水印。当接收FIFO填充级别达到由RXFnC[FnWM] 配置的接收FIFO水印时,中断标志IR[RFnW]会置 1。比如接收FIFO0大小配置为64,水印值配置为60,当接收了60条消息时会触发水印中断,提前进行处理,这样就避免了FIFO溢出。' L  R7 {. w, `+ j3 R: e# |
        读取消息就是直接从RXF0C.F0SAn或RXF1C.F1SAn(n为索引号 0~64)中读,消息的组成结构在上面已经说过了,比如HAL库HAL_FDCAN_GetRxMessage函数读数据过程(假定从FIFO0中读):
' H3 I4 {3 y0 ~' }/ [( C% H% b
# c/ v- [- f) V( ?. X$ k8 M
  1. /* Calculate Rx FIFO 0 element address */
    5 u5 P: g# s; C) s
  2. GetIndex = ((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8);. s- c( a. p' M/ ~4 ~
  3. RxAddress = (uint32_t *)(hfdcan->msgRam.RxFIFO0SA + (GetIndex * hfdcan->Init.RxFifo0ElmtSize * 4));
复制代码
* I7 U" w3 W! m
1)、((hfdcan->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8):作用是获得FIFO0状态寄存器FDCAN_RXF0S的bit[13:8],读这里可以知道接收到的数据保存在FIFO0中的第几个元素中,GetIndex的范围是0-63,刚好对应64个元素。当FIFO接收到一条消息,GetIndex会加一,当从FIFO中取出一条消息,GetIndex会减一。% O: t- w: E. H, K+ Q

0 O  |7 Z! ]2 u& d" v9 w
2019112119095892.png
1 u1 W5 [- F8 e) O! a3 Q( r

" R8 V& ?% U: R/ o& J6 q2)、hfdcan->msgRam.RxFIFO0SA:是RXF0C.F0SA区域的开始地址。7 A% ]) h, U" f

6 B2 W; \# R0 e5 c; g. ~& e# t3)、hfdcan->Init.RxFifo0ElmtSize:是接收FIFO0的元素大小,根据数据长度不同而不同
/ l5 ~/ r" g; E6 p# P
6 @( L$ Z+ i# t" K, W
" k3 o9 U% M/ a# w/ G1 P
# M' @6 K: X4 x% l7 D
如果消息长度是8个字节,那么算上固定的R0、R1,元素大小就是4个字。
1 c; |$ h4 @6 ?2 G5 V
9 x) I; Z0 ?! _- Y如果消息长度是64个字节,那么元素大小就是64/4+2=18个字。
( I. B8 L; w% e8 f& _# z
& @  C) O1 h6 x" O        计算出了元素在FIFO0中的地址,直接按数据结构读数据就可以了:
. ]9 L3 Q: Y4 T: u, w+ Z9 H% c( f, w; ?8 z5 a& X
  1. pRxHeader->IdType = *RxAddress & FDCAN_ELEMENT_MASK_XTD;
    % o3 f) D& j; Y6 [
  2. pRxHeader->Identifier = ((*RxAddress & FDCAN_ELEMENT_MASK_STDID) >> 18);
    0 j6 g. y& [  \/ {3 z
  3. pRxHeader->RxFrameType = (*RxAddress & FDCAN_ELEMENT_MASK_RTR);! B& }* g. y0 H1 _3 T* T. s: \
  4. pRxHeader->ErrorStateIndicator = (*RxAddress++ & FDCAN_ELEMENT_MASK_ESI);  //这里地址自加了一次
    5 y8 s! C! M9 `4 u' @2 L$ B
  5. pRxHeader->RxTimestamp = (*RxAddress & FDCAN_ELEMENT_MASK_TS);6 i# i* y6 l1 ]) M5 P+ h; Z7 E
  6. pRxHeader->DataLength = (*RxAddress & FDCAN_ELEMENT_MASK_DLC);
    9 E/ ~: b- p( B, I) z
  7. pRxHeader->IsFilterMatchingFrame = ((*RxAddress++ & FDCAN_ELEMENT_MASK_ANMF) >> 31); //这里地址又自加了一次
    5 f0 K: M1 Z* u4 n" L' D6 s
  8. ......! d! u8 O& u3 R: ^5 b* h9 D/ R; r
  9. pData = (uint8_t *)RxAddress;
    , ]" ]7 j+ k- J2 _0 K) u% o6 t
  10. for(ByteCounter = 0; ByteCounter < DLCtoBytes[pRxHeader->DataLength >> 16]; ByteCounter++)8 r, }, @! b& S3 q
  11. {
    " t# I1 M, v+ B$ _; r8 Q% R
  12.     *pRxData++ = *pData++;
    . h, y: T9 ^5 R/ Z2 Y
  13. }
复制代码

( g2 q! J2 c! G, v8 t        2、CAN发送流程/ R1 ^  Y3 O3 B2 \% v8 M  n$ p% q
5 b2 g# @6 n" v( k! m' f/ w- K$ f+ A: X# ~
        在消息RAM中TXBC.TBSA是发送buffer,最多有32个元素。发送buffer可以配置为专用发送buffer和发送FIFO/队列。发送FIFO/队列是指要么是发送FIFO,要么是发送队列,即发送FIFO模式或发送队列模式,由TXBC[TFQM]决定:
: s1 [# a& [9 j7 w7 r& Z; G" Q: v4 J" ~7 K  C6 m
20191127150042199.png

6 t+ f/ g* X' t( V0 H) n0 k2 R; `2 B8 }6 `3 c6 v; J( O
        因此发送buffer可以有三种组合:全部是专用发送buffer、专用发送buffer加发送FIFO、专用发送buffer加发送队列。
) A  @6 `5 h, a' d' Y9 i0 ]8 z7 D% C
        下面来介绍一下专用发送buffer、发送FIFO、发送队列的情况:* K0 U+ F# f% {! o# {0 J1 _" Z

" T* R4 r1 f8 d9 Y" i        1)、专用发送buffer- s* o/ }4 W( B! P- E, I
6 P9 q9 |. p. u) J, S; J; w
         专用发送buffer可完全在CPU的控制下发送消息。每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。2 v& l* C  d! x8 @6 A7 w
        如果数据部分已更新,则会通过添加请求的TXBAR[ARn] 位请求发送。请求的消息在内部会与发送FIFO/队列中的消息进行仲裁,在外部会与CAN总线上的消息进行仲裁, 并会根据其消息ID发送出去。7 t1 P" b1 z9 e+ n

( N  W; f# o: Z1 c# Q: Z; t
20191122101555794.png
( K5 R- s2 o. e$ ?" d& N

  R/ e& _$ |$ z4 `5 w" U        专用发送buffer会在消息RAM中分配四个32位字(元素大小为4个字)。因此消息RAM中专用发送buffer的起始地址是:7 [, o) _7 h5 l" w3 f

# O1 i& q) P: t发送buffer索引 (0…31) *4+发送buffer起始地址。& b- ?& ^! T1 i2 U1 S* i& w
" t  c6 ?, w" B) m/ ]4 X

. C. e+ B# d! y2 }. T9 R0 I$ h+ g6 V5 s- i
        2)、发送FIFO
! `/ \% }1 x0 H4 ]5 J8 t* l) ]$ i) B3 [1 |) z: H% r$ @
        发送FIFO中存储的消息是先从获取索引TXFQS[TFGI] 引用的消息开始发送的。每次发送后,获取索引会循环递增,直至发送FIFO为空。发送FIFO可按消息写入发送FIFO的顺序发送来自不同发送buffer但消息ID相同的消息。FDCAN会计算获取索引和放入索引之差作为发送FIFO空闲级别TXFQS[TFFL],用于指示可用(空闲)的发送 FIFO 元素数。# \  y0 M: }& p
        新的发送消息必须写入以放入索引 TXFQS[TFQPI] 引用的发送buffer开始的发送FIFO中。 添加请求会将放入索引增加到下一空闲发送FIFO元素。放入索引达到获取索引后,会指示发送FIFO已满(TXFQS[TFQF]=“1”)。在这种情况下,下一条消息已发送且获取索引已递增之前,不应继续向发送FIFO写入消息。! ~' q$ j4 f- o" C4 e
, Z( ^5 B6 A& h. B3 ?$ v: u
20191127160804244.png
1 M9 m: x, n1 R) q: d( U3 X
$ H0 Z1 R0 L7 C% \4 D8 H; n3 W! }
        发送FIFO其实就是个环形队列,放入索引是队头,获取索引是队尾,队头和队尾之间保存着消息,相减当然就是待发送消息个数。当从发送FIFO取出数据,获取索引会自加,自加到等于放入索引时,表示发送FIFO为空。当放入数据到发送FIFO,放入索引自加,当绕了一圈自加到等于获取索引时,表示发送FIFO满了。
  W& @8 W. i/ U8 s3 K8 a9 R' n/ f
% O9 ]# ~. g4 ?        如果有一条消息添加到发送FIFO,则会通过向与发送FIFO放入索引引用的发送buffer相关的TXBAR位写入“1”来请求消息的发送。
/ ~1 E4 {7 p; J% i" p5 W! m* E        如果有多条 (n条) 消息添加到发送FIFO,则会写入以放入索引开始的n个连续发送buffer中。 随后会通过TXBAR请求发送。放入索引随后会循环递增n。请求的发送buffer数不应超过发送FIFO空闲级别指示的空闲发送buffer数。
/ R' O7 b- G5 w2 m        如果获取索引引用的发送buffer的发送请求取消,获取索引会增加到下一个具有挂起发送请求的发送buffer,并会重新计算发送FIFO空闲级别。如果取消对其他任何发送buffer的发送,获取索引和FIFO空闲级别保持不变。
6 L7 v( _/ q% c, P  s        发送FIFO元素会在消息RAM中分配4个32位字。因此下一可用(空闲)发送FIFO 缓冲区的起始地址是:& p/ V# K3 R6 r: r* S8 b& Z7 D

2 Q* m# n2 R, Q8 c放入索引 TXFQS[TFQPI] (0…31)的值*4+发送buffer起始地址。
8 A: m8 L# @  E3 |9 w$ f  ], l$ K& C% B/ n
        3)、发送队列
8 d; n% J0 I' G# F' {+ c+ x# Q4 T        发送队列中存储的消息是先从消 息ID最小(优先级最高)的消息开始发送的。如果多个队列缓冲区配置为使用同一消息 ID,则会先发送缓冲区编号最小的队列缓冲区。
, A+ s* X* J- R) j8 `4 n2 O        新消息必须写入放入索引 TXFQS[TFQPI] 引用的发送buffer中(放入索引 TXFQS[TFQPI] 只是队列头的数字)。添加请求会将放入索引循环增加到下一空闲发送buffer。如果发送队列已满(TXFQS[TFQF]=“1”),则放入索引无效,并且在至少有一个请求的消息已发出或挂起的发送请求已取消之前,不应继续向发送队列写入消息。$ ]" z( A* o( s; d' p! ~! v
        应用可使用寄存器 TXBRP来代替放入索引,并可将消息放入任何没有挂起传输请求的发送缓冲区中。
" `4 M5 ]# L: }- i/ i% ^& x        发送队列缓冲区会在消息 RAM 中分配四个 32 位字。因此下一可用(空闲)发送队列缓冲区 的起始地址是:
6 C7 b' o3 G# [7 `# k4 p1 }/ C' a
+ f. l2 U! D1 _. x$ K! L4 g- e# I发送队列放入索引 TXFQS[TFQPI] (0…31) 的值*4+发送buffer的起始地址。
/ _; `, \. J6 F0 n
/ e2 f/ }1 j5 @$ ?3 T        因此可以知道专用发送buffer、发送FIFO、发送队列的区别是对放入其中的多条消息的发送顺序不同:
; h# {7 f% N3 u% C  C# w, B* r: o# n: K2 ~8 N- E/ t( k* ], F- x
        1)、发送buffer全部配置为专用发送buffer6 I$ O; l; X2 z5 `- W) R3 P

  |1 y; N8 x/ R  ]6 X5 o1 x        每个专用发送buffer都配置了特定的消息ID。如果多个发送buffer配置为使用同一消息ID,则会先发送buffer编号最小的发送buffer中的消息。
6 ^1 E, w1 y4 Z% G6 P0 B
8 e6 s) ~; s, H        2)、混合专用发送buffer和发送FIFO
9 b$ N$ |' J" |7 [; X* M! j1 c' |' S, W4 w2 J$ H. v3 T* }
        在这种情况下,消息RAM中的发送buffer部分会被划分为一组专用发送buffer和一个发送FIFO。专用发送buffer的数量是通过 TXBC[NDTB] 配置的。分配给发送FIFO的发送buffer数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送buffer。
; H& T/ u/ z% o" i8 q2 V" x& I( G+ ^1 r( [8 S. y% f
20191127162943182.png

0 f* m+ Y! j2 q- o0 L
) F: [. m% L5 k" ?' N3 n' A% m2 Y        发送优先次序:, R( h, f4 c: G2 I( a9 i
A、扫描专用发送缓冲区和时间最久的挂起发送FIFO缓冲区(TXFS[TFGI] 引用的缓冲区)$ s) ]3 r3 ?8 D" C. c, h+ N
B、消息ID最小的缓冲区优先级最高,下次将发送该缓冲区的数据/ Z& q) c1 O% W) W+ w0 P% R

. v7 i7 c" [: B/ O* ~8 d0 Z1 Z6 e        3)、混合专用发送buffer和发送队列: J" U& M+ X9 C- }2 y
4 L1 j( e1 J& p/ m( b# y% \
        在这种情况下,消息 RAM 中的发送缓冲区会被划分为一组专用发送缓冲区和一个发送队 列。专用发送缓冲区的数量是通过 TXBC[NDTB] 配置的。发送队列缓冲区的数量是通过 TXBC[TFQS] 配置的。如果 TXBC[TFQS] 编程为 0,则仅会使用专用发送缓冲区。
8 E: `+ v, R' O4 c6 H/ r$ p$ q6 t2 n
20191127163202212.png
' v* e% F" l9 l# r( y' {. s6 K
$ H0 w  W% a# v7 C0 y  S; l
        发送优先级设置:
9 V3 ^" N7 V4 Z: \: b$ W- MA、扫描所有激活了发送请求的发送缓冲区
: J) D$ A$ d$ U; G5 i+ ^, l2 b! WB、消息 ID 最小的发送缓冲区优先级最高,下次将发送该缓冲区的数据
/ u+ G2 i4 J5 ^, Z1 U" a% t% G3 w8 }; ]$ g
        再来介绍一下发送事件FIFO,它并不是发送FIFO,不是用来存储发送消息的,而是用来存储发送消息的状态的。' n' F( g& }9 p- b
- ]% T& Y. U8 V3 |/ N/ _
        FDCAN 在 CAN 总线上发送消息 后,消息 ID 和时间戳会存储在发送事件 FIFO 元素中。为了将发送事件关联到发送事件 FIFO 元素,已发送的发送缓冲区中的消息标志会被复制到发送事件 FIFO 元素中。# j) ~+ x; C/ m
        发送事件 FIFO 最多可配置为 32 个元素。发送 FIFO 中介绍了发送事件 FIFO 元素。根据元素 大小 (TXESC) 的配置,会使用 2 到 16 个 32 位字 (Tn = 3 ..17) 来存储 CAN 消息数据字段。2 t7 `/ q: X1 E! F& d
        发送事件 FIFO 的用途是将处理发送状态信息与处理发送消息分开,也就是让发送缓冲区仅 保存要发送的消息,而将发送状态单独存储在发送事件 FIFO 中。这样做有很大的优势,尤 其是在处理动态管理的发送队列时,发送缓冲区可在发送成功后立即用于新消息。覆盖发送缓冲区之前,不需要保存该发送缓冲区的发送状态信息。; G4 ~9 J1 l. \0 `2 }! T; w  d
* R4 }' _# y' E, S
        为了防止发送事件FIFO溢出,发送事件FIFO仍然支持水印功能。; N3 H8 T; [3 D& _( x

, t8 q* E0 Q+ r- d+ T        CAN发送消息的代码实现:
1 {# B! L6 h! y- w# S. a% R
# n: T8 ^. y1 Y; L& A        发送流程与接收流程刚好相反,只需要把消息按照TXBC.TBSA格式构建好,然后写入发送buffer,写进去后设置FDCAN_TXBAR寄存器的指定位为1来请求传输即可。如HAL库函数HAL_FDCAN_AddMessageToTxFifoQ:, R& ~% R/ ^3 c- q9 |  W

9 U/ \; R/ c. M, o
  1. PutIndex = ((hfdcan->Instance->TXFQS & FDCAN_TXFQS_TFQPI) >> 16); //获取元素编号
    ) d, o7 b' j& s; |% E1 D+ i9 E) _
  2. FDCAN_CopyMessageToRAM(hfdcan, pTxHeader, pTxData, PutIndex); //复制消息到Tx buffer
    7 \. E$ R# i8 `, q  j
  3. hfdcan->Instance->TXBAR = (1 << PutIndex); //发出传输请求
复制代码

, f* B& j3 R! u5 q: U$ Q3 ?        发送buffer请求寄存器FDCAN_TXBAR,描述为:
! u4 [5 S& ?' }8 @8 h
$ R& R: Z4 c: v9 S! [; j
! i) ]7 @& a+ p* N* C7 E3 j- g

8 E, E: l9 ]8 o! o5 W/ M1 o) |& x4 f8 g        此寄存器用来设置FDCAN的哪个发送buffer可以发送数据,FDCAN有32个发送buffer,当把消息复制到这些buffer中后,就需要设置此寄存器来标记此buffer可以发送。, ]2 A+ d7 n: A0 |( n
4 f. ?- v' j3 N! w% W
- `! g3 E9 G+ n. A# t! G$ V2 v8 G
' k3 M8 n/ T2 r3 o! H
五、初始化$ Q; ?& d) Z9 u, h( U( W% ]

. b' G+ b7 |* m        初始化可以直接使用HAL库,% {4 n% p8 `9 C7 h1 w

% i- a1 N7 j3 z9 Z9 U
  1. u8 FDCAN1_Mode_Init(void): A. L8 a2 H7 \
  2. {
    " x: |) q) z  U  L! ~# a
  3.     FDCAN_FilterTypeDef FDCAN1_RXFilter;  z$ Y# G! ?7 o, o: C) ]

  4. # x7 r/ b1 R0 A1 N% y
  5.     HAL_FDCAN_DeInit(&FDCAN1_Handler);                              //先清除以前的设置
    : d/ H1 I6 x( U, e  i4 \
  6.     FDCAN1_Handler.Instance=FDCAN1;0 G* j) h8 U" F- j
  7.     FDCAN1_Handler.Init.FrameFormat=FDCAN_FRAME_CLASSIC;            //传统模式2 o0 o2 T3 W- T5 ]8 p
  8.     FDCAN1_Handler.Init.Mode=FDCAN_MODE_NORMAL;                     //正常模式
    . B& Y9 c  D1 _; A" k
  9.     FDCAN1_Handler.Init.AutoRetransmission=DISABLE;                 //关闭自动重传$ j9 `; O/ C, r4 g
  10.     FDCAN1_Handler.Init.TransmitPause=DISABLE;                      //关闭传输暂停           
    3 {" B) [  b+ G/ }( `" R
  11.     FDCAN1_Handler.Init.ProtocolException=DISABLE;                  //关闭协议异常处理/ {2 _' ?- y% `. C
  12.         5 i; R5 s" d6 J; |  ^
  13.         //时钟为200M,baudrate=200M/(NominalTimeSeg1+NominalTimeSeg2+1)/NominalPrescaler,这里配置为1M6 V; B; i0 c( p3 i* W; f+ f
  14.     FDCAN1_Handler.Init.NominalPrescaler=10;                        //分频系数
    ! x! F* t2 A& R+ b+ A3 F0 V, A- ^
  15.     FDCAN1_Handler.Init.NominalSyncJumpWidth=8;                     //重新同步跳跃宽度
    / V6 @/ j6 ^1 a9 C
  16.     FDCAN1_Handler.Init.NominalTimeSeg1=11;                         //tsg1范围:2~256
    1 n/ B4 K5 C5 Y0 d* d+ T
  17.     FDCAN1_Handler.Init.NominalTimeSeg2=8;                          //tsg2范围:2~128
    ! K! L1 ~; y% Q6 q
  18.         
    ' X- A- g+ y8 z$ T* u0 ^4 R" \
  19.     FDCAN1_Handler.Init.MessageRAMOffset=0;                         //信息RAM偏移,10KB消息RAM共有2560字,故可以偏移0~2560
    ( j4 L* D5 K# ~% n
  20.         //使用了多少个滤波器就要设置为多少
    7 ?: t; z" b8 c
  21.     FDCAN1_Handler.Init.StdFiltersNbr=3;                            //标准帧滤波器个数,0~128
    & B6 a% g# ~0 f! F
  22.     FDCAN1_Handler.Init.ExtFiltersNbr=2;                            //扩展帧滤波器个数,0~64
    0 U5 b5 N  Q$ q4 \# n/ w+ Y3 W
  23.         + P" E5 M9 s3 N/ L$ u9 ~
  24.         //接收FIFO0、FIFO1和buffer配置,此处没有使用FIFO1故个数设置为0* I+ c8 `! E( Y% H$ S" H
  25.     FDCAN1_Handler.Init.RxFifo0ElmtsNbr=64;                         //设置接收FIFO0元素个数,0-642 G' [4 M. o. g7 I5 p3 r
  26.     FDCAN1_Handler.Init.RxFifo0ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO0元素的数据域大小:8字节        
    7 t: j* ^1 {) ?" ]$ b7 W4 F* P  X
  27.     FDCAN1_Handler.Init.RxFifo1ElmtsNbr=0;                          //设置接收FIFO1元素个数,0-64) }5 s7 l3 c9 d  k. E. F) _( I
  28.     FDCAN1_Handler.Init.RxFifo1ElmtSize=FDCAN_DATA_BYTES_8;         //接收FIFO1元素的数据域大小:8字节                ) b* A& ~0 z  q  L% k1 Q2 p* R
  29.     FDCAN1_Handler.Init.RxBuffersNbr=64;                            //接收buffer元素个数,0~64
    ( T8 O: G' ~7 k
  30.         FDCAN1_Handler.Init.RxBufferSize=FDCAN_DATA_BYTES_8;            //接收buffer元素的数据域大小:8字节        
    ) e' a+ b% N! p4 ~- N# S, U
  31.         
    * M8 f+ ?  G1 b% J, \. M
  32.         //没有使用发送事件FIFO功能,故TxEventsNbr设置为0。把发送buffer全部作为专用发送buffer使用,故TxFifoQueueElmtsNbr设为0.9 n+ E! ]: }) j6 G
  33.     FDCAN1_Handler.Init.TxEventsNbr=0;                              //发送事件FIFO元素个数,0~32
    6 |1 _4 o' G6 h# Z/ Q- O
  34.     FDCAN1_Handler.Init.TxBuffersNbr=32;                            //发送buffer元素个数,0~321 M3 r* J. k% r9 H& S0 a  z3 H: p
  35.     FDCAN1_Handler.Init.TxFifoQueueElmtsNbr=0;                      //发送Buffer被用作发送FIFO/队列的元素个数,0~32
    3 b% t# H+ j3 N, A
  36.     FDCAN1_Handler.Init.TxFifoQueueMode=FDCAN_TX_FIFO_OPERATION;    //发送FIFO模式选择,可以选择FIFO模式或队列模式
    $ f8 ]' k* v' u3 k) o6 s. q% F! r; q
  37.     FDCAN1_Handler.Init.TxElmtSize=FDCAN_DATA_BYTES_8;              //发送元素的数据域大小:8字节
    * i: s9 b2 s9 c/ {! `1 C$ g
  38.         - ]: c4 p! U* k; o6 C+ b2 _( u
  39.     if(HAL_FDCAN_Init(&FDCAN1_Handler)!=HAL_OK) return 1;          //初始化FDCAN" _  k" e) N: ]8 X

  40. , n9 }$ q) X+ \; J! v
  41.     //配置RX滤波器,标准帧   - A; g4 E; f- V0 w* `* o$ O1 U
  42.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID
    0 a' ?3 z: b% G. k
  43.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
    2 R7 r& c" f; R' B% {
  44.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    5 C9 ]0 L! D$ R, [' ]3 Z9 A+ j+ r
  45.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  ' M+ |) X* J9 h# Q3 Q
  46.     FDCAN1_RXFilter.FilterID1=0x112;                                //11位ID, {$ r) g# Z3 J, g6 b! j
  47.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码
    $ f( Q6 `9 K. Z/ }2 @: B2 |6 [1 h6 N
  48.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    * A/ K  ]0 M/ S$ C! e) o' |
  49.   G/ H& {2 g% R2 N* F. f5 a
  50.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID  }: q8 q- C; \: |
  51.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                  
      m  h7 m9 N* V5 w
  52.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型% [- n7 z2 H4 \9 }
  53.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  
    * \, u( o, X' `# J/ A  Y* b- Y
  54.     FDCAN1_RXFilter.FilterID1=0x113;                                //11位ID
    " s) x6 b' w$ |! v; W, D
  55.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码  m! M  d* I# f. `
  56.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化' U* C# J# E! c" o- X- w& X
  57.         2 r) o5 W- L/ p4 Z
  58.     FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID;                       //标准ID$ c5 _/ B- x1 J; i. x
  59.     FDCAN1_RXFilter.FilterIndex=2;                                  //滤波器索引                   + _3 l3 [! f, F9 n) M
  60.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型, d0 k- j5 i9 O* L. n
  61.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  1 }$ q: _7 G+ O+ s4 O3 X/ L, j
  62.     FDCAN1_RXFilter.FilterID1=0x114;                                //11位ID
    3 ^6 G6 H8 G4 ^0 D& c$ E
  63.     FDCAN1_RXFilter.FilterID2=0x7FF;                                //11位掩码$ i+ k, U, w& h% s  D
  64.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化
    6 l! N3 K5 x' V* t
  65.         
    4 \; b' Q7 b2 A. a- L6 I) `
  66.         //配置RX滤波器,扩展帧。标准帧和扩展帧的滤波器索引是分开的   8 t) z0 A8 F( G! w/ J0 H& Z
  67.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID) m& R, }# l$ P8 M' z) ~- v( c' `
  68.     FDCAN1_RXFilter.FilterIndex=0;                                  //滤波器索引                  
    9 k$ g3 B( d+ N) n' z
  69.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型4 ?/ c4 u2 @$ f+ I+ P
  70.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  + }0 b2 t) U0 d: r+ B( L3 Y' N
  71.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(2 << 12);                  //32位ID% ~2 n. E' j. T. W) E- g3 A
  72.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码
    6 [; @9 O+ m- V) z; P. ]6 C
  73.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;//滤波器初始化' B3 L9 k( W2 C1 K3 [7 ?( T. Q
  74. 7 L8 a) h! ]' }/ m' D6 A% V- v: N
  75.     FDCAN1_RXFilter.IdType=FDCAN_EXTENDED_ID;                       //扩展ID
    & h  e$ u  \" l" L) ]
  76.     FDCAN1_RXFilter.FilterIndex=1;                                  //滤波器索引                   . U$ m3 D# Z- Q7 U1 [
  77.     FDCAN1_RXFilter.FilterType=FDCAN_FILTER_MASK;                   //滤波器类型
    2 K" o# C# ]. i  R2 x
  78.     FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0;           //过滤器0关联到FIFO0  - [1 R+ R$ f% k
  79.     FDCAN1_RXFilter.FilterID1=(1 << 20)|(3 << 12);                  //32位ID
    7 V  c) {9 A4 ?0 a% i  F' \, Q2 |5 }
  80.     FDCAN1_RXFilter.FilterID2=0x1FFFF000;                           //32位掩码
    ) ~9 ^9 v/ |( I  n
  81.     if(HAL_FDCAN_ConfigFilter(&FDCAN1_Handler,&FDCAN1_RXFilter)!=HAL_OK) return 2;    //滤波器初始化
    / }, C$ E6 A- j- y7 U; U) l
  82.         % @7 Z" x" J- @0 X1 B" ~1 x& G/ E
  83.         //滤除的消息直接丢弃0 f5 v& f9 H6 g$ K. f$ m2 r4 G
  84.         HAL_FDCAN_ConfigGlobalFilter(&FDCAN1_Handler,FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);  //设置被滤除掉的消息的处理方式
    7 h4 x' B5 C* E8 U$ e
  85.         
    & T4 {8 P0 Z  {4 C
  86.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);   //使能新消息接收中断
    1 ~' Z# h4 o. m7 e6 Z" ?, c5 B0 v
  87.         HAL_FDCAN_ActivateNotification(&FDCAN1_Handler,FDCAN_IT_TX_COMPLETE,0xffffffff);   //使能消息发送中断,0xffffffff表示所有的发送buffer都触发中断        
    8 N/ h6 _$ ^! W' H, ]
  88.         
    ! W7 N/ H( f+ Z% J5 L5 a/ P" v) y2 E
  89.     HAL_FDCAN_Start(&FDCAN1_Handler);                               //开启FDCAN
    + ~6 A$ S9 o- I9 T$ p9 g" E3 \* T
  90.     return 0;
    0 I# m) ~6 {% f' X. n3 o% U
  91. }
复制代码
5 m! [7 u4 Z/ I. q8 Q( y: b
; P/ T9 H" F& M: P
六、CAN发送示例(来自正点原子)
/ H$ R; n0 Q' [/ H  R# Q8 B* q) c6 v9 L; o
  1. //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)        
    ; N2 d! @9 U6 i  b" M
  2. //len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8                                    
    2 T' Q* Q) F* L- g8 s. m
  3. //msg:数据指针,最大为8个字节.' r3 v) |& M& t) z2 \! L; I
  4. //返回值:0,成功;
    9 H6 d4 {6 Q$ H6 C/ J. }. H9 B% Q9 t- U
  5. //                 其他,失败;
    # ^4 @& G; T* j  x/ Y
  6. u8 FDCAN1_Send_Msg(u8* msg,u32 len)
    ' @" E- @* h) ]3 [
  7. {        + d9 s) h4 z3 B5 d
  8.     FDCAN1_TxHeader.Identifier=0x12;                           //32位ID
    # _( D+ X7 ~4 e- |0 l- _
  9.     FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID;                  //标准ID% q$ r# Y. x1 D7 K3 H( m; Q
  10.     FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME;              //数据帧
    ( R3 h1 O# b& C: j% N* v% t+ M  R
  11.     FDCAN1_TxHeader.DataLength=len;                            //数据长度
    * r( \. `  j2 ~. I/ J
  12.     FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;            ( l* N$ @$ W' T. X# k# R( S
  13.     FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF;               //关闭速率切换' C9 \. W- V' F% f" M( U- F) B+ n! z
  14.     FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN;                //传统的CAN模式) q! K/ x+ A, S1 G( m3 T5 N5 W! Y
  15.     FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS;     //无发送事件
    % r7 \# s+ M( m2 I7 M( @9 S
  16.     FDCAN1_TxHeader.MessageMarker=0;                           ( N1 [( t2 @' ?' H  L" I
  17. & f' I& R$ W/ W7 Q! E% Z8 M6 R& e: R
  18.     if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,msg)!=HAL_OK) return 1;//发送. w* ^4 @( A6 b/ X+ L
  19.     return 0;        % v5 z- Z) V$ q$ F% K3 B0 L" _7 X4 e
  20. }
复制代码

+ G+ N& D/ x! I' G七、CAN接收示例(来自正点原子,因为滤波器被关联到FIFO0,因此消息只会放入到FIFO0)& M% h8 h0 V5 a5 Y+ s
9 D* c3 B! @! D$ o6 w  s
  1. //can口接收数据查询
    ) v! W+ o& n! b2 H+ t) ^' X
  2. //buf:数据缓存区;         
    * Y# O  U2 E- m
  3. //返回值:0,无数据被收到;0 t$ c0 k& p$ B) M7 [
  4. //                 其他,接收的数据长度;8 \5 g! z! {) ~/ J3 |: N; l
  5. u8 FDCAN1_Receive_Msg(u8 *buf)
    ( D  E) t+ c5 r3 }6 {; _
  6. {        
    , S0 v: Z2 p# G& u" W" W5 L
  7.     if(HAL_FDCAN_GetRxMessage(&FDCAN1_Handler,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,buf)!=HAL_OK)return 0;//接收数据
    # [# Q4 A, a8 u% N. I
  8.         return FDCAN1_RxHeader.DataLength>>16;        
    # H, _; A9 [9 L8 d
  9. }
复制代码
+ V3 A3 W4 C$ b2 T  t+ P0 n. ^
参考:《正点原子》  STM32H7开发指南-HAL库版本_V1.0 (文档中有很多错误的地方,笔者被误导了好久,阅读的时候还是要以H7官方手册为准)" R" c9 V+ [& E" w! z
- o* J8 G9 t: G$ U7 e* k
8 ~, P6 l9 l. e/ D
赞 收藏 评论0 发布时间:2021-12-22 14:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版