控制器局域网总线(CAN,Controller Area Network)作为一种专为实时应用设计的串行通讯协议总线,在工业及自动化领域占据着举足轻重的地位。它凭借简单的双绞线即可实现信号的高效传输,凭借出色的性能与广泛的适用性,已然成为世界上应用最为普遍的现场总线之一。实际上在我的应用场景并没有实际使用过这种借口,接下来我们也是主要学习一下STM32这方面的的具体使用方法。
5 `' X( ^; O% n$ I& X4 V 本次测试的C092是支持FDCAN的,CAN FD(CAN with Flexible Data rate),即“可变速率的 CAN”,可视为传统 CAN 协议的升级版。值得注意的是,此次升级主要聚焦于协议层面,物理层并未发生改变。CAN 与 CAN FD 在多个关键方面存在显著差异: p9 l- z, V( U8 E3 C1 X' t
传输速率不同:传统 CAN 的传输速率相对固定,而 CAN FD 引入了可变速率机制,能够根据实际需求灵活调整传输速率。在数据量较大或对实时性要求较高的场景下,CAN FD 可以提高传输速率,从而更快地完成数据传输任务;
% l- ]/ _' I G( }( i: P9 ?8 n. l 数据域长度不同:传统 CAN 的数据域长度有限,限制了每次传输的数据量。而 CAN FD 扩展了数据域的长度,使得在一次通信中可以传输更多的数据,减少了通信次数,提高了通信效率;
" B. y7 k& o/ S( j& @. @$ C 帧格式不同:为了适应可变速率和数据域长度的变化,CAN FD 对帧格式进行了相应的优化和改进。新的帧格式能够更好地支持可变速率传输和大数据量传输,同时保持了与传统 CAN 协议的兼容性;
- u( F N, O ` h ^% b ID 长度不同:CAN FD 不仅在数据域长度上进行了扩展,还对标识符(ID)的长度进行了调整。更长的 ID 长度提供了更多的寻址空间,使得系统能够支持更多的节点,进一步增强了系统的扩展性和灵活性。
, r) O1 Z0 d2 j( _- U8 ]6 G2 R4 m 我们看一下班次开发板的原理图:
3 m% l& h$ e7 L& ?4 z
6 |! w% ]" ~. T
" _" P) n6 i% Q0 ~. \& j 实物接口如下:) Z& l1 |) i# L# f* T% d- H
( ?% e+ [! O4 _2 a* H
' H$ ]$ B3 g/ ?- K/ R8 w 可以看到就在常用的USB接口旁边,也就是说基本上对于C0系列来说,USB和FDCAN只能二选一了。6 @2 C0 a3 w% Z8 G
那么如何实现FDCAN的配置呢?
5 h7 h: b4 ]0 m; C: e" |5 N 先看一下时钟配置,因为C092支持最大的是48MHz,到FDCAN最大也是48MHz:+ X' _! F* q% r2 E6 b5 B L
; R9 D1 o5 ~2 G; M& i, r
启动FDCAN:5 `# D, \3 W# @; f
# O4 H$ M3 U' h& O" {
( |8 C! Y9 n3 _$ \1 s) F* M ~
+ \; S$ `3 n% P4 n) Y2 ?! x+ H 这里没有过多的模式和配置,直接启用就好;1 _' g7 K6 ~" t. P
注意修改默认引脚:0 v, F! k0 W9 [' q/ i
" ^6 I; O' N* x
0 Q! F. ?. e% F
下面就是具体参数的设置,我们本次主要进行一下回环测试,所以要进行如下配置:
; a3 W2 L7 u+ G6 H2 ]/ `$ _ 基本参数, i, C2 T9 Y; `0 ?) j6 R/ @
| Clock Divider
. c- u; }* t: I. B: B7 P- s0 i: n | 时钟分频
: a2 G2 m I# l5 c+ a: ? | | Frame Format0 V* X2 F# q! e* n! Y( ]* _0 Q% A
| CANFD模式7 o5 r% w. F, C$ Z
| | Mode
$ P6 m- M7 Z9 |3 P6 p9 V | 正常工作模式: S1 l* d' A' R/ B8 V! Q
| | Auto Retransmission6 |+ Z6 e5 r. f: R
| 自动重传
$ H- T+ @% Q8 Q2 u; t% \; I | | Transmit Pause
Q/ B; j5 C0 [* U$ ` | 传输暂停6 N7 F, H) b. M! S6 @/ ^7 U
| | Protocol Exception; Z% ?$ ]2 w, \& m% k, r. \
| 协议异常处理
1 `, ^3 T: i9 c5 L | | Nominal Sync Jump Width4 l9 C; D, y. D. @* k9 ]
| 裁决段同步跳转段宽度. Z3 _+ V% F Q- L0 V. t
| | Data Prescaler
9 h% W8 Z5 n/ i | 数据段分频系数
# @9 e: d7 @. {8 v | | Data Sync Jump Width8 p1 v. V# t- G/ z3 s! s# O
| 数据段同步跳转段宽度
F% p; O7 X9 w! b. d( @ | | Data Time Seg1
7 f/ ^, D1 v' P6 F# j | 数据段时间段15 P& y/ o7 m: }2 N
| | Data Time Seg2
! T* q8 c, [. @ | 数据段时间段2
7 M% X+ ~6 n# l9 |- P, f' F | | Std Filters Nbr
( a$ t8 k" q) a4 X" a | 标准滤波器数量$ ~- D+ h/ w2 y# m4 D i
| | Ext Filters Nbr
3 j! N+ t% ^: I% ~, Y7 o3 c" B/ K | 拓展滤波器数量
7 w% ]: v! D' V+ W j | | Tx Fifo Queue Mode
# J% u8 w0 I. s0 f1 t8 y: t8 C& C | 发送模式. D# X% ^ h0 [
| | 我们按照例程中的配置参数进行一下配置:( P! A; g! M7 Z
1 E0 F) j+ C* J# s7 v o7 E - Y- ~" M& I% V
接下来进行比特率的配置,如下图:
8 t' J% R! e& x" }' z& O# d' O
! O1 ]' L) j% n3 ? |) t; e8 E
1 U9 R" }; L- T 上图的选项最小数值是1,比特率=CAN时钟/时钟分频/预分频/(Seg1+Seg2+1),所以就算是最小配置参数,得到的最大比特率是16M,不过我们还是不要设置那么大,就和SPI的配置一样,太大不一定通信稳定。
; c1 [' C) Y% |( ^ 依然是回环测试,我们将CAN接头短接:
' k" e2 U; p* C L ]
8 ^& K6 I( @" s+ v: x
5 P, C/ u6 h1 r 这里我们主要配置过滤ID,前面各开启了一个标准滤波器和一个扩展滤波器:
- K8 O, S' F+ s- E( P3 B; ~5 o m- /* Configure standard ID reception filter to Rx FIFO 0. Only accept ID = FilterID1 */
) a" F6 W7 Q1 d8 s) Y" ?( J - FDCAN_FilterTypeDef sFilterConfig;
- V/ Z' q, ?' r; K. I: V - sFilterConfig.IdType = FDCAN_STANDARD_ID;6 G8 V1 E1 G' U
- sFilterConfig.FilterIndex = 0U;) I. m. n G" s4 `7 o
- sFilterConfig.FilterType = FDCAN_FILTER_DUAL;
N% s4 z! v' O; a0 |# D7 d - sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
! k- j# x. n# k6 p6 I: T - sFilterConfig.FilterID1 = 0x444;
n9 g" m) v) o+ M4 D/ i3 H- F - sFilterConfig.FilterID2 = 0x444; /* For acceptance, MessageID and FilterID1 must match exactly */4 I# X( b% u! _; `
- if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)+ c! S3 l* a4 R# ~
- {( ^9 |' c( l2 o
- Error_Handler();0 D$ u7 F1 L. y( p, e
- }
* E9 {4 Y* ^4 t, D$ y, Z - ) D$ a6 F8 n+ D9 Z" x- G
- /* Configure extended ID reception filter to Rx FIFO 1. Only accept ID between FilterID1 and FilterID2. */
, {8 W7 A) v2 F) V/ D - sFilterConfig.IdType = FDCAN_EXTENDED_ID;
, E& A! [/ o$ P% r( c. `$ ~ - sFilterConfig.FilterIndex = 0U;
1 q7 g9 s" q/ s4 {6 L. Q1 B+ g' w2 j - sFilterConfig.FilterType = FDCAN_FILTER_RANGE_NO_EIDM;
! W" m: q" a9 ^' Q& O3 u6 g - sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO1;
1 k; k/ |( F8 g& B - sFilterConfig.FilterID1 = 0x1111111;
* g) C. a Y) w - sFilterConfig.FilterID2 = 0x2222222;+ X- A% x8 g# T5 }
- if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
/ g* N& n: ~# y4 [ - {
) L( r" p0 i2 n# Q7 ^: g: p* ` - Error_Handler();& V* s( p o8 {* G! A( O4 M) \ j
- }
复制代码 然后是进行数据的发送和接受,这里都是通过FIFO的模式进行缓存的,并在接收节点进行接收数据打印:
* h, q* M/ Y# e$ |- FDCAN_TxHeaderTypeDef txHeader;
( D( Y2 {: _5 q; l9 z
) N- N6 R1 E: p; X- /* Add message to Tx FIFO */3 p8 ^6 X1 p$ d
- txHeader.Identifier = 0x444;
2 R2 B: k/ }; k( w - txHeader.IdType = FDCAN_STANDARD_ID;3 _6 ?) l3 p% A: b* L! _
- txHeader.TxFrameType = FDCAN_DATA_FRAME;, N8 y, M3 J c) C8 u" ]
- txHeader.DataLength = FDCAN_DLC_BYTES_12;
& A+ x j" i8 A7 ` - txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;- `: l- n9 ]# Z4 ]# M& T1 ~
- txHeader.BitRateSwitch = FDCAN_BRS_ON;! {) U$ X7 h( E' `! ?
- txHeader.FDFormat = FDCAN_FD_CAN;
6 S9 n( T/ ] s& U( E$ u- t/ m0 ^9 E - txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;: I7 o. B8 U9 Z, L9 R% W- v- @3 X
- txHeader.MessageMarker = 0x52U;
& V+ G9 n. R: p' A: V5 L/ P& r3 ~ - if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData0) != HAL_OK)
0 E& x. ^% G T6 E2 r+ n% Y! Q4 E9 i - {2 w" C8 C2 y3 n% [, _
- Error_Handler(); ^2 }* Y0 e& D& U; }
- }( e' I/ ]2 z' d& m4 X! B9 X. y
- ( i, B- s# w4 w* A* q
- /* Add second message to Tx FIFO */
( g) V6 h) j: |( X) ~' q - txHeader.Identifier = 0x1111112;) v5 G" ?! `3 N) N V9 n: }7 w/ k; e
- txHeader.IdType = FDCAN_EXTENDED_ID;
- E) H! J* t+ y, L" t - txHeader.TxFrameType = FDCAN_DATA_FRAME;
) Q% e, r3 Q) Q: E# g# ? - txHeader.DataLength = FDCAN_DLC_BYTES_12;
* G5 ?( ]: A5 B) A# Y1 ?$ p" ?1 K. { - txHeader.ErrorStateIndicator = FDCAN_ESI_PASSIVE;1 q7 c- h5 x8 z* U( I5 O& G1 H
- txHeader.BitRateSwitch = FDCAN_BRS_ON;
& J0 q$ n8 G4 R - txHeader.FDFormat = FDCAN_FD_CAN; U. a- t1 o6 G o" M s) J2 ?
- txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;6 X7 Q: n B+ D, N+ v* J$ H* s
- txHeader.MessageMarker = 0xCCU;. ?; W9 \. U% n$ \% s1 x, g# m2 [, {) o5 D
- if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData1) != HAL_OK)
! X5 N, V9 L+ ]9 ?! w- B, R( p - {, `% V" u t; T
- Error_Handler();
# b. h$ I) `9 C2 O/ T) h- A - }
# u6 }8 ^4 X+ n6 U2 ?3 K) W
6 F" w, A' d& |3 v6 v- /* Add third message to Tx FIFO */+ d- u& l5 R- l q% x- E: `) l6 O% ^
- txHeader.Identifier = 0x1111113;; R. D; J s* x; [$ L4 @
- txHeader.IdType = FDCAN_EXTENDED_ID;
) A6 y" s' O. G - txHeader.TxFrameType = FDCAN_DATA_FRAME;
5 N+ p5 |/ ^. ^5 }& n6 z" Y; \ - txHeader.DataLength = FDCAN_DLC_BYTES_12;
N0 x2 t# f6 Q" }3 [, a - txHeader.ErrorStateIndicator = FDCAN_ESI_PASSIVE;3 Q# g* \" s+ F) z+ D; w+ J' w9 {2 X
- txHeader.BitRateSwitch = FDCAN_BRS_OFF;" ~) t7 a+ r6 i. m- _$ R8 A
- txHeader.FDFormat = FDCAN_FD_CAN;: c' F! R8 B7 U; G- \
- txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;' L- V/ V% |# x* v( f- j
- txHeader.MessageMarker = 0**U;' F9 @. g" X( U% E
- if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData2) != HAL_OK)3 J9 p. M0 T! B& T) Z
- {
0 ` o( d2 h( h' g6 g8 c+ r - Error_Handler();
: \3 o# ^/ t/ B2 {0 s5 u - }) Z+ c% g1 w6 }6 y+ y7 |
- ; @# o9 H3 u; p+ v L# H9 L
- /* Get tick */% x. g% s3 v. @. t f
- uint32_t tickstart = HAL_GetTick();
) B% B2 x0 Q) X& m: u- v - 3 G' j) M4 i2 y1 P( @
- /* Wait transmission complete */
9 ?9 g* x5 G9 {, p' e' V - while (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) != NB_RX_FIFO)
6 c& z! Q* j8 v" U8 q; q3 R/ f - {" O/ g# ]% C8 m. F3 @, ^/ \1 G0 H
- /* Timeout handling */
$ ] T- W) l9 g% [8 f4 D1 h - if ((HAL_GetTick() - tickstart) > TX_TIMEOUT)
# r( J$ ^5 B9 Z' S - {) ]: @4 ^5 `. m% _2 z
- Error_Handler();
9 }3 C+ s# d3 P - }9 N" q' N, \7 l+ Y n0 ?; i* C
- }6 s9 D; y" V* Y8 f# ?. p7 V+ d, n- P
-
% e+ @) H! N$ t1 g& G - /*##-4 Receive messages ###################################################*/
! U) u) M8 ]( h4 e% ?! U! F1 s
# G* f( z; I7 A5 y2 s- /* Check one message is received in Rx FIFO 0 */4 ]# o0 [/ \( e3 s+ u0 f! M A
- if (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0) != 1U)6 V: V; V3 _1 k" y. U V
- {
& ?! H# X# A A4 @ - Error_Handler();5 _. ], o4 v' _8 N; m; I& S% I Z
- }# _) t' ~, K/ p% p/ F& g- q' ?% ?
- 7 J3 B) k- i+ q4 [4 {
- /* Retrieve message from Rx FIFO 0 */
5 d; X9 ?9 e) w g( [2 A/ s - if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &rxHeader, rxData) != HAL_OK)
8 |; v: Q% f0 s, O( c. f! B - {! f; L0 _7 ]9 K. Z2 U
- Error_Handler();
( A2 @3 B) N/ z* t# ?9 U1 I - }
2 ?$ I/ h! F% g6 [9 d0 l - 6 ]1 Y2 m3 p& @
- /* Compare received RX message to expected data */
8 S: K @, d& i, i: c4 ` - if ((rxHeader.Identifier != 0x444) ||
4 Z8 i# F- K, l |0 i; R - (rxHeader.IdType != FDCAN_STANDARD_ID) ||
: z8 o& @1 `* w4 Q* F - (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||( s) |; v' X" X! X/ d
- (BufferCmp8b(txData0, rxData, COUNTOF(rxData)) != 0U)). M* Y! V% b1 C" J g& O7 Y
- {
6 Y7 Y( m( y# a) G8 b$ {6 w - Error_Handler();' t" d2 P. h/ |0 G# N
- }
' G8 V( R5 J2 G+ V s) G - HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);$ s# J1 B" J& q$ d3 ?* H
- /* Check two messages are received in Rx FIFO 1 */, t1 n# y' C0 V$ S: _ N
- if (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO1) != 2U)% i6 B% W) Q) w. U
- {& @* {0 p; D' ~" ~8 [" N( w' o: E
- Error_Handler();
- s# E" B: {3 B7 `( I4 Q3 j - }
3 C) l+ f' G8 t, O' Z - 2 i* K0 `) P$ X' i- t
- /* Retrieve message from Rx FIFO 1 */% b, I( Z* j! c1 P/ w% Q
- if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO1, &rxHeader, rxData) != HAL_OK)3 g( r! A4 _. _; _9 Z
- {! M- _1 D* K" Z* B
- Error_Handler();2 T: O3 K) c6 D: z& m$ Y ~1 p1 ]
- }
v! }/ F* i- f1 K' ]% ~ - ) C. M& r5 \; C- F: x1 J2 Q' R
- /* Compare received RX message to expected data */
* D( A0 f4 `) ]8 s1 k( c/ n3 H - if ((rxHeader.Identifier != 0x1111112) || I! P4 i9 l/ @, o/ a- l
- (rxHeader.IdType != FDCAN_EXTENDED_ID) ||
! H( Y0 {! k2 R7 J - (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||
. N. v# X& z& v) w3 I7 M1 W - (BufferCmp8b(txData1, rxData, COUNTOF(rxData)) != 0U))
5 ]) u6 T6 \& B; F7 p7 A1 g - {
% ]7 |; n* R. Z$ d1 o/ J0 q - Error_Handler();
* ]9 T, ^) n# [2 V5 ~ { - }
- E' A# n& L3 i, J0 L - HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);
9 R5 Z, R2 u5 }% N - / I7 v/ `& c6 ?1 t9 Q' [4 L9 r
- /* Retrieve next message from Rx FIFO 1 */
8 Z' z: X8 O3 H' `( L - if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO1, &rxHeader, rxData) != HAL_OK)
4 W, F) U0 f+ {+ \; m+ A' K - {; F! e& ^3 z2 Z, Q( |" W
- Error_Handler();2 ]* j) B+ U5 \5 x
- }, N- A7 l0 w7 @: f
- S( s# I9 I4 k7 }
- /* Compare received RX message to expected data */
4 B- Y: V/ \; t9 I - if ((rxHeader.Identifier != 0x1111113) ||
/ U& s8 b: A. @5 W( b& f, h( i - (rxHeader.IdType != FDCAN_EXTENDED_ID) ||' b" b1 j- I* c5 C2 `
- (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||
5 G7 R9 E! E" F0 c- B - (BufferCmp8b(txData2, rxData, COUNTOF(rxData)) != 0U))8 W$ D; A" J$ u5 Y3 N
- {- l1 s( Q0 a$ w5 p2 e3 C5 e9 ?4 C) P
- Error_Handler();
T1 j0 [# T. R) g& [) a - }
: I$ W' }( `- t4 ^& G: @0 n9 I - HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);
复制代码 最后在通过在FIFO中查询的数据进行打印,与发送的数据一致:1 N4 ^6 S, @2 h8 M
/ G3 z) b, D- i: t4 \$ Y
( _# W- ^- b1 c; `( n9 X2 Q
这里多了一个C8,应该是串口出现的问题。+ q% ~% T5 N% D0 G/ A( b
9 B" M2 ]0 Z0 N G9 j
! I. i: c6 S# v6 V0 d
8 O- N2 o. K+ y& n, w& S4 p
) X7 s2 ` X- b/ j- f5 t, l* i8 }/ c0 `! P2 b
5 I# r( U0 [& _- t" e" Q& {# L7 p9 j6 C* u! Z% z3 n
# H) f6 y$ [" U. D: {& X
4 O, H/ a$ u2 q7 I( c* o- M
0 B& m1 I) M; x% ~3 `8 A- b
& `9 E! J) ?6 v o: d- `/ r; H
' E: p, q* s4 y# o* |
! u! Z( o( `2 H% k6 r. u |