CAN基础知识介绍文中介绍了CAN协议的基础知识,以及STM32F4芯片的CAN控制器相关知识,下面将通过实例,利用STM32CubeMX图形化配置工具,来实现CAN通讯的环回测试" O& f! C9 k2 |3 j D: W3 U/ p
3 h9 @$ x; o: l) M1. STM32CubeMX配置
$ E7 }: k/ ^5 k2 {5 `
% }3 w4 S% S- _' q+ F+ ?8 Q( r3 `- z/ l: o" |
⏩ CAN是挂载在APB1总线上,设置PCLK1时钟频率到最大45MHz2 g V& S6 R' ^7 J7 W G+ T T
6 [% } @/ V$ m* a" t
* p- I8 S! h/ S% S% @ J7 T6 a
\: `9 K+ S0 j2 y5 H7 @! O9 x& ?3 h! |$ r
⏩ 激活CAN1,配置位时序参数,其他基本参数以及工作模式(此处设置为Loopback环回模式)
. p) t! `/ n+ Z( J" S9 f9 w' `( L2 }' i3 P4 x
d9 J8 f6 f5 n; @
- X1 y( m" Q' t* W' G3 D
7 }0 S$ d C. s! B, e0 w- {CAN波特率的计算公式:只需要知道BS1和BS2的设置,以及APB1的时钟频率,就可以方便的计算出波特率。比如设置TS1=8、TS2=6和BRP=6,在APB1频率为45Mhz的条件下,即可得到CAN通信的波特率=45000/6/(8+6+1)=500Kbps; C$ ]# f2 l+ n7 F
# h# ]2 ?( O2 Q
4 L9 E$ I7 N5 ?+ x5 U: R
" M8 L6 \# H- I& V4 P: \ h4 Y
$ ~) Z8 O7 Y- v) ?/ H⏩ 激活USART1作为调试串口,配置相关LED对应的GPIO引脚作为指示灯
! W7 T6 t d3 C V1 U* c
- S' K" }0 c4 Q% }
/ [6 L- {/ k6 g6 l3 t) @2. MDK-ARM编程
; O- |2 S3 h2 Y: |# X3 Z$ y
) i! ?: [, `8 b/ R+ F4 m0 U2.1 几个重要的结构体
9 O) u7 o1 F7 G* D& \% k2 H9 G" j% _
+ k9 `0 L0 E4 N6 l6 s8 g, m( a⏩ 主控寄存器 CAN_MCR:负责管理 CAN 的工作模式: D9 }: S, p/ M8 ]* K
- typedef struct: L7 e5 r( d3 t4 P5 u; l9 ~
- {
9 l+ W) K+ l2 n4 u, P" \ - uint32_t Prescaler; /* 配置 CAN 外设的时钟分频,可设置为 1-1024*/
# W! Q1 R( |! y; D - uint32_t Mode; /* 配置 CAN 的工作模式,回环或正常模式 */
4 X7 k- V3 N' @ - uint32_t SyncJumpWidth; /* 配置 SJW 极限值 */
4 w: _7 |4 f" ~# G0 w! v) h7 l2 _" R - uint32_t TimeSeg1; /* 配置 BS1 段长度 */* p1 _* U: E( O% A6 X/ P
- uint32_t TimeSeg2; /* 配置 BS2 段长度 */; W6 y2 k1 l3 K5 y: z2 k7 Q
- FunctionalState TimeTriggeredMode; /* 是否使能 TTCM 时间触发功能 */5 L- P, I# @" v% [
- FunctionalState AutoBusOff; /* 是否使能 ABOM 自动离线管理功能 */
# N5 `" g& [- S( o1 l - FunctionalState AutoWakeUp; /* 是否使能 AWUM 自动唤醒功能 */
& K. ?& O8 u8 n6 ]7 U; V7 g - FunctionalState AutoRetransmission; /* 是否使能 NART 自动重传功能 */2 y$ w3 _& C$ ]# _! F) f& q- M
- FunctionalState ReceiveFifoLocked; /* 是否使能 RFLM 锁定 FIFO 功能 */
8 V- m2 T( X+ x0 Q2 b - FunctionalState TransmitFifoPriority; /* 配置 TXFP 报文优先级的判定方法 */
7 y, E* V, g* I! V - } CAN_InitTypeDef;
复制代码 ; W4 ~) Q6 ~* a, j' j$ v( ?5 j: k
⏩ 发送及接收头结构体:主要用于构造发送报文,以及接收报文。收发发文时,需要自定义头结构体变量
4 [& F" @6 H# G
$ I* U! C% ?8 p, k5 b! |: X. @3 Y* }/ F! j' Z' h
- typedef struct
+ t0 A7 y" g/ m - {
0 d" D2 E- Q7 A! J: J - uint32_t StdId; /* 存储报文的标准标识符 11 位,0-0x7FF. */
4 F7 ^5 m. V6 K8 [ - uint32_t ExtId; /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */2 H6 N% K0 h" P1 `* d [% `0 s
- uint32_t IDE; /* 存储 IDE 扩展标志 */5 Y4 B7 X' J/ L! G4 ?8 ?4 I) A
- uint32_t RTR; /* 存储 RTR 远程帧标志 */+ r+ |& n C- s1 o# Y2 z+ }
- uint32_t DLC; /* 存储报文数据段的长度,0-8 */, x6 D# K9 O4 d7 p
- FunctionalState TransmitGlobalTime; " U- U' q* C/ @
- } CAN_TxHeaderTypeDef;
% I- d9 K7 n! _ - + H. S! y) a& \
- typedef struct
" d0 T4 A" @! m) Q: G - {' \0 M- T; B9 b* m
- uint32_t StdId; /* 存储报文的标准标识符 11 位,0-0x7FF. *// m* @8 |; h4 {6 S) h1 p
- uint32_t ExtId; /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */" ?- Z: B) W& B5 W( D2 `( J! W% I" o
- uint32_t IDE; /* 存储 IDE 扩展标志 */6 G4 X/ w, y- q9 _
- uint32_t RTR; /* 存储 RTR 远程帧标志 */
% O. D) F, B0 t - uint32_t DLC; /* 存储报文数据段的长度,0-8 */
k. a: @6 c! @' s& E! U - uint32_t Timestamp;
M! I! Y- E) y0 R. B/ E - uint32_t FilterMatchIndex;
9 W6 [( O. N' H! Z1 Q. q/ [ - } CAN_RxHeaderTypeDef;
复制代码 4 G, m! E2 x+ j
1 y3 Z0 @" d5 G# }3 g
⏩ 过滤器结构体:STM32CubeMX不会初始化过滤器的相关内容,需要自己添加4 b5 }, G$ i# @3 a0 E
# @7 t' {! d# {+ V( ?2 p/ O( c R/ K+ b' [% V; B6 a$ d; j9 i
- typedef struct
) j' r0 g! E& j* }) c- Q& J+ p4 c - {
G; P& B n" I! A - uint32_t FilterIdHigh; /*CAN_FxR1 寄存器的高 16 位 */0 F; t4 R+ a3 A* ~ {" z2 }
- uint32_t FilterIdLow; /*CAN_FxR1 寄存器的低 16 位 */
9 x- r% E( N1 q - uint32_t FilterMaskIdHigh; /*CAN_FxR2 寄存器的高 16 位 */
* Z$ R1 T) Z) d - uint32_t FilterMaskIdLow; /*CAN_FxR2 寄存器的低 16 位 */: {( X! t# d( \
- uint32_t FilterFIFOAssignment; /* 设置经过筛选后数据存储到哪个接收 FIFO */
" r# y" X. v# m9 m! @" R# K - uint32_t FilterBank; /* 筛选器编号,范围0-27,CAN1是0-13,CAN2是14-27 */
8 Z( W9 U+ A! U - uint32_t FilterMode; /* 筛选器模式 */; ?) V1 H9 X5 G) E# N
- uint32_t FilterScale; /* 设置筛选器的尺度 */' h# z1 O4 d( Z4 T
- uint32_t FilterActivation; /* 是否使能本筛选器 */
! f4 u9 S* ^" u; s - uint32_t SlaveStartFilterBank; /* CAN2起始过滤器组 */0 r9 H8 y% A5 w( s
- } CAN_FilterTypeDef;
复制代码 , d7 C. d) x" T% ?" `; q
. L* v6 T" R0 G6 c) u6 {. P, ^2.2 程序编写( a! _8 h4 W0 R- {) z
3 F L$ s2 B, R% x6 e
' B2 }/ D j8 ]9 D: v⏩ 生成工程后,打开can.c文件,可见STM32CubeMX已经对位时序参数、其他基本参数以及工作模式进行了初始化。但是并没有初始化过滤器的相关内容,因此需要我们自己添加,并在CAN初始化时调用
/ P3 E' {8 c1 Z% R! a) v0 |2 B* Y( t6 \7 ~, L
# ]& l# H# q K( ~2 d$ P- //下面的设置只使能了FIFO0,并不过滤任何消息
; X8 e- f C: c3 G: G4 M0 s - void CAN_Filter_Config(){8 ^7 @* F0 h' ?( ]7 |* n- K9 d
- CAN_FilterTypeDef sFilterConfig;
* W# [7 M9 A: r8 r5 K - 3 R8 T6 Y6 `4 O
- sFilterConfig.FilterBank = 0; //筛选器编号, CAN1是0-13, CAN2是14-27
6 p3 w! U5 g2 U2 x) A% D - sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //采用掩码模式
4 n4 e3 a" Z/ L/ R( R! W* s' X - sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设置筛选器的尺度, 采用32位
! f" l& D& P" h0 O& ?" A7 P& Q - sFilterConfig.FilterIdHigh = 0X0000; //过滤器ID高16位,即CAN_FxR1寄存器的高16位
' Z5 \4 K( W: ?7 o7 y% Q - sFilterConfig.FilterIdLow = 0X0000; //过滤器ID低16位,即CAN_FxR1寄存器的低16位; L% U& r f$ O$ e
- sFilterConfig.FilterMaskIdHigh = 0X0000; //过滤器掩码高16位,即CAN_FxR2寄存器的高16位. e# U6 D6 W- e3 c
- sFilterConfig.FilterMaskIdLow = 0X0000; //过滤器掩码低16位,即CAN_FxR2寄存器的低16位
, S5 V/ L9 n8 C8 T - sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; //设置经过筛选后数据存储到哪个接收FIFO
( Q8 I4 n4 b% ^9 r# u8 E& K4 z* {2 F - sFilterConfig.FilterActivation = ENABLE; //是否使能本筛选器: y& l3 v O9 D+ o; G, A6 y
- sFilterConfig.SlaveStartFilterBank = 14; //指定为CAN1分配多少个滤波器组2 B8 U( `& {! Y6 v# }9 C9 U
-
! {) z6 Q4 P1 N - if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)& E7 a7 j8 w% x! i% d( }: O' W
- {
" h; I m) k; K; j7 g( ^4 I - Error_Handler();/ k* H) g z$ y7 u" z
- }; X: @/ b7 W0 |4 f9 [ r n7 c
- }
复制代码 & b' Q- \7 r6 n' X5 K
⏩ 编写发送和接收数据函数:此处将格式固定为标准数据帧,ID为12; s" E" M5 J' t( o. K: Q' v4 A$ F& K
) A( S1 B+ X1 ^- x. q S5 a
8 L4 ?% v7 H/ n; V6 p
- uint8_t CAN1_Send_Msg(uint8_t *msg, uint8_t len){
7 I% Y1 l M5 U/ F7 u - uint16_t i = 0;5 ^2 ]/ c, U& U8 |+ ~+ A9 }' v
- uint32_t txMailBox;: U0 j) U1 Z$ n; F+ \; ~
- uint8_t send_buf[8];5 L1 W/ X7 Z: f7 u" N3 Z$ Y
- 6 Z% L% j/ ]9 D
- txHeader.StdId = 12;
- G, y3 Q# ^. J" T& n - txHeader.ExtId = 12;* f8 ^ q/ c0 v% k# f0 V9 b
- txHeader.IDE = CAN_ID_STD;
" M& n E# ?8 W& ^2 j/ D% u - txHeader.RTR = CAN_RTR_DATA;8 J% l$ D. ]/ ?- ?/ p4 S7 e8 i/ w( I
- txHeader.DLC = len;* f- X" v6 }1 F1 Z" c: b
-
" y* t- m5 m+ |1 H- I& @ - for(i = 0; i < len; i++); M* P& }/ L2 J3 T) S3 V
- send_buf[i] = msg[i];3 e9 i$ ^& c, t
- 5 S6 F. K3 u; Q K
- if(HAL_CAN_AddTxMessage(&hcan1, &txHeader, send_buf, &txMailBox) != HAL_OK)
; I6 ]# f' F M$ r! ~ - return 1; 8 \! ?; W8 Q; e7 f! h) I/ c( `
- return 0;
/ E4 E3 J- c4 x. A" h" W L3 s - }/ G+ A9 K) T6 F! H8 g$ e
- 3 U7 t. x( c, S' M) k" s# c
- uint8_t CAN1_Recv_Msg(uint8_t *buf){" L0 R9 y+ Q3 q# ? Q3 I
- uint16_t i = 0; ; W6 y. i. P! S# t
- HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rxHeader, buf);8 G! C, \+ ]4 z: j
- 4 G/ j5 F# k4 p
- if(rxHeader.IDE == CAN_ID_STD)
. ^9 C3 w; A; l- m+ H - printf("StdId ID: %d\r\n", rxHeader.StdId);
+ f4 X/ P4 U1 ~# C, N6 R0 f4 o. t - else1 a+ x+ W9 W$ \; j9 m4 H0 Q j
- printf("ExtId ID: %d\r\n", rxHeader.ExtId);& x R1 D l2 s2 G! J2 k) B
- * K" S J1 k" u; A e9 K; `% ?
- printf("CAN IDE: %d\r\n", rxHeader.IDE);
0 [! |' h }, s( y: Y7 H - printf("CAN RTR: %d\r\n", rxHeader.RTR);: r& p% |7 `. X% Z
- printf("CAN DLC: %d\r\n", rxHeader.DLC);/ E+ q* o7 K% \/ d0 \+ o, ]- |
- printf("Recv Data: ");
( \4 v! c: R( c4 G _% @1 ^- X: I -
@, b2 G P- W ? - for(i = 0; i < rxHeader.DLC; i++)1 A# N" x. C% s) a* a2 @0 d
- printf("%c",buf[i]);: F, W4 S# G) p6 l2 R& k
-
: g8 r( S O" p) x% Q - printf("\n");! {# G: V! s7 @% u, i
- return rxHeader.DLC;5 V- G" F4 y& C( D6 n9 }* u
- }
复制代码 $ s: f2 P |/ ^0 B
⏩ 默认Cubemx生成的代码并没有can start,没有调用HAL_CAN_Start(&hcan1) 来使能CAN,因此需要在CAN初始化代码中添加0 j4 T7 ?; h; K Q0 l! J S
+ ?* x X3 c& S9 t: x1 H1 X% e9 j4 O s' ^" d6 X) B+ G1 e+ w
- void MX_CAN1_Init(void){
; X, h/ H* Z8 ~7 F e - ......' A* x# n. g% p& R
- /* USER CODE BEGIN CAN1_Init 2 */% w3 x6 b3 ?+ N( L" F0 d$ ? r
- CAN_Filter_Config();- {* t$ S( B0 l' ?+ b
- HAL_CAN_Start(&hcan1);
/ V! ?8 A/ ?/ y - /* USER CODE END CAN1_Init 2 */
b* \( n; g9 B6 D g* w. J: w - }
复制代码
X2 k; P& g* C! |! O⏩ 主函数main.c中,代码如下
1 @* k$ q$ m% H0 f# b* i6 z0 T
$ V0 O; i' b. s: j, ^* A7 L2 o e
! a& S: J0 ]8 B& c- int main(void){* \" S1 c1 w) _! J
- HAL_Init();1 C& H) f7 r2 y9 Y1 i
- SystemClock_Config();# W8 N: m4 D5 I' | |5 v b
- MX_GPIO_Init();
; z% T& P' y# E3 N2 ?, i, s2 f - MX_CAN1_Init();
1 r) z4 x: l9 I' K - MX_USART1_UART_Init();
1 H- J E! e: S7 L- b2 I% y, i. W - /* USER CODE BEGIN 2 */
" E% Q7 r3 J& S7 L3 f; U8 V; G9 N% d6 | - uint8_t ret,i;/ o. W( u, L1 ?7 Y% {( z
- printf("CAN Testing....!\r\n");
* |) C0 ~6 g% _& }3 V6 `. a - uint8_t txdata[8] = {76, 79, 79, 80, 66, 65, 67, 75};
/ a( u4 C6 L% y6 O8 Q+ |3 ^ - uint8_t rxdata[8];: N0 u1 u1 J* O9 \
- /* USER CODE END 2 */
s# ]2 E7 N% }$ X3 k2 O5 ^ - /* Infinite loop */0 ]6 y; G& F! s. @
- /* USER CODE BEGIN WHILE */) ]5 N! c; q, U: @! N- f4 h$ D
- while (1)3 D ]% \3 S; J+ M9 o
- {3 x1 R0 J8 V L+ y
- HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
- m- G* c; r9 N* a+ F4 p - HAL_Delay(1000);
; J7 y8 G3 H& u; m - printf("Start Send data...\r\n");
/ ~# e& k' ^4 j$ h - + Y6 z: `9 r7 T2 N! j+ f
- ret = CAN1_Send_Msg(txdata, 8);
, V3 Z- y$ K' H4 z - if(ret == 0)
+ |$ m/ ?" i; _ - printf("CAN Send success!\r\n");$ \- @- r/ X' H8 Z/ g
- else 5 x8 n$ Q3 W" `' O. o
- printf("CAN Send failed!\r\n");/ S5 n/ U: K* Z* C5 L# ~
-
' P! f# {( m3 {6 T8 J6 C - CAN1_Recv_Msg(rxdata);
- |* B( m/ l# ~& E" w7 s - printf("+++++++++++++++++++++++++++++++\r\n");5 T/ A- T R7 {, e7 e q* p
- /* USER CODE END WHILE */5 G5 {" Y( D! P: Z
- /* USER CODE BEGIN 3 */
/ a2 F, q# W5 i( S; ~- g* f9 W) q+ z - }
6 h# y& j) P) G& C+ g! ?1 H# P - /* USER CODE END 3 */8 v; J7 ~5 U6 c! ~# q2 D/ _
- }
复制代码
/ m% Z% q0 a! Q* l/ p3. 下载测试) j! m0 ^* c1 X' @$ @
" O& F7 N: N0 n
4 m# V' Q; {# e6 [, c; v& Q; |6 g2 r
编译无误后下载到开发板,可以看到系统运行时D1指示灯不断闪烁,串口不断的打印CAN环回测试的数据. M3 r3 d& r! y! G7 u; N8 S+ ]
: m; X3 W5 ?7 D3 q/ s# Z9 C* L
% b" F: R" q- \5 F
5 ^- T7 H1 D* w7 Y, z4 n
/ @. N0 r, }# \& M5 O: h
6 ]: ]1 h9 C; C' W9 _2 z转载自: 嵌入式攻城狮
) P; P8 K# L- L如有侵权请联系删除
0 L) X1 n9 ~8 B, a5 |
. e1 h1 t+ `4 f) D/ h, r2 G& t' O* @
( @$ o o3 B* j0 u. D: o
! l0 U1 L* ~* w8 K# e7 v" g; l |