控制器局域网总线(CAN,Controller Area Network)作为一种专为实时应用设计的串行通讯协议总线,在工业及自动化领域占据着举足轻重的地位。它凭借简单的双绞线即可实现信号的高效传输,凭借出色的性能与广泛的适用性,已然成为世界上应用最为普遍的现场总线之一。实际上在我的应用场景并没有实际使用过这种借口,接下来我们也是主要学习一下STM32这方面的的具体使用方法。
/ s0 F7 B G# ^0 f/ C 本次测试的C092是支持FDCAN的,CAN FD(CAN with Flexible Data rate),即“可变速率的 CAN”,可视为传统 CAN 协议的升级版。值得注意的是,此次升级主要聚焦于协议层面,物理层并未发生改变。CAN 与 CAN FD 在多个关键方面存在显著差异:
1 h3 D# r* [, U/ |1 a- [" C0 b7 {4 L 传输速率不同:传统 CAN 的传输速率相对固定,而 CAN FD 引入了可变速率机制,能够根据实际需求灵活调整传输速率。在数据量较大或对实时性要求较高的场景下,CAN FD 可以提高传输速率,从而更快地完成数据传输任务;* h, @9 ?7 j7 s& U {
数据域长度不同:传统 CAN 的数据域长度有限,限制了每次传输的数据量。而 CAN FD 扩展了数据域的长度,使得在一次通信中可以传输更多的数据,减少了通信次数,提高了通信效率;+ F3 p; F( Q1 k. L, \
帧格式不同:为了适应可变速率和数据域长度的变化,CAN FD 对帧格式进行了相应的优化和改进。新的帧格式能够更好地支持可变速率传输和大数据量传输,同时保持了与传统 CAN 协议的兼容性;* J( C4 b' K v
ID 长度不同:CAN FD 不仅在数据域长度上进行了扩展,还对标识符(ID)的长度进行了调整。更长的 ID 长度提供了更多的寻址空间,使得系统能够支持更多的节点,进一步增强了系统的扩展性和灵活性。
) b s- p* i+ I9 E p 我们看一下班次开发板的原理图:
" Z: i2 x$ j: h
5 C; R: d! _: N: g2 w! ?* F
- p/ k3 [$ t# [7 ^7 K& ]& P
实物接口如下:2 {" z3 o; d, \, `5 o
3 a' Z3 W( \' _6 O9 G0 _; Y V) ~
) k# f+ l* G1 Q9 j: e
可以看到就在常用的USB接口旁边,也就是说基本上对于C0系列来说,USB和FDCAN只能二选一了。, a5 w9 O0 k! ?2 e- N! U* K
那么如何实现FDCAN的配置呢?
9 u: v- q2 `. u( j 先看一下时钟配置,因为C092支持最大的是48MHz,到FDCAN最大也是48MHz:3 `8 P% _! I5 x5 ^2 K2 X- K0 G
# q! ?( q9 k! P/ f( ~' q6 p" c 启动FDCAN:. g% g2 t* ?2 V& E/ M9 G: m; c5 H
5 F* G1 {: ]( D$ c1 C. h
9 `( z4 O+ {: K: d
% \/ {4 n( J! K# L' ?# z
这里没有过多的模式和配置,直接启用就好;) F' @1 D6 r1 y
注意修改默认引脚:
( u! x: N% p- z' K
, {: z. B$ J5 A9 l0 M
! Y% s8 ^2 d+ u 下面就是具体参数的设置,我们本次主要进行一下回环测试,所以要进行如下配置:
5 L$ l) R' w2 P" h# K0 G 基本参数
7 S; C! ]2 U: w# M | Clock Divider y' J8 ^; o2 M4 C- W Y6 C; D
| 时钟分频8 ~ n9 U1 N" ^. B7 K
| | Frame Format: I% n( ~/ I5 z4 i5 z; `/ k" H
| CANFD模式, ~& j# o6 G, m$ z) C* o
| | Mode
$ q2 D7 {) D6 z$ [; \% D | 正常工作模式
$ I3 v; A$ I2 l | | Auto Retransmission. H3 V) ^8 l( k3 Q" l
| 自动重传+ a9 L4 d5 l' R4 e9 d. u
| | Transmit Pause) n8 H; t0 j. C
| 传输暂停$ j( { n# d* K
| | Protocol Exception% O$ |6 z) A/ C, F' K1 G3 \% F f
| 协议异常处理& s' H# L! P* Q9 U2 l4 D6 e
| | Nominal Sync Jump Width M( @% Q7 G, F: ]1 r1 E) d
| 裁决段同步跳转段宽度) K8 N$ [( k/ s' S4 }1 g
| | Data Prescaler& }$ ?4 M2 U9 u0 a+ O( x: I @
| 数据段分频系数
7 _' Y$ }3 V5 F. M$ L; v8 H | | Data Sync Jump Width1 A, Z: g0 L" n5 m
| 数据段同步跳转段宽度
; W5 ?; U* o) P @+ { | | Data Time Seg1
) U+ @8 M2 S- [( K! U5 h3 F | 数据段时间段1
. k2 u# q M4 _5 K) f | | Data Time Seg2
' D; D( u, @6 J$ Y; Q+ {' U4 { | 数据段时间段2) w, ?3 |' D2 i3 \" m
| | Std Filters Nbr
8 s' y0 I5 d: }/ a- a | 标准滤波器数量
4 |% D- c9 o5 z3 T3 W7 m | | Ext Filters Nbr
6 o# k' l0 v a | 拓展滤波器数量
& T7 o7 t; ?9 Z$ c. h2 K& m1 L | | Tx Fifo Queue Mode
4 D! G) y9 W; v2 |/ j: W | 发送模式. v, P; O) c2 h0 K7 } L% w
| | 我们按照例程中的配置参数进行一下配置:) n7 c! Y$ j- ?; ?; j8 }$ q
8 C) J* A* M( ^3 F/ m m4 [+ x
8 [; S( Y( A, z$ Q 接下来进行比特率的配置,如下图:
a% L: D8 O' n
# e2 n) T7 A8 o5 ^8 I
) I: X% x1 Y' S' T9 F& C( D5 C# @ 上图的选项最小数值是1,比特率=CAN时钟/时钟分频/预分频/(Seg1+Seg2+1),所以就算是最小配置参数,得到的最大比特率是16M,不过我们还是不要设置那么大,就和SPI的配置一样,太大不一定通信稳定。
- ?! |% R4 _/ M/ _& `: { 依然是回环测试,我们将CAN接头短接:
! C: ^$ l6 K1 u. `1 B
" R( y+ Q5 a, N. D, j. B% Z
3 W; s* W& z5 c4 Y& U) n `: O K 这里我们主要配置过滤ID,前面各开启了一个标准滤波器和一个扩展滤波器:
0 Q/ W) G0 s; o) _$ X/ R- /* Configure standard ID reception filter to Rx FIFO 0. Only accept ID = FilterID1 */6 Y* {0 O( P7 E5 {8 h, k: m
- FDCAN_FilterTypeDef sFilterConfig;; ?' ~" g! C' ^
- sFilterConfig.IdType = FDCAN_STANDARD_ID;- V& ]7 ?$ k! n4 M% Q) Q# @
- sFilterConfig.FilterIndex = 0U;4 \3 q0 r$ i8 j2 }6 z
- sFilterConfig.FilterType = FDCAN_FILTER_DUAL;
$ c! o4 m: S4 }2 K% l, F - sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;" o/ h# C }7 T7 C, e0 y2 j
- sFilterConfig.FilterID1 = 0x444;
" m( T+ V9 V, n/ k7 I6 ^ - sFilterConfig.FilterID2 = 0x444; /* For acceptance, MessageID and FilterID1 must match exactly */
5 j( A& }" h7 m2 v# w; R" e - if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)2 q: o" B9 U! S, S# P
- {
0 K5 J# g/ z" s: p& j - Error_Handler();
' h0 j/ _! |/ G' r4 j( f - }
; W/ Q. Y- A4 Q& I
: q i2 J& V: q! O8 F- /* Configure extended ID reception filter to Rx FIFO 1. Only accept ID between FilterID1 and FilterID2. */' d! T5 _; V. q5 e+ H8 ~
- sFilterConfig.IdType = FDCAN_EXTENDED_ID;
& a2 i) V. C" @' u3 A/ ^, h9 ~$ L - sFilterConfig.FilterIndex = 0U;
0 a2 c. Y: Y5 A; e* D - sFilterConfig.FilterType = FDCAN_FILTER_RANGE_NO_EIDM;" ]( e6 B: e4 [
- sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO1;$ v+ x& d5 \" ?
- sFilterConfig.FilterID1 = 0x1111111;
) N# S* h9 ]+ ]4 L: H( E - sFilterConfig.FilterID2 = 0x2222222;
/ H' M; T# }4 u9 H - if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
" @9 K# k1 }) o2 W - {5 ~; _( n9 t/ B" Q. ]0 m
- Error_Handler();7 E) M6 _$ z% Y
- }
复制代码 然后是进行数据的发送和接受,这里都是通过FIFO的模式进行缓存的,并在接收节点进行接收数据打印:
* R, }( m+ \. }2 l$ I% c& y- FDCAN_TxHeaderTypeDef txHeader;) X% [- B4 }8 b' f. l1 Z) @
; j# T) l I4 W3 D- /* Add message to Tx FIFO */0 \( n: a/ l0 A" H
- txHeader.Identifier = 0x444;) a* E" ?' k( L& y% O
- txHeader.IdType = FDCAN_STANDARD_ID;, v6 w, Z+ i% o4 ?3 J" ]
- txHeader.TxFrameType = FDCAN_DATA_FRAME;4 R6 j3 A# X$ v" b
- txHeader.DataLength = FDCAN_DLC_BYTES_12;
% b4 m4 ]9 U; n& b B& w+ h - txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;/ ?; N8 V% u% w" ~+ {0 }
- txHeader.BitRateSwitch = FDCAN_BRS_ON;
7 x6 y9 i! x+ e( e+ q% `2 P - txHeader.FDFormat = FDCAN_FD_CAN;! ?- G2 v w& }2 y) l
- txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;
6 e2 S+ S+ P! `: I - txHeader.MessageMarker = 0x52U;' y$ N6 U. n& o$ S9 U
- if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData0) != HAL_OK)
" h, b7 E2 P6 n+ R% U - {8 I! x/ M) b4 ^, f2 Y
- Error_Handler();
7 e6 ^1 G) E+ ?8 r3 H4 u( h, X+ r - }$ S# @4 X% \. I' T: B( u- `' F
" p: z6 }6 r( q- /* Add second message to Tx FIFO */
( T" J" {- D% |' |( @" j, ] K - txHeader.Identifier = 0x1111112;+ s! D, S3 |7 t9 |4 [; G
- txHeader.IdType = FDCAN_EXTENDED_ID;$ J1 N, {5 f6 m- |: w
- txHeader.TxFrameType = FDCAN_DATA_FRAME;" b+ o5 }, b0 P( [+ Q# |7 S
- txHeader.DataLength = FDCAN_DLC_BYTES_12;
, R/ |% Z% T$ V/ K0 m! P1 Q" } - txHeader.ErrorStateIndicator = FDCAN_ESI_PASSIVE;- ]" k2 e7 S# y6 L7 M
- txHeader.BitRateSwitch = FDCAN_BRS_ON;& g c: k# ]5 y, \% I. A
- txHeader.FDFormat = FDCAN_FD_CAN;
5 I( j* Y! ^' h, Y/ ~6 n: } - txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;
4 O" Y ~7 U( T( }3 G- \ - txHeader.MessageMarker = 0xCCU;
( n q. R2 J5 k0 Y4 a - if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData1) != HAL_OK)
) x+ p. I/ ]1 S" ^ - {# r# P7 L8 p: r" y+ E! W- Y7 t, h
- Error_Handler();4 L$ y" w U7 V! r n
- }
2 E, N( Y$ ^( N' O5 d E
* C/ O4 O& \7 W1 ^: I" A- /* Add third message to Tx FIFO */& b: L" s6 ^+ |' m) J9 A
- txHeader.Identifier = 0x1111113;; B6 c) |8 [, Y5 l; E0 G
- txHeader.IdType = FDCAN_EXTENDED_ID;* Y/ b3 P5 i+ e* g' Q! J, Z
- txHeader.TxFrameType = FDCAN_DATA_FRAME;
A7 S& U) E: Q* |3 R - txHeader.DataLength = FDCAN_DLC_BYTES_12;0 \( b) p% }& N) ~: P+ U) p
- txHeader.ErrorStateIndicator = FDCAN_ESI_PASSIVE;4 e z2 T3 K2 S" c: E( n* A0 ^
- txHeader.BitRateSwitch = FDCAN_BRS_OFF;! A* U4 V6 q2 z" q( ~
- txHeader.FDFormat = FDCAN_FD_CAN;+ C1 ?& Z3 X8 Z9 {& C8 C
- txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;
- v. l# i' Z( X8 n( p, p: N - txHeader.MessageMarker = 0**U;" O* I' c8 ]6 o) j, Y- s
- if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData2) != HAL_OK)9 E* j. V0 ~. F% S; S& b
- {$ x6 w( C' s7 a7 B+ A/ X
- Error_Handler();$ h2 E2 m7 B% C9 X, ~) L; H" A
- }% I9 k' q( H0 c; x
- ! R9 _+ w7 X; s! F
- /* Get tick */
$ A& @' R7 w/ F5 j - uint32_t tickstart = HAL_GetTick();
$ \% z! C- o8 ~: M: Q
) E+ p R% c2 Z! [6 ~- /* Wait transmission complete */
3 U% ^5 z# p* h; K" E: v0 z - while (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) != NB_RX_FIFO)
6 O* M6 ?: Q9 T" d - {
" L; q+ \+ w+ v7 j e - /* Timeout handling */
% n8 Y4 U! n3 h* q+ }/ L - if ((HAL_GetTick() - tickstart) > TX_TIMEOUT); q- \3 E# B& D p
- {2 Y) ?$ L" |" ^# T" t
- Error_Handler();
( t5 C9 c& r+ a3 ~0 |; _/ B. @ - }8 p' J/ \( u0 g/ e" t0 q
- }% h) O# H, Y3 I, F6 P \. U
-
/ z* y9 x# B3 \ ~ - /*##-4 Receive messages ###################################################*/
$ Y4 \& B$ Y$ n. [# N1 U
^ p' g+ F! M; t ^5 b9 j ?- /* Check one message is received in Rx FIFO 0 */
3 o* z, a& w& \8 @& N: G - if (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0) != 1U)" s9 j/ E6 X' Y+ S! l
- {
5 W" W$ I; }$ ^4 _. Q2 U+ V* R' H - Error_Handler();8 T9 D i8 M9 g
- }
7 q: {7 X" t' ~, a0 U/ g( X
( `+ p" k9 N8 A: n5 F# E6 n- /* Retrieve message from Rx FIFO 0 */0 @- {: E x; L; ~3 ]
- if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &rxHeader, rxData) != HAL_OK)
: F9 d+ I0 Z2 O' r) n$ K( ^ - {
: z/ K" l$ d5 d - Error_Handler();
. h% f. @# }' e- M - }
* s# g3 q9 r, `3 k/ O
8 W) m9 o O1 g$ X9 r! o6 r9 f- /* Compare received RX message to expected data */
: w1 c; G7 {+ ` C E - if ((rxHeader.Identifier != 0x444) ||
9 A6 s* I# ]' m* n& E - (rxHeader.IdType != FDCAN_STANDARD_ID) ||
7 J) | K# }" } - (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||
! \" H, @1 C( E" d& _5 c, M. Y - (BufferCmp8b(txData0, rxData, COUNTOF(rxData)) != 0U))
1 T1 o7 P% U: R4 [* S# P - {
# M5 w1 u7 T, j4 s - Error_Handler();" G0 Y/ ~4 v6 c( s, P7 M# g
- }
% H+ E! S# A6 ?+ K0 d - HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);' R, F1 N3 Y1 r7 A, T/ ~: ~: V
- /* Check two messages are received in Rx FIFO 1 */
+ d" T. U8 \" J* d K - if (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO1) != 2U)
$ H+ N5 {1 Y8 ~* v8 ^ - {% C% W. W* j; A+ Y; E6 L
- Error_Handler();
% x( \4 {) k, V$ Q! m - }
, F3 V: d Q% l) m2 L9 @( e - $ H. g4 |! w& E) [
- /* Retrieve message from Rx FIFO 1 */4 ]5 m" u) e. P! b) {- i
- if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO1, &rxHeader, rxData) != HAL_OK)- H# L F+ [# F; W8 J% C
- {' o. q2 `4 G$ d: u, J5 ?8 ]# ~+ h
- Error_Handler();/ J6 ^ x/ D/ b
- }# u% e- X/ J+ X5 |
- ( t0 W5 Z% \- w; l
- /* Compare received RX message to expected data */8 n, M* t x" Y% h9 b/ s
- if ((rxHeader.Identifier != 0x1111112) ||
/ n6 \& d1 t, z8 _% B+ }7 H& r7 n# b - (rxHeader.IdType != FDCAN_EXTENDED_ID) ||
. ` l: p' M" R( l1 ~ - (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||
' t& ? P# K# n) S# p% _ - (BufferCmp8b(txData1, rxData, COUNTOF(rxData)) != 0U))% z1 z; }- F r7 l; D; q" i* }
- {1 y' ]; ]+ \) P3 ~3 c1 [5 H; K
- Error_Handler();
4 [3 F$ t4 g& w2 w - }$ ~& ~' J; b/ R: i9 F
- HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);
: Z- d/ S% u# ~; K6 Q - % m' E. ^9 |7 d: b. ?% w9 H
- /* Retrieve next message from Rx FIFO 1 */
4 @) q) g& T- t+ ` - if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO1, &rxHeader, rxData) != HAL_OK)
+ [* U9 R3 r- t- [ - {) j+ x) S% J% j* ?
- Error_Handler();/ X. z# V, p, H9 V) }% t, L
- }
0 E" `2 k+ w6 N1 A, Q4 h
: h2 F+ v" U* P8 k# y6 n. ^( a4 Z1 M- /* Compare received RX message to expected data */+ {2 r8 {0 j G$ W
- if ((rxHeader.Identifier != 0x1111113) ||& {) e& A2 V- d3 \- W
- (rxHeader.IdType != FDCAN_EXTENDED_ID) ||: r4 Y( m8 ]5 P* f1 {
- (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||
* s: c6 v7 y$ D) }" R4 Y - (BufferCmp8b(txData2, rxData, COUNTOF(rxData)) != 0U))
& a7 M6 C- Z- J' [ - {$ V/ B3 e: c( O _- O6 z& y$ o6 Z
- Error_Handler();+ X% U# P# y( j2 ?( W
- }1 g5 D8 e) k3 y7 a
- HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);
复制代码 最后在通过在FIFO中查询的数据进行打印,与发送的数据一致:
2 g N* [. m' B% h8 N! T( i
* B" |* k5 `5 q+ L- v
& |( r& o; m4 U" K7 d" }* a/ d 这里多了一个C8,应该是串口出现的问题。' B) E' J! j9 E$ v: D$ G8 f6 G/ s
& \5 t( }8 ?5 a& i
! Z2 A- t* @: Y1 o" J- g
_7 T/ p+ m. w- Q0 s
) x! A) i b' U7 i" l
9 [( {1 H' ]2 a" |5 i" K9 D' _3 M3 r$ P! Q" R: P/ Y
3 a6 n1 H$ T: @. \
# P7 S1 I4 e/ k6 \- K: _
5 l% O( e; x8 y, z9 Y$ e7 D( i5 u
7 v5 q% J2 J z0 W ]% Q6 A% \: N1 r, S
& j) O/ D$ t+ v" k* G% v% N
! I6 J6 q( A7 G: c! g2 a
|