控制器局域网总线(CAN,Controller Area Network)作为一种专为实时应用设计的串行通讯协议总线,在工业及自动化领域占据着举足轻重的地位。它凭借简单的双绞线即可实现信号的高效传输,凭借出色的性能与广泛的适用性,已然成为世界上应用最为普遍的现场总线之一。实际上在我的应用场景并没有实际使用过这种借口,接下来我们也是主要学习一下STM32这方面的的具体使用方法。
$ I' ~1 E: {$ g' [ t& p R5 ? 本次测试的C092是支持FDCAN的,CAN FD(CAN with Flexible Data rate),即“可变速率的 CAN”,可视为传统 CAN 协议的升级版。值得注意的是,此次升级主要聚焦于协议层面,物理层并未发生改变。CAN 与 CAN FD 在多个关键方面存在显著差异:# l% a% N" H6 L3 H1 {4 H! U
传输速率不同:传统 CAN 的传输速率相对固定,而 CAN FD 引入了可变速率机制,能够根据实际需求灵活调整传输速率。在数据量较大或对实时性要求较高的场景下,CAN FD 可以提高传输速率,从而更快地完成数据传输任务;) b9 B5 q. C. `' L8 o
数据域长度不同:传统 CAN 的数据域长度有限,限制了每次传输的数据量。而 CAN FD 扩展了数据域的长度,使得在一次通信中可以传输更多的数据,减少了通信次数,提高了通信效率;
( D6 F4 x1 {) C4 c 帧格式不同:为了适应可变速率和数据域长度的变化,CAN FD 对帧格式进行了相应的优化和改进。新的帧格式能够更好地支持可变速率传输和大数据量传输,同时保持了与传统 CAN 协议的兼容性;
3 K2 b9 m, `: S ID 长度不同:CAN FD 不仅在数据域长度上进行了扩展,还对标识符(ID)的长度进行了调整。更长的 ID 长度提供了更多的寻址空间,使得系统能够支持更多的节点,进一步增强了系统的扩展性和灵活性。7 k2 R6 F' m Q6 H, h, ^6 I2 X0 {
我们看一下班次开发板的原理图:& D# e) z& N# M7 {8 F
% `5 H6 B, U" Z. s0 `
" r/ c: ^4 k5 f& y; \! G
实物接口如下:
# [, t) R( A0 i9 x1 s1 l# T; t
7 a- d* F/ s2 t' Q( O3 { ! m' _% i" B5 H) u+ f6 H
可以看到就在常用的USB接口旁边,也就是说基本上对于C0系列来说,USB和FDCAN只能二选一了。
5 D/ f! I% O* q 那么如何实现FDCAN的配置呢?
1 l% L7 \3 U8 c% n& v: R7 Y 先看一下时钟配置,因为C092支持最大的是48MHz,到FDCAN最大也是48MHz:) q: e/ s' F6 A5 j8 F( @! U
6 v3 {0 h4 R: C8 g; X$ _ 启动FDCAN:' i! A" y4 ^% w; I
' W! f& `1 E( h% K3 \
* S- q: E. U; n6 B; g
( Y7 W9 n0 ?3 U2 J0 X" O 这里没有过多的模式和配置,直接启用就好;
( j5 a6 [. W6 a; c 注意修改默认引脚:
' P+ Z- [1 i2 S5 s! f6 ~( ~1 c6 o
4 i0 i. c6 r K' a( n# r5 O/ v
0 B; o7 t2 ?4 c5 n 下面就是具体参数的设置,我们本次主要进行一下回环测试,所以要进行如下配置:
# T! f8 w( H$ x9 N3 O F. x' ` 基本参数
, m; g3 s B0 Q6 c3 D& G% m | Clock Divider
7 K" w; A# J1 O! u# V | 时钟分频
6 [, _& l3 ~4 h8 N3 Y | | Frame Format+ |: \% y/ X$ H+ y$ x) C
| CANFD模式" ^! _1 v* q4 u% Y, S6 z
| | Mode% \5 p i9 y; p! u$ S4 [, B: R
| 正常工作模式
' e+ `+ M8 T' m# Q6 j+ u( _! n! x/ S | | Auto Retransmission; I2 V4 h- J6 ?) x
| 自动重传
6 ~+ d5 R, r* ]/ D0 D | | Transmit Pause
! e: q5 p; C8 v | 传输暂停1 o. N; R( X/ K P; h. S
| | Protocol Exception. z& b( y" C, n" b2 A( G3 ^# I2 b
| 协议异常处理 R6 r3 }. L' @. ]" x7 ?& c! q
| | Nominal Sync Jump Width& c1 P# Q C' X% ?) F# w) d
| 裁决段同步跳转段宽度+ x5 y0 H' }6 x' _
| | Data Prescaler1 ~1 R8 D* K. `: \1 @: O4 D
| 数据段分频系数
; \1 A; D. M, d- p | | Data Sync Jump Width0 D! ^3 y8 E/ r0 a" Z- B/ }
| 数据段同步跳转段宽度
% b' S( r! |# Y2 a9 d | | Data Time Seg1
& ^3 p6 @$ Z7 d6 @8 u/ ]# J8 I | 数据段时间段10 r: T/ Q' M4 D4 b- ^
| | Data Time Seg2
- `( x" g# b' u; o0 K | 数据段时间段2. Q: ]1 G9 b. G- W
| | Std Filters Nbr
/ r- T k% S+ k* Q7 J5 s* V | 标准滤波器数量
! p' r/ U6 V& z* N$ y ~/ c | | Ext Filters Nbr
. m7 U5 h9 W8 \+ A/ G) x; Z7 f | 拓展滤波器数量6 }) m) u" K0 }- |! ?
| | Tx Fifo Queue Mode* Q- k: M5 H4 r
| 发送模式$ a; r" ?0 ^2 p' j0 s$ p0 W
| | 我们按照例程中的配置参数进行一下配置:2 l* l: s& [0 v# e/ p! q, @
4 t3 N2 e0 v+ r* g0 w, L1 s
! }; q/ Z2 g2 k- A 接下来进行比特率的配置,如下图:
, _: P9 K5 O5 R
" a, |3 C0 ] C* R5 H% N ) h$ _6 l F3 J
上图的选项最小数值是1,比特率=CAN时钟/时钟分频/预分频/(Seg1+Seg2+1),所以就算是最小配置参数,得到的最大比特率是16M,不过我们还是不要设置那么大,就和SPI的配置一样,太大不一定通信稳定。
9 V. |' Z. T+ I9 c& Q 依然是回环测试,我们将CAN接头短接:
8 t: g% O# n# b! G4 Z
( h/ L( Z/ ~. ~/ m z: N1 M4 f
8 B3 q0 H( e* m" ~* \5 u9 M9 E 这里我们主要配置过滤ID,前面各开启了一个标准滤波器和一个扩展滤波器:8 c) j+ [9 ~0 X
- /* Configure standard ID reception filter to Rx FIFO 0. Only accept ID = FilterID1 */0 j3 h8 I+ @7 k2 G! C8 U. S! Z
- FDCAN_FilterTypeDef sFilterConfig;
* J: w5 C% O) ^% h - sFilterConfig.IdType = FDCAN_STANDARD_ID;0 |$ ~# l# ^( Q" n. N# U' J
- sFilterConfig.FilterIndex = 0U;2 k" T% X0 I; P+ I0 p3 G
- sFilterConfig.FilterType = FDCAN_FILTER_DUAL;+ q( F6 c( a+ F! \) `1 [
- sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;" |* N5 z- g' g
- sFilterConfig.FilterID1 = 0x444;- P0 b1 d) V7 M. L$ R0 J
- sFilterConfig.FilterID2 = 0x444; /* For acceptance, MessageID and FilterID1 must match exactly */
8 { C7 P( O/ m$ d - if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)7 a" ~" k! Y. E3 ^5 S
- {
0 Y' \3 r$ G: j - Error_Handler();& P1 V' A T" @( t. f5 p+ A5 U
- }
. @4 u# G# S3 M4 [8 P8 R! i
3 y g- o" ?* s1 F U- /* Configure extended ID reception filter to Rx FIFO 1. Only accept ID between FilterID1 and FilterID2. */
3 b( y3 r u, i3 s) c - sFilterConfig.IdType = FDCAN_EXTENDED_ID;
! @9 J8 |6 [. b1 E6 ~ I - sFilterConfig.FilterIndex = 0U;1 K& d. U& L* f8 h* s2 `
- sFilterConfig.FilterType = FDCAN_FILTER_RANGE_NO_EIDM;
: O7 |% Q, r. ^; ] - sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO1;
: X( C: o4 N7 ~! A2 H$ y9 ?/ w - sFilterConfig.FilterID1 = 0x1111111;
. T8 q) J* x, r9 e& w' n8 i - sFilterConfig.FilterID2 = 0x2222222;
# e! j2 h' f8 P) v - if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK) P7 s6 R4 Y9 N+ v7 C( X; K# Z
- {
7 f% M/ Q* B, q# ^& u- y& E' p! \ - Error_Handler();& V0 y% L% F3 k- B( n+ i
- }
复制代码 然后是进行数据的发送和接受,这里都是通过FIFO的模式进行缓存的,并在接收节点进行接收数据打印:1 k, t; Z6 ^& x) a
- FDCAN_TxHeaderTypeDef txHeader;+ `7 ?* `$ U0 f$ J
- ) r, t# h; d i O6 q+ c! v
- /* Add message to Tx FIFO */) Z0 x( w' z) E0 W/ ^ C
- txHeader.Identifier = 0x444;: l6 h$ @: E# B
- txHeader.IdType = FDCAN_STANDARD_ID;
! O( ] A7 m' i& A$ K8 Q+ } - txHeader.TxFrameType = FDCAN_DATA_FRAME;
: R& O p8 y- h+ j8 V - txHeader.DataLength = FDCAN_DLC_BYTES_12;
) K9 f0 R/ F6 U, [ - txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
% m8 @, \. z. w# Q. Q7 B6 V - txHeader.BitRateSwitch = FDCAN_BRS_ON;& v9 t$ l2 H8 B- a# a, l4 M, }) O; z
- txHeader.FDFormat = FDCAN_FD_CAN;
9 A! t" n* f. C3 ]) `5 R( W - txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;
' w5 E# j# z( t- F3 U. V Q/ k - txHeader.MessageMarker = 0x52U;
0 `& r8 v% ~8 E - if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData0) != HAL_OK)' I8 ?% T* z1 X5 {
- {' q6 J: {% C3 i( R' ]* N
- Error_Handler();+ k, w) | ]* S/ _* x4 j
- }9 x' m$ n1 c; E. k- n
- 2 o$ y+ ?. q0 v6 J% w4 q
- /* Add second message to Tx FIFO */6 b v* i8 F: j) ?: i* ?. n$ N# k% c
- txHeader.Identifier = 0x1111112;
7 a1 X3 ~. p0 u3 S# A/ u - txHeader.IdType = FDCAN_EXTENDED_ID;0 b) k& }& k- Z; N6 s& c$ z* b
- txHeader.TxFrameType = FDCAN_DATA_FRAME;
' q; j. k6 f) f* v/ e3 h4 W J - txHeader.DataLength = FDCAN_DLC_BYTES_12;; U$ j$ x! r$ o
- txHeader.ErrorStateIndicator = FDCAN_ESI_PASSIVE;
( k0 \2 O! B X/ _0 v, M2 V* { - txHeader.BitRateSwitch = FDCAN_BRS_ON;
4 P. C# A5 [1 h* W7 _6 X, g! N( m - txHeader.FDFormat = FDCAN_FD_CAN;
. h5 j0 k9 l+ X - txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;
* ~# b) y% w9 U - txHeader.MessageMarker = 0xCCU;8 @) k8 w& \- B- C: Y
- if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData1) != HAL_OK)6 d. N7 H! d9 s
- {
0 \, p) |% `6 u" p - Error_Handler(); h/ Q- Z# r/ P; c( [
- }5 _5 {+ z* _! I# Z
- ! g* U9 o/ b8 U3 I
- /* Add third message to Tx FIFO */
5 c: W9 N; G( g- p, Q1 } - txHeader.Identifier = 0x1111113;
) f( z3 F# d( z4 f, l- ?1 j1 l2 ` - txHeader.IdType = FDCAN_EXTENDED_ID;
6 Y3 ^4 k e1 I4 S) w7 ]. n! F# e4 g - txHeader.TxFrameType = FDCAN_DATA_FRAME;* D U: }- g6 N2 d' B
- txHeader.DataLength = FDCAN_DLC_BYTES_12;0 D: l' D% M4 U% J( w9 h( V
- txHeader.ErrorStateIndicator = FDCAN_ESI_PASSIVE;. ^! y7 g3 y3 _9 s0 }! |& Z
- txHeader.BitRateSwitch = FDCAN_BRS_OFF;
9 _) X; L+ w6 G- `% j% P - txHeader.FDFormat = FDCAN_FD_CAN;
$ h& A) [7 @5 A O4 Y6 g - txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;
7 T& |# K, Y$ r" u6 p - txHeader.MessageMarker = 0**U;
1 V v9 b$ t; x. z7 v - if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData2) != HAL_OK)
; F5 v+ W: D2 G5 t) P2 j - {
( B# ~" v& j0 d P% L4 q - Error_Handler();3 L* `. P( j# O1 g8 t! x; D
- }" g- o5 Y9 x2 P" g
- ; E4 U) c$ I" J$ ?
- /* Get tick */# D* @- |! i# o3 k5 G+ @) i
- uint32_t tickstart = HAL_GetTick();2 k2 f0 \1 ?3 a( r' W0 R
: t, B6 j, u3 q2 H% k8 u/ a- /* Wait transmission complete */
" l* H8 D% ]; H3 u - while (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) != NB_RX_FIFO)0 {7 x, x- e- u ?# W* i0 R4 l
- { |% g6 R2 O0 L* K ?; Z/ g& \7 J
- /* Timeout handling */
! e" o) p. k) t" t. B ` - if ((HAL_GetTick() - tickstart) > TX_TIMEOUT)
Q( C- ?: F: A4 V& U! F - {
7 H2 ]9 i# C4 \( a - Error_Handler();4 ~3 x; c9 g1 ]9 B3 b! y
- }
( f* j6 M& u& ^ - }1 z3 a; s" @8 p+ @3 Q+ S4 [+ m
-
* G: ^1 S9 Z1 {! p - /*##-4 Receive messages ###################################################*/ l) ~2 h# [6 A+ Q" U; K
& m5 v/ ]$ F9 O- ^2 W- /* Check one message is received in Rx FIFO 0 */
; z; ~! w! d0 {! }7 _ t) N4 m! T - if (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0) != 1U)
& a# O. h9 y5 [' p - {; O9 Z& O/ R- q/ D! Q
- Error_Handler();5 t0 Y% w3 a3 |5 T6 z
- }
0 N0 ~4 |4 I5 _# ` - . K& l8 Y0 Z9 V. C) B3 B/ a
- /* Retrieve message from Rx FIFO 0 */
5 O) e. @- L" z( c2 U) p - if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &rxHeader, rxData) != HAL_OK)0 v* `+ x$ K* g8 |. \
- {
& A7 f6 Y: H# j5 V# T- K6 j - Error_Handler();; r+ ^" z4 ]" o
- }
# r, T5 I7 s$ J s I/ O$ S - 6 t5 b- J3 O0 E. L* }. b4 b3 [! z
- /* Compare received RX message to expected data */( D" @) k! D$ |7 C: r- H
- if ((rxHeader.Identifier != 0x444) ||2 k, t% d7 r1 l. r7 j3 A
- (rxHeader.IdType != FDCAN_STANDARD_ID) ||9 U9 B T F) C* v( n8 ]
- (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||" b% g: o! z. l) I* e
- (BufferCmp8b(txData0, rxData, COUNTOF(rxData)) != 0U))$ u9 T" {/ ], ~% w4 Q# C' ]
- {& p2 Q, g+ o/ F* }2 O' r
- Error_Handler();' l8 I+ M% q$ H' I$ Y& q
- }
# Q4 S% D' p0 y3 D - HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);
/ w7 A# U2 d+ N, W, O2 T1 u - /* Check two messages are received in Rx FIFO 1 */; G6 o5 p0 v& _% b
- if (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO1) != 2U)
1 M0 v0 \0 ?4 r8 @& C- F - {4 O0 V. R# V) w+ {- l. G
- Error_Handler();4 q- P7 W: B* ^# y
- }
* A6 h9 P) `- L' G9 K3 N. u
5 I8 y. a: H; g# j/ Z9 x- d3 D# N- /* Retrieve message from Rx FIFO 1 */
0 _) p. K7 |4 R$ _( S - if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO1, &rxHeader, rxData) != HAL_OK)
/ ~2 I4 ^" F( o. l+ ^ - {' W5 @/ R% R( Z1 H; A' f/ q
- Error_Handler();
, q) I5 S5 |0 h$ y0 [% q - }2 W7 T# ^9 b# L' {
- ) s& t( S8 {( {9 v
- /* Compare received RX message to expected data */
( j9 }( [6 i* U$ w! {3 g" ] - if ((rxHeader.Identifier != 0x1111112) ||
, l2 f" t5 z0 E - (rxHeader.IdType != FDCAN_EXTENDED_ID) ||7 x+ b$ ^# W5 K* M
- (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||3 x7 |2 \3 I' X1 C" j
- (BufferCmp8b(txData1, rxData, COUNTOF(rxData)) != 0U))) _6 l. S/ l' x z' k S9 O
- {
) K; d5 Z3 ^" r% W- X* ^ - Error_Handler();
u3 M* ~2 D! x( ^- o! | - }
, ` l( d- P/ Q: g9 G, B) h" z) X - HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);, n* w; Q& p0 c; c: _
2 P( g1 {3 j; Q- /* Retrieve next message from Rx FIFO 1 */
- U+ G' _( n; x$ B! R3 X8 d0 R: U7 B - if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO1, &rxHeader, rxData) != HAL_OK)
9 m0 H, R! w9 L! v$ c$ a, @; }/ ?' k - {& |$ V; ]6 P; q
- Error_Handler();
* ]# V/ Z. I f' d9 [1 V" J; g - }
9 k9 x# H. r" X3 i% c - 2 a& A& t* j7 }1 Q6 L2 P+ {
- /* Compare received RX message to expected data */
( C* {' |9 X+ [( d - if ((rxHeader.Identifier != 0x1111113) ||
- p! c) }1 t0 E/ [9 `2 {, O - (rxHeader.IdType != FDCAN_EXTENDED_ID) ||$ M! |( u9 ]4 {9 \ `9 l
- (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||
: s4 L+ t) X4 F% k8 {8 a4 f - (BufferCmp8b(txData2, rxData, COUNTOF(rxData)) != 0U))
3 j8 A6 R6 Z( |2 A. S - {
) g, H9 ]1 {5 z& H1 i5 c3 h - Error_Handler();
# {; M6 l, U/ i( A) [! O1 w0 F/ | - }
9 p5 n# U* C: u& U/ R1 D - HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);
复制代码 最后在通过在FIFO中查询的数据进行打印,与发送的数据一致:5 s# s6 _$ @ f8 L1 [
; T$ J4 L: m7 k! |
; u z" {4 i0 p8 q0 j 这里多了一个C8,应该是串口出现的问题。! G% B/ n+ M: Z
- D0 T, z6 ~$ u$ V, D7 W# I0 ^
; f T- E6 _1 y6 V g3 J% P
: i& m8 Z& y- a( h: s+ H6 V, n; m
( w c0 w, T' l1 O" m& f
8 C& H: d$ i0 }( k
! h+ p+ n$ l$ F/ M
4 @. Y9 s3 F8 e1 a5 e
/ Y* n7 v6 d$ h. a5 ~& r: R0 N- T( ~2 q% F2 j, `1 L9 x/ J. e" R
4 b; D5 s4 y! J! @8 Y7 q( _" Z$ j3 M p j# h2 H* c* y
) X3 L5 d \/ O! O9 a7 e/ Y
" ?0 ~, k9 r# l3 N |