控制器局域网总线(CAN,Controller Area Network)作为一种专为实时应用设计的串行通讯协议总线,在工业及自动化领域占据着举足轻重的地位。它凭借简单的双绞线即可实现信号的高效传输,凭借出色的性能与广泛的适用性,已然成为世界上应用最为普遍的现场总线之一。实际上在我的应用场景并没有实际使用过这种借口,接下来我们也是主要学习一下STM32这方面的的具体使用方法。
- c# |: f; L: X8 `8 N3 c 本次测试的C092是支持FDCAN的,CAN FD(CAN with Flexible Data rate),即“可变速率的 CAN”,可视为传统 CAN 协议的升级版。值得注意的是,此次升级主要聚焦于协议层面,物理层并未发生改变。CAN 与 CAN FD 在多个关键方面存在显著差异:
. a' F6 ]# P) y- y0 q 传输速率不同:传统 CAN 的传输速率相对固定,而 CAN FD 引入了可变速率机制,能够根据实际需求灵活调整传输速率。在数据量较大或对实时性要求较高的场景下,CAN FD 可以提高传输速率,从而更快地完成数据传输任务;
1 T0 {+ z& R6 x7 h! v- u3 ^- [0 W 数据域长度不同:传统 CAN 的数据域长度有限,限制了每次传输的数据量。而 CAN FD 扩展了数据域的长度,使得在一次通信中可以传输更多的数据,减少了通信次数,提高了通信效率;* @$ s$ K+ w: I3 D
帧格式不同:为了适应可变速率和数据域长度的变化,CAN FD 对帧格式进行了相应的优化和改进。新的帧格式能够更好地支持可变速率传输和大数据量传输,同时保持了与传统 CAN 协议的兼容性;# s3 r$ D% X0 O: B4 P
ID 长度不同:CAN FD 不仅在数据域长度上进行了扩展,还对标识符(ID)的长度进行了调整。更长的 ID 长度提供了更多的寻址空间,使得系统能够支持更多的节点,进一步增强了系统的扩展性和灵活性。! p" }1 k: B5 \
我们看一下班次开发板的原理图:' t3 r, b0 k- f' c T4 G6 h
0 J4 P7 B& l) h D
( X/ a$ X+ f# H$ I
实物接口如下:: p) [' y* N1 E* L5 ?
( G9 W& M$ W$ F/ J P
, v6 `% ?/ z: g; x7 Z& O3 @8 T 可以看到就在常用的USB接口旁边,也就是说基本上对于C0系列来说,USB和FDCAN只能二选一了。* i" X+ }) d" b9 H; r0 C) C) U) q
那么如何实现FDCAN的配置呢?& N0 U: K u$ A6 p
先看一下时钟配置,因为C092支持最大的是48MHz,到FDCAN最大也是48MHz:
- @' b* B- z4 Q/ l! D& l& J
7 g% C6 H0 f* s5 e4 B- _ 启动FDCAN:
8 w* {8 b4 p* S6 v }" z7 _1 I. y! M' w) |: A
, Z: p" H4 z! l8 h
5 A7 V/ L4 Z) D3 b/ Q: e3 O
这里没有过多的模式和配置,直接启用就好;0 [' d5 ^* w* n# Y' v9 E% b" z- t
注意修改默认引脚:
; y& J& Y3 p3 H$ B0 ]- U
( S! w! Z% X" C& t" R+ g* K 6 B8 Q' a* T2 J7 O# z0 s
下面就是具体参数的设置,我们本次主要进行一下回环测试,所以要进行如下配置:
# L" P1 Q: R4 f9 ` \# U& k+ P 基本参数
: P! A$ w1 A- h' I | Clock Divider! n0 K1 s3 w2 B" t% J! c; G
| 时钟分频2 N3 d- M8 a' ^% D
| | Frame Format
; n# i/ {: |9 F7 M | CANFD模式
) C0 q) I5 g- f& E, d9 R8 X* w | | Mode( E8 h$ }# d6 G+ A L- ?) U
| 正常工作模式! Z7 ^) P# g! j" ?$ O
| | Auto Retransmission
- p: y$ x# D3 i! k; E* V( Z | 自动重传/ l. ~ j1 e# b/ G+ C
| | Transmit Pause
7 I: ^( w, ]6 x3 Z5 @ | 传输暂停
0 i# e' N- ]8 ]$ ^( h" Y | | Protocol Exception0 N2 n9 i, a2 P) E& D/ [
| 协议异常处理* L0 B. S8 Z; m m/ a4 ?. s% p
| | Nominal Sync Jump Width
& ]4 V: K# g: X5 X; v | 裁决段同步跳转段宽度2 T8 l: f4 q% @; w8 B. C- H9 w3 W B
| | Data Prescaler! `% p* A( n- j
| 数据段分频系数( M$ e \$ X, v2 ^; s
| | Data Sync Jump Width9 l. C! W p9 s- w7 ]$ O
| 数据段同步跳转段宽度
! T H4 |$ |' K9 [, Q5 v | | Data Time Seg1
" W$ H* t4 x% G | 数据段时间段1
6 D+ J$ V+ W9 h7 f) P | | Data Time Seg2
) |/ P& g/ i3 X8 P4 x | 数据段时间段25 n6 i5 {$ [2 Y+ R! q: e4 B# Z4 Y% s
| | Std Filters Nbr
5 r& w! r, c0 a9 d" d+ d' T | 标准滤波器数量
# `: G: {8 H3 n9 z: d. n U | | Ext Filters Nbr$ F& D7 d$ e# ^5 f5 g
| 拓展滤波器数量
/ N1 T T+ u5 @0 \3 H( s& |1 x | | Tx Fifo Queue Mode6 O# p- |3 I+ J' `
| 发送模式2 B8 y# u3 u" O2 X
| | 我们按照例程中的配置参数进行一下配置:+ |* y0 c* A7 r6 a8 L1 C
; K2 y0 ^! j1 {6 x7 u+ e* o0 U7 R
; W z* i! f6 D8 ]( Y 接下来进行比特率的配置,如下图:+ M7 G9 w5 E/ e
o2 A. o$ t9 j& }/ Q1 Z
- W6 g$ P) K9 X) W
上图的选项最小数值是1,比特率=CAN时钟/时钟分频/预分频/(Seg1+Seg2+1),所以就算是最小配置参数,得到的最大比特率是16M,不过我们还是不要设置那么大,就和SPI的配置一样,太大不一定通信稳定。1 _5 e$ V2 [- q: k
依然是回环测试,我们将CAN接头短接:% W8 Y& I; z6 N7 x* O
, D! Q' v6 E/ v* ` K! o# m; q! {
& }2 O0 ]; Q8 G, @6 _" |, i0 n7 } 这里我们主要配置过滤ID,前面各开启了一个标准滤波器和一个扩展滤波器:0 v, M a* \/ I3 ? v5 o. M
- /* Configure standard ID reception filter to Rx FIFO 0. Only accept ID = FilterID1 */
$ M$ }% f# t, P+ r4 J w7 q - FDCAN_FilterTypeDef sFilterConfig;) ^6 [( C* |! [9 r7 }
- sFilterConfig.IdType = FDCAN_STANDARD_ID;
. o$ V( \1 I# Q" p" |* |6 V - sFilterConfig.FilterIndex = 0U;0 j0 D) o3 E" k' ]3 P
- sFilterConfig.FilterType = FDCAN_FILTER_DUAL;$ n1 M! @, E7 q$ p
- sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
8 w! z( m; H* n1 y. p& e x - sFilterConfig.FilterID1 = 0x444;
6 Z4 j5 ]. u5 B' w" K# X - sFilterConfig.FilterID2 = 0x444; /* For acceptance, MessageID and FilterID1 must match exactly */
/ q9 y7 J) }& s7 \+ [ - if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
* r# @1 V) T9 _# E0 E6 Y, z - {7 u. f" C# O3 c, ^
- Error_Handler();' I6 S* s; Q7 P
- }
( P& c& U3 g5 h6 F& c - 8 D7 ^. ~! v0 u: J; m
- /* Configure extended ID reception filter to Rx FIFO 1. Only accept ID between FilterID1 and FilterID2. */" G- g5 j' ^% ?" [: F# W5 }
- sFilterConfig.IdType = FDCAN_EXTENDED_ID;/ K" Q1 J, n) `8 _
- sFilterConfig.FilterIndex = 0U;) k2 x+ T' T0 E! H) ~& d3 W
- sFilterConfig.FilterType = FDCAN_FILTER_RANGE_NO_EIDM;5 `* F" v% h5 l6 x1 i) R' B: j
- sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO1;% [' F" [* w6 F6 s, R4 H& [
- sFilterConfig.FilterID1 = 0x1111111;8 ]2 | L7 [! G/ x
- sFilterConfig.FilterID2 = 0x2222222;, ^ t* i" Z3 J# h; D2 j
- if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
q7 H9 U; }5 g4 _" i - {
$ U' k, j$ q& ?6 j& } - Error_Handler();( d2 E3 W. D) o/ m9 q: C
- }
复制代码 然后是进行数据的发送和接受,这里都是通过FIFO的模式进行缓存的,并在接收节点进行接收数据打印:. t, f( {- w& m+ H
- FDCAN_TxHeaderTypeDef txHeader;3 Z1 B+ L8 ]/ @ J- K& v4 D# w1 ~
- ) _( z& H$ b# i; K Y+ F: \$ v6 S4 O
- /* Add message to Tx FIFO */
7 e2 i& w: v; k6 X - txHeader.Identifier = 0x444;) q) M @1 M3 j. h
- txHeader.IdType = FDCAN_STANDARD_ID;
" l7 t) e6 h7 e - txHeader.TxFrameType = FDCAN_DATA_FRAME;
5 N$ M; x' k- W8 T. r - txHeader.DataLength = FDCAN_DLC_BYTES_12;9 {: \9 F% Y9 F& T1 g. ~
- txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
9 U8 A, p/ j$ o6 g( X0 Y" R - txHeader.BitRateSwitch = FDCAN_BRS_ON;, R5 N( W. f7 W
- txHeader.FDFormat = FDCAN_FD_CAN;
2 B# _: p: Z3 C) J5 A - txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;; H$ |9 i6 p8 i, p
- txHeader.MessageMarker = 0x52U;( U3 K, | S# \$ \1 i
- if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData0) != HAL_OK)
) i+ T9 H3 l$ w/ L* \ - {# h8 Y/ C- A4 n. f3 X
- Error_Handler();
5 h' y9 s9 b8 C: r+ N7 s - } g/ }2 o" z- {/ G
! A5 L+ u. G! _2 w5 s: g; b" W- /* Add second message to Tx FIFO */
$ ]4 X7 i' t6 E1 O - txHeader.Identifier = 0x1111112;
: @8 }! C# M" a& o. O" w5 K7 H3 M - txHeader.IdType = FDCAN_EXTENDED_ID;, D& N4 A& |5 L' E
- txHeader.TxFrameType = FDCAN_DATA_FRAME;" e& v" O* n, h1 {9 _
- txHeader.DataLength = FDCAN_DLC_BYTES_12;
, B8 C. n3 t- X" Z) n+ {- |, Z - txHeader.ErrorStateIndicator = FDCAN_ESI_PASSIVE;
: L2 P' f% P% e0 I - txHeader.BitRateSwitch = FDCAN_BRS_ON;
7 W+ M& F! H5 r1 y- D& m5 A7 x - txHeader.FDFormat = FDCAN_FD_CAN;
- }: I8 t( s( {" ~5 c& M+ ? - txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;% _0 `: M( d2 t) {* s
- txHeader.MessageMarker = 0xCCU; Q, L* z4 ]$ z: o9 S* k1 H, N
- if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData1) != HAL_OK)
3 Y: n3 s0 c/ o$ X& H1 o! \& W - {
+ }' }! g/ v$ A: r: C - Error_Handler();
5 d; O! T) y% I; e6 P( h, a& n - }) f6 w2 g: ?9 Z" Q4 h) r8 w8 y
- ( E2 s$ b, r. X3 U& E1 |% u0 b
- /* Add third message to Tx FIFO */
, M; A5 K! P/ u1 B. G - txHeader.Identifier = 0x1111113;
7 e5 m: e* s5 A( c/ m$ G - txHeader.IdType = FDCAN_EXTENDED_ID;
+ u% E/ Q1 y( N! c, a - txHeader.TxFrameType = FDCAN_DATA_FRAME;4 t [0 v' W. |" H) J
- txHeader.DataLength = FDCAN_DLC_BYTES_12;+ g; T: `: V3 s7 Q9 h0 o
- txHeader.ErrorStateIndicator = FDCAN_ESI_PASSIVE;
. \. [! I- \1 Y/ \, ~5 T - txHeader.BitRateSwitch = FDCAN_BRS_OFF;
9 f( m$ S8 W0 b/ q- j - txHeader.FDFormat = FDCAN_FD_CAN;
& E9 p9 w( [% @3 s - txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;
. X s x. l6 V6 k - txHeader.MessageMarker = 0**U;( v( \( G1 L& [
- if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData2) != HAL_OK)+ @/ `( r' h6 @0 i+ O& X
- {
% j2 G$ K' C$ x: l - Error_Handler();! |! [1 ~5 i. ~
- }
% U/ a4 N( F W! Q) p -
/ N; U, ?. T& M6 l8 z' A - /* Get tick */$ E7 ^' _% F2 i- _! g" B' I
- uint32_t tickstart = HAL_GetTick();
* { ^, A: ~3 T9 K) T - . L8 [) K' N* Y% q2 i# c% D5 Y5 Z! G
- /* Wait transmission complete */* s, V- i, E( b
- while (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) != NB_RX_FIFO)
' g: g/ @2 @( t* C" z# R8 F - {
; V+ D% b. i8 H; S+ [! g e- n - /* Timeout handling */
, j1 U8 Z2 V3 ?5 z2 \* X; R - if ((HAL_GetTick() - tickstart) > TX_TIMEOUT)/ ?+ ~9 r q: T
- {0 o7 `0 E4 q/ c3 K8 ?: r3 a6 Y
- Error_Handler();7 w6 f9 a# d: P/ `5 d7 c8 ~* p
- }
+ P* }6 A5 i! [# Z+ r - }
6 Y: [- L; O5 U4 z# V# a - % m7 ^9 \9 U7 o3 x1 L& P; @
- /*##-4 Receive messages ###################################################*/
% L) S- A" K& K5 O! j6 T; X8 L - : a" e3 {2 q* k6 b1 y# y7 ~: Y
- /* Check one message is received in Rx FIFO 0 */& H! ^. R& D! O+ c3 }
- if (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0) != 1U)( @; W( _$ H4 b; Q8 k: c- p- j/ D" w
- {0 f5 R* e/ P4 A7 R( ^
- Error_Handler();
4 g. K- _: Z6 z5 Z9 Z+ |( f9 z! V. L# } - }5 h( G: o1 P- ]% T6 ?! Z$ N4 w
- ^- u1 V6 V$ U# H- /* Retrieve message from Rx FIFO 0 */$ u2 }5 M3 Y4 p3 G3 s; k
- if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &rxHeader, rxData) != HAL_OK)
- a$ _# D w5 f" @$ c8 y - {8 M* L L# g. G' i
- Error_Handler();
9 g3 R, P E/ `) \8 Z! h - }
% V0 R4 w4 M' w. e* I( \- E6 F9 U - $ J; g, Q/ ?+ c5 n
- /* Compare received RX message to expected data */" Y( `* A0 `7 P* Z
- if ((rxHeader.Identifier != 0x444) ||
; c7 {1 W* U6 S8 J) p. ~ - (rxHeader.IdType != FDCAN_STANDARD_ID) ||8 O$ \8 A' b* T1 m
- (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||
8 U4 L! R) ?9 i - (BufferCmp8b(txData0, rxData, COUNTOF(rxData)) != 0U)). J8 ]. n7 [# R( l+ }0 P. A8 {
- {' V: w2 o3 q' g ?
- Error_Handler();8 o! c* {4 d$ F* ^# Y+ R
- }, k8 ~! w% A, C/ z
- HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);& S7 t9 [2 q; \ F# f
- /* Check two messages are received in Rx FIFO 1 */
* p9 s. O; G0 R0 O1 G - if (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO1) != 2U)
7 n) I5 X- Q4 N0 ]# Y1 _9 c: o5 e( Q - {1 M6 s4 a, z+ E5 ?& l7 b0 P
- Error_Handler();) ~8 G( B" Q& `
- }
% N9 A9 P5 B! X7 }, V9 O9 | - 9 [/ t' V2 _* j2 U* M: V
- /* Retrieve message from Rx FIFO 1 */
# ^3 A) Q- ^7 b1 ?- n- m7 K% q - if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO1, &rxHeader, rxData) != HAL_OK)
6 [( u& G4 I1 \& _ - {
. e3 T4 w5 H& `& ~ - Error_Handler();
( W y+ g ?% W6 t* ^+ L - }
: a, P/ a! L3 X" V B+ u1 W- A
4 m+ `) o! y% ~- /* Compare received RX message to expected data */7 y) z7 _9 ^( h _
- if ((rxHeader.Identifier != 0x1111112) ||
8 }: K. I! n* H# z- z - (rxHeader.IdType != FDCAN_EXTENDED_ID) ||; \# p$ \+ O5 t, M8 ~0 _
- (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||
" r: K& P& b4 `0 R; b, t: d - (BufferCmp8b(txData1, rxData, COUNTOF(rxData)) != 0U))7 \1 n* ]% |' K! m" H
- {: D( X) f2 C2 l# R1 ^
- Error_Handler();
- }: q/ C+ f- S; S4 J, U - }
9 B2 R0 N# S$ U - HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);! \- Z# E" S8 ]( @4 `! |$ M
- c; s3 ~1 r" V2 w- /* Retrieve next message from Rx FIFO 1 */
a/ `: m+ X% e! ?7 J3 A - if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO1, &rxHeader, rxData) != HAL_OK)& H( w9 S# q1 y* E) s
- {
" d+ K* Q- `* j. K4 T9 B - Error_Handler();# B( q G% {& g" y. ]6 {7 m
- }
/ @' m) P% n4 _ - 2 i3 `: l6 ~8 t
- /* Compare received RX message to expected data */
8 m2 F% r1 C9 v/ n - if ((rxHeader.Identifier != 0x1111113) ||' z, { @# ?: z$ m1 c
- (rxHeader.IdType != FDCAN_EXTENDED_ID) ||+ Y# M0 A$ q6 C4 E4 J2 ^
- (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||
L7 H9 x8 }- y0 @1 } - (BufferCmp8b(txData2, rxData, COUNTOF(rxData)) != 0U))
2 q2 `7 H9 {# B8 p$ G9 J \ - {; ^1 T' x" k+ v4 S& @9 t
- Error_Handler();
& p* i, y1 x3 D) g3 z2 f9 y - }
5 h( {& J: G" D3 I4 U+ Z - HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);
复制代码 最后在通过在FIFO中查询的数据进行打印,与发送的数据一致:, v7 R, I5 M( `, O
1 P, Q( l- H' T- | $ c# J' t5 e. i$ v
这里多了一个C8,应该是串口出现的问题。
$ x8 T% W. G* G/ j$ x
# R0 F1 z4 Y; T7 q6 I; Q6 O1 B; s; K& G
* u6 ?% E+ Q- L# n3 ~- m
7 k/ M2 v3 D$ x3 e6 I/ a' y$ h c. E5 ~ [% k
1 ^. Q1 R9 L+ n+ C3 N4 S( M- Q0 `6 t
" [9 d# h ?( H/ ^; G
( T$ z: T. p T! V
. N, l8 \7 {% b4 N: |, H+ p! T; m4 _5 s
7 C$ R* ~' x/ T" Y' u9 A) }: F: V6 f$ v3 x) ~
|