控制器局域网总线(CAN,Controller Area Network)作为一种专为实时应用设计的串行通讯协议总线,在工业及自动化领域占据着举足轻重的地位。它凭借简单的双绞线即可实现信号的高效传输,凭借出色的性能与广泛的适用性,已然成为世界上应用最为普遍的现场总线之一。实际上在我的应用场景并没有实际使用过这种借口,接下来我们也是主要学习一下STM32这方面的的具体使用方法。
- k1 T) ]3 s9 {0 {- f- j 本次测试的C092是支持FDCAN的,CAN FD(CAN with Flexible Data rate),即“可变速率的 CAN”,可视为传统 CAN 协议的升级版。值得注意的是,此次升级主要聚焦于协议层面,物理层并未发生改变。CAN 与 CAN FD 在多个关键方面存在显著差异:0 H3 b$ T Y& r: ]; o3 I
传输速率不同:传统 CAN 的传输速率相对固定,而 CAN FD 引入了可变速率机制,能够根据实际需求灵活调整传输速率。在数据量较大或对实时性要求较高的场景下,CAN FD 可以提高传输速率,从而更快地完成数据传输任务;
; ~, s! ?' T A7 R+ {. w i& | 数据域长度不同:传统 CAN 的数据域长度有限,限制了每次传输的数据量。而 CAN FD 扩展了数据域的长度,使得在一次通信中可以传输更多的数据,减少了通信次数,提高了通信效率;: \- S/ K- I! M3 z) [2 A* f6 L
帧格式不同:为了适应可变速率和数据域长度的变化,CAN FD 对帧格式进行了相应的优化和改进。新的帧格式能够更好地支持可变速率传输和大数据量传输,同时保持了与传统 CAN 协议的兼容性;* \% |: L. x6 J u7 V5 v
ID 长度不同:CAN FD 不仅在数据域长度上进行了扩展,还对标识符(ID)的长度进行了调整。更长的 ID 长度提供了更多的寻址空间,使得系统能够支持更多的节点,进一步增强了系统的扩展性和灵活性。. A2 r) y8 |: n
我们看一下班次开发板的原理图:
/ M/ w8 C8 P; b, j' Y" K/ c) P
# I* t8 N) w9 A
% Q+ s/ D3 E# | 实物接口如下:
6 T% v7 J4 O5 Y
/ j' [4 K! l9 _% ?( w- l
7 N" G; d8 G4 |8 I2 ~7 s8 s" H 可以看到就在常用的USB接口旁边,也就是说基本上对于C0系列来说,USB和FDCAN只能二选一了。
& W" U5 s8 g8 Q, i; C, A 那么如何实现FDCAN的配置呢?
: T9 ?2 b M: S* R5 J- B: h8 u8 e# ] 先看一下时钟配置,因为C092支持最大的是48MHz,到FDCAN最大也是48MHz:' z" R$ z1 `/ K! _7 P$ a5 c; F, D
1 G' l/ _5 E0 q$ T0 u% Z
启动FDCAN:3 @$ S& T, e. z# N- w
* x8 S( W: ~' I2 C% j' b6 P
" N" K1 N/ Q% b/ I3 A$ ?: Y% {- Z
/ |3 t. L% ], P1 Z, _; ]% M' s6 s
这里没有过多的模式和配置,直接启用就好;
# S6 I4 t+ i* O. T 注意修改默认引脚:
* b0 T) E1 p; d0 @3 h& p
. v% c) }2 @: ]) O# j$ I# V; m& L- Y9 g
( G& ]( N1 J r- c 下面就是具体参数的设置,我们本次主要进行一下回环测试,所以要进行如下配置:1 }' o& \$ A" U9 _, Z
基本参数; t" k* x. P- N+ k: E6 U& l, P+ L& i
| Clock Divider: ]5 p8 o: {4 A! M, X8 i2 H* v
| 时钟分频4 K" l! s4 o! A) ^$ |
| | Frame Format" u' t4 l5 N( N3 H, u' I) z4 u
| CANFD模式
; ^/ n+ l6 d8 E; i8 U | | Mode
, C8 l, G- ^) v" |0 c. ] | 正常工作模式! [' H7 ^' g; t! q
| | Auto Retransmission
1 }1 T& R4 P/ P& [$ [ | 自动重传
, `9 F* m8 @( j9 b; r& q" v8 z5 h | | Transmit Pause
7 r b. X6 u- R | 传输暂停
- B6 Y& k! N y: k | | Protocol Exception
4 n/ O3 a. K! }# @1 ?1 K; _- h | 协议异常处理
$ L& V' f% e/ y8 Y* M$ q5 B | | Nominal Sync Jump Width
) ]6 j4 B! I& W5 w | 裁决段同步跳转段宽度; b4 `8 x% ]8 {! J, L# q
| | Data Prescaler
2 x! a: t# F. a/ X | 数据段分频系数! Q! Q2 p! [3 h
| | Data Sync Jump Width
/ z% N* x) k; Z | 数据段同步跳转段宽度' N3 n D8 m1 ]& h5 _' y
| | Data Time Seg1
/ s m: A1 V" a$ P2 B6 g | 数据段时间段12 ]) L/ q7 g) f6 M Y2 y; d
| | Data Time Seg29 k! Y" W( R6 Z) B3 P$ {& n/ [
| 数据段时间段2; s2 }2 D2 v; o2 H) m+ I' q
| | Std Filters Nbr9 z" R: e% p- e
| 标准滤波器数量
2 ?' p/ J' E/ I/ {( [. Y. p | | Ext Filters Nbr
& O4 Z7 u( q% t N6 e6 p | 拓展滤波器数量
; C; L8 O; D& D | | Tx Fifo Queue Mode8 z% G2 ^% X2 {
| 发送模式) d& T# A6 P7 o0 ~; d5 I- e8 ~2 e
| | 我们按照例程中的配置参数进行一下配置:3 `" M. m Y2 T7 {9 j2 T9 z2 R
4 H+ G5 N' z1 T T- X/ z' y1 v
+ j3 G' z8 C+ ` 接下来进行比特率的配置,如下图:
) B. y `% h) r2 Q. t
; t _. f% ~0 C# O. ^
% B+ ~% \% E: S4 F
上图的选项最小数值是1,比特率=CAN时钟/时钟分频/预分频/(Seg1+Seg2+1),所以就算是最小配置参数,得到的最大比特率是16M,不过我们还是不要设置那么大,就和SPI的配置一样,太大不一定通信稳定。4 T' _* n/ e) h8 ^2 u& z
依然是回环测试,我们将CAN接头短接:
; t- y2 i; u: n% u ~
2 J# A, X4 j2 i. h4 z 4 q. Y% H: a7 _$ A+ g+ x
这里我们主要配置过滤ID,前面各开启了一个标准滤波器和一个扩展滤波器:6 K% s# Y; {7 f# F' e: @1 T8 [( }# U+ y
- /* Configure standard ID reception filter to Rx FIFO 0. Only accept ID = FilterID1 */7 g7 X; S* l$ @) z9 F2 {9 ~
- FDCAN_FilterTypeDef sFilterConfig;9 k1 O0 |9 {2 F r& y
- sFilterConfig.IdType = FDCAN_STANDARD_ID;6 W' z' d" V+ W) K& s& Z
- sFilterConfig.FilterIndex = 0U;
, d) J1 i! T; i. x4 u1 A! C - sFilterConfig.FilterType = FDCAN_FILTER_DUAL;
/ |2 O! E3 ~ z0 Z - sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;$ q4 B) n N/ Z' H' a' s$ B% m
- sFilterConfig.FilterID1 = 0x444;% {! }% `" ~ q* w. o# W
- sFilterConfig.FilterID2 = 0x444; /* For acceptance, MessageID and FilterID1 must match exactly */
0 p' ]/ Y* D! x: I - if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)3 c; g$ e9 p3 R) |, W
- {" I! ?% C2 R0 _$ }
- Error_Handler();
+ w7 N) O: x0 k% y- _6 | - }
& {1 o5 [7 R$ M9 x4 \/ ~ - * Q( N, S: N3 z+ o; \ t r
- /* Configure extended ID reception filter to Rx FIFO 1. Only accept ID between FilterID1 and FilterID2. */
/ j4 g" n0 s5 g% q- s2 a - sFilterConfig.IdType = FDCAN_EXTENDED_ID;
" \. J+ [% ?7 Z! ~2 s - sFilterConfig.FilterIndex = 0U;& L1 E% f: }- m5 a5 f6 j
- sFilterConfig.FilterType = FDCAN_FILTER_RANGE_NO_EIDM;
. |; f v8 f( r - sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO1;
! z% {$ Q3 y- A - sFilterConfig.FilterID1 = 0x1111111;, p3 q) y x6 a. a9 W
- sFilterConfig.FilterID2 = 0x2222222;
9 n; l3 E ^$ Y4 ]* Z* a+ j - if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
$ J# s! {/ E U - {+ V+ k/ o% _- j C
- Error_Handler();
! Q7 w, |! L1 t - }
复制代码 然后是进行数据的发送和接受,这里都是通过FIFO的模式进行缓存的,并在接收节点进行接收数据打印:) r9 d" r" p8 p- d5 l, c' A: c7 i
- FDCAN_TxHeaderTypeDef txHeader;6 X: x6 y5 B# H: r5 B
' _7 v( y/ h5 k1 y7 t% \0 y/ p/ R- /* Add message to Tx FIFO */
9 ~* h \* S {. g" |4 m9 P - txHeader.Identifier = 0x444;
: Q: l) i+ A9 Y; _; E4 d4 B - txHeader.IdType = FDCAN_STANDARD_ID;
0 b( k; r" \" z: s - txHeader.TxFrameType = FDCAN_DATA_FRAME;# o; t8 G# v6 A/ e6 A
- txHeader.DataLength = FDCAN_DLC_BYTES_12;
2 q G F; z8 T6 C: S; | - txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
; X$ s2 m% \$ [! s7 y - txHeader.BitRateSwitch = FDCAN_BRS_ON;
1 u+ c: q5 F0 O0 a( ?) ~ - txHeader.FDFormat = FDCAN_FD_CAN;
i5 O n& O' y( X, z# q: b - txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;. X& M8 i% ?. e6 z; D# M
- txHeader.MessageMarker = 0x52U;
! t, K) v6 \2 M2 S& Z' B - if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData0) != HAL_OK)
0 O! [5 _2 C+ ~ - {
& H3 c" `- b5 r - Error_Handler();
0 G d/ n: L. N# f ` - }* A9 J! [8 G$ E! \9 I, L6 Q
, _' h n4 A0 a' l" o' }- K6 n. O- /* Add second message to Tx FIFO */% i1 h2 `9 i/ q6 @5 K4 k: _6 I
- txHeader.Identifier = 0x1111112;
( j4 ]; ]# c0 T - txHeader.IdType = FDCAN_EXTENDED_ID;1 [2 }6 E4 c1 Q3 C) s
- txHeader.TxFrameType = FDCAN_DATA_FRAME;% n0 d" ?( E! E% o z Z: w8 y
- txHeader.DataLength = FDCAN_DLC_BYTES_12;
; {/ F% R) O& j5 I - txHeader.ErrorStateIndicator = FDCAN_ESI_PASSIVE;6 A! G* g3 T" l' Q6 f8 E
- txHeader.BitRateSwitch = FDCAN_BRS_ON;: Q9 D: x6 {5 R
- txHeader.FDFormat = FDCAN_FD_CAN;& s& a1 \$ z; F6 r( Y
- txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;
/ M( D. }; Y9 `$ h2 w* _ - txHeader.MessageMarker = 0xCCU;
) S( L S8 F7 y% F5 W! J) t# F - if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData1) != HAL_OK)5 H3 {: s% ^ z
- {& a1 k5 P% l1 k: P) l+ \ p
- Error_Handler();+ z) m! }- g6 u' c; N1 Z
- }( A. y2 M# |3 O2 B
- . [- J# W7 t5 P) n3 U" N) h6 a
- /* Add third message to Tx FIFO */
( X! @+ w. D& @6 j' @2 M4 _7 S& d2 C - txHeader.Identifier = 0x1111113;* k, P' a: X! f( ?; L
- txHeader.IdType = FDCAN_EXTENDED_ID;& |- x9 |/ v* M$ M
- txHeader.TxFrameType = FDCAN_DATA_FRAME;
; A# g! {' T) Y7 G: k6 t - txHeader.DataLength = FDCAN_DLC_BYTES_12; I- X! m) C/ E. W
- txHeader.ErrorStateIndicator = FDCAN_ESI_PASSIVE;3 j2 x! p8 G! p* A& ]2 x
- txHeader.BitRateSwitch = FDCAN_BRS_OFF;1 f4 d9 S7 q9 D& o4 U
- txHeader.FDFormat = FDCAN_FD_CAN;
* A8 G' G* {3 e9 ]; v. R7 _8 R - txHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS;
4 H: f2 x4 C6 g - txHeader.MessageMarker = 0**U;$ m, G. b. m7 _2 e- B
- if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData2) != HAL_OK)+ S F9 h# U) w8 v- m
- {: i, X6 y4 e% H: T( \
- Error_Handler();
2 F. S1 p5 F( h/ t' T - }( K' {! ^: y0 _9 N
-
" }' ]: l" u) `# m" ]! ?* I0 e - /* Get tick */
$ y8 ~) Z4 X5 W: V$ X z+ `% K; L - uint32_t tickstart = HAL_GetTick();3 r. i& y& ] j5 |6 I
5 a1 E# U2 w; ~0 D- /* Wait transmission complete */
: F ]8 i, e- h2 g( z/ q4 S2 O4 I8 m9 o - while (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) != NB_RX_FIFO)1 s3 A/ u8 e% @8 F0 ?4 n% A; a! @4 Z
- {) U' G3 @( i3 X3 f% S2 }$ x& C
- /* Timeout handling *// O" Q) k) _* A
- if ((HAL_GetTick() - tickstart) > TX_TIMEOUT)
& k4 l$ }7 @0 c" o - {
! B' F0 ?0 F( e1 v9 t - Error_Handler();
: \5 V: O1 D( r- v+ G+ ^ - }
* Z. @+ J( \1 ]' Z a0 |* ^6 c - }
% | D; ]+ m7 \% {7 J. V9 [- u -
) A" F/ h% i6 C! Y - /*##-4 Receive messages ###################################################*/3 Y3 y2 B3 `( w; U, |4 `1 K
7 I5 o4 Q3 Q2 n7 I; w+ f# e/ o- /* Check one message is received in Rx FIFO 0 */
1 b! x4 ?+ y7 q - if (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0) != 1U)
& ]+ I& k, M$ D- \+ ?; v - {
' k* _0 }( m; M+ Z4 A, \9 m f" t - Error_Handler();
; g/ c2 |, F- \* R2 B. Z4 k - }
1 ?4 u4 @: p& S: I
# v, i6 l2 Y6 T9 `7 C' ~, b9 y% h- /* Retrieve message from Rx FIFO 0 */* C0 ^; ~2 w0 ^1 U
- if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &rxHeader, rxData) != HAL_OK)0 w# h n) I4 K# r
- {
, d T$ D$ w% s6 y7 U - Error_Handler();8 I& z/ A, c0 V
- }3 ~% B; |; L3 C# K$ G% J; y" b
- 8 B& a% L0 Q( `6 B2 p
- /* Compare received RX message to expected data */* f0 P s. [( H6 U; ^ z
- if ((rxHeader.Identifier != 0x444) ||
W2 H8 j6 O% {/ b - (rxHeader.IdType != FDCAN_STANDARD_ID) ||( f! e7 f& j) W% L2 ~* e
- (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||
+ u4 C8 W3 T- ]+ h - (BufferCmp8b(txData0, rxData, COUNTOF(rxData)) != 0U))
- L9 V. v' H' g* v1 k0 ]6 g% S - {
0 I' s; z' K. @* x) I) ^- x - Error_Handler();+ y5 g4 c8 u1 y
- }
! O9 O. F2 e1 G& w4 |# O, d5 f - HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);: `' P9 y! V# Q) o' n3 }0 g) K+ b9 _
- /* Check two messages are received in Rx FIFO 1 */
8 t' ~- Q2 e# ]" z5 O) a - if (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO1) != 2U)# Z0 ?# w' R6 a3 _2 }9 s
- {6 R9 ?1 I& e0 a' h8 B, m M' g3 F4 d
- Error_Handler();
, d: K* N7 B1 Z x5 `1 O - }; v4 o' l: |" U* S$ q
$ S" j2 L) b4 I2 } W- /* Retrieve message from Rx FIFO 1 */7 T6 D3 U* b3 @0 H3 O$ g; X
- if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO1, &rxHeader, rxData) != HAL_OK)
" E, k4 J* \, Z# d - {
7 P/ Z: Q9 g# f- \7 t% y3 i3 J: K - Error_Handler();
" v- U1 w3 c A! q% f) O# a L - }
: c& \$ e9 @6 t3 ~ - ; b; g' U) _! s: n3 k2 h! J
- /* Compare received RX message to expected data */
' ]) s! E2 H; B% `# j - if ((rxHeader.Identifier != 0x1111112) ||* c' e" I u7 b% V9 t
- (rxHeader.IdType != FDCAN_EXTENDED_ID) ||# M5 b9 f3 m5 Y* ~
- (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||
& a8 ~& u4 J# D- g; [ - (BufferCmp8b(txData1, rxData, COUNTOF(rxData)) != 0U))
Z4 T n3 U. T7 e+ q; \, N3 v* J - {/ p* \) W# W, h$ `* q3 s# c3 E# d
- Error_Handler();
" m9 K& m& x( B% f; Q - }4 D7 N$ f! ]$ f) h+ i5 v
- HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);" H7 X6 y& A. A8 k& @# y7 S2 v
- % m- z' _& g. \, R- l
- /* Retrieve next message from Rx FIFO 1 */, i2 w, j% c/ L4 L* C6 z
- if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO1, &rxHeader, rxData) != HAL_OK)! D1 v) X3 W l0 T
- {/ ~2 a' i. h% M' |" w- U
- Error_Handler();
7 v. r0 P3 p8 I) e - }
6 |5 n4 L b$ p, }' \9 F; z5 U8 {! |! z
5 d, i4 u! w7 S6 @. K- /* Compare received RX message to expected data */) F' d: e; P2 p2 \
- if ((rxHeader.Identifier != 0x1111113) ||7 q7 A" j# Q+ d5 J& Q" P
- (rxHeader.IdType != FDCAN_EXTENDED_ID) ||; y5 V, u# d* V9 `
- (rxHeader.DataLength != FDCAN_DLC_BYTES_12) ||: Y- v2 S$ ~. l& Z9 y
- (BufferCmp8b(txData2, rxData, COUNTOF(rxData)) != 0U))
' }+ y6 f: R) Z- L9 T7 ] - {
# J( a5 m- Q4 }; ~2 l8 U' ~; e - Error_Handler();
- {* q* {( e7 p+ l( ^0 k/ E - }& ]" |; T- S$ x6 f# j5 d6 Q: r* W4 H. t* c
- HAL_UART_Transmit(&huart2, (uint8_t *)&rxData, 12, 0xFFFF);
复制代码 最后在通过在FIFO中查询的数据进行打印,与发送的数据一致:; v1 J% |) G0 o
* I0 y2 W+ y4 q" u$ ~5 N k- S# { % M4 o8 g7 V" T- w6 a P
这里多了一个C8,应该是串口出现的问题。% }- ?8 ?) _# D; Y3 E, r- R3 b
. n) c" p4 n; ^( x3 J! i
. i4 J( B+ @( n' M# x0 V1 H2 h
- L# {6 C6 l: u8 x: t0 M# |3 v. }5 q4 S) Y# {6 |0 O4 W* ^. K0 I
$ @; ]& k5 w+ d7 g( W3 I7 j. _: u6 C* d1 R
: Z- @! x, ]4 @( w1 R
@) e$ t* {* M
9 H" ?8 C6 S7 c' b, I( w& b+ u% J5 O
! \ |& b: u- W& n! A+ d
7 G3 _1 L6 J6 @" V" V0 [' K: q! B
|