CAN基础知识介绍文中介绍了CAN协议的基础知识,以及STM32F4芯片的CAN控制器相关知识,下面将通过实例,利用STM32CubeMX图形化配置工具,来实现CAN通讯的环回测试) s* q7 H9 P6 g" t6 s
4 V9 @+ s0 a# e9 q* K1. STM32CubeMX配置: |! V8 t4 m9 M' c5 Y7 h
3 [/ ?0 S% q' f2 y* R: c. o/ n
1 T9 _7 w# @& ^# M& X2 Q/ [⏩ CAN是挂载在APB1总线上,设置PCLK1时钟频率到最大45MHz
. q i0 a) k, P4 J) w# ]6 F. t3 | w7 Y5 L8 U# Y
$ c m7 g$ m7 `7 C; z0 h3 d/ T
) e7 g3 q; |) O# F) E* A
8 @9 D# n* X! ~7 U+ T9 b' I2 y⏩ 激活CAN1,配置位时序参数,其他基本参数以及工作模式(此处设置为Loopback环回模式). i: G9 o1 @. c- U& u
. D5 w M! j) G1 Z7 n/ h# s
+ ]( `* a- t+ e. s
2 y2 j: Z& c; I% \* j" i6 W: m, x7 N
( A: f4 }% d* ]- i
CAN波特率的计算公式:只需要知道BS1和BS2的设置,以及APB1的时钟频率,就可以方便的计算出波特率。比如设置TS1=8、TS2=6和BRP=6,在APB1频率为45Mhz的条件下,即可得到CAN通信的波特率=45000/6/(8+6+1)=500Kbps
, _/ _7 P" `. H# e2 W8 a' h2 W
% b! b0 O2 ~1 h/ C+ J3 i' M
) Q. A2 o7 x" D+ [/ [) `
' i6 R$ P! [; K- ]% f/ l; y0 }0 K⏩ 激活USART1作为调试串口,配置相关LED对应的GPIO引脚作为指示灯( D# r& O9 J8 z5 P- o( f
- p/ K( ] X' i
+ ?, n% C9 Q0 ~5 k- X: V2. MDK-ARM编程% Q, |; `! Q, J/ P$ w/ c6 b
% i8 E0 e; ], l: e, F2.1 几个重要的结构体0 T4 X3 T) a* C! a1 e
+ u, x( C7 E) m. I8 B
* l+ F5 t% Z- o" H, K5 X/ P$ ?
⏩ 主控寄存器 CAN_MCR:负责管理 CAN 的工作模式
$ D. y+ \" `6 y. i" R' {3 v7 k- typedef struct3 _! B+ I+ V1 k0 G1 M) D
- {/ F V6 X' A$ P0 q3 h) D& }
- uint32_t Prescaler; /* 配置 CAN 外设的时钟分频,可设置为 1-1024*/6 y @; u2 o- n2 j( L& Z- } t
- uint32_t Mode; /* 配置 CAN 的工作模式,回环或正常模式 */
- w, n+ z" e8 \* L - uint32_t SyncJumpWidth; /* 配置 SJW 极限值 */! Z0 S0 t" @: Y1 \* R, d
- uint32_t TimeSeg1; /* 配置 BS1 段长度 */) U7 J/ U3 {0 a1 K( `" U
- uint32_t TimeSeg2; /* 配置 BS2 段长度 */& K$ Z2 [1 ~# O% _- `; C/ p
- FunctionalState TimeTriggeredMode; /* 是否使能 TTCM 时间触发功能 */
# k: h' G3 R& J - FunctionalState AutoBusOff; /* 是否使能 ABOM 自动离线管理功能 */
7 S% h$ g' U9 P" T - FunctionalState AutoWakeUp; /* 是否使能 AWUM 自动唤醒功能 */" U( w$ B' T* M- e4 H% Q# O- E$ w
- FunctionalState AutoRetransmission; /* 是否使能 NART 自动重传功能 */( j2 [, C+ f' i4 A% i- D& @
- FunctionalState ReceiveFifoLocked; /* 是否使能 RFLM 锁定 FIFO 功能 */
$ D, H. A0 f8 \/ U1 d4 W1 f - FunctionalState TransmitFifoPriority; /* 配置 TXFP 报文优先级的判定方法 */2 Q4 {& I" a8 o" X
- } CAN_InitTypeDef;
复制代码 5 I* L& d) T% a; ] A
⏩ 发送及接收头结构体:主要用于构造发送报文,以及接收报文。收发发文时,需要自定义头结构体变量 u0 q9 e5 ~' X! | O
/ q+ u: y% M: S( w1 T: _2 }! n7 b$ F; }) g0 }8 }8 t1 B W( z$ u
- typedef struct2 d* R* m# V! q; _+ o
- { j: x' D8 }; ?) q1 @' G, z, h2 g& x
- uint32_t StdId; /* 存储报文的标准标识符 11 位,0-0x7FF. */
* \# _7 ~- [( r% }4 b - uint32_t ExtId; /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */
+ |/ M7 B! m+ k& z - uint32_t IDE; /* 存储 IDE 扩展标志 */5 _5 Y, m' ?: _1 n& q
- uint32_t RTR; /* 存储 RTR 远程帧标志 */
6 b# Q- G/ f* ?- s% X - uint32_t DLC; /* 存储报文数据段的长度,0-8 */: M5 M! H" r6 n+ `- m- j
- FunctionalState TransmitGlobalTime;
) B2 h6 V. m0 o/ u - } CAN_TxHeaderTypeDef;8 D5 I" v% M& u0 S. h1 W
. E z8 v ^2 F1 G; T( @8 B- g0 E- typedef struct1 h; R( Y( Y2 c5 b: i9 c2 S
- {
3 l5 n! J- O2 ~ ^ - uint32_t StdId; /* 存储报文的标准标识符 11 位,0-0x7FF. *// |( h( q) r0 s3 p
- uint32_t ExtId; /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */
% ^3 ]. \; d+ s0 w/ ]$ ^ - uint32_t IDE; /* 存储 IDE 扩展标志 */! O; z" Z% _; E* J) _* Q8 R8 h% n
- uint32_t RTR; /* 存储 RTR 远程帧标志 */
' e+ D* f5 \ { y4 ^3 h' @/ o q - uint32_t DLC; /* 存储报文数据段的长度,0-8 */
2 n3 _1 A& m; J' F a! u r, | - uint32_t Timestamp; . J r( m! H P
- uint32_t FilterMatchIndex;
4 ?3 J# j4 K7 a! M, R* I! i, j- H - } CAN_RxHeaderTypeDef;
复制代码 7 e0 i# }3 @4 t# Q4 w$ R$ t- U( B
( O7 F0 K$ _( W" Y7 v⏩ 过滤器结构体:STM32CubeMX不会初始化过滤器的相关内容,需要自己添加1 |; w1 [. } H& Q# B4 G
% o3 c9 U& [3 E5 j# ~& w
/ h) T' {& Q7 r7 Z: L6 U- typedef struct
6 @& e) d* [% A6 A - {0 `9 }9 J+ Z* ~' p
- uint32_t FilterIdHigh; /*CAN_FxR1 寄存器的高 16 位 */- g; f& V2 R5 n' i O+ P
- uint32_t FilterIdLow; /*CAN_FxR1 寄存器的低 16 位 */9 w( J/ F: n9 ]3 h
- uint32_t FilterMaskIdHigh; /*CAN_FxR2 寄存器的高 16 位 */
F8 e: P2 L$ _6 f3 U4 Y9 F - uint32_t FilterMaskIdLow; /*CAN_FxR2 寄存器的低 16 位 */
! F0 `" d1 d3 R0 j1 G - uint32_t FilterFIFOAssignment; /* 设置经过筛选后数据存储到哪个接收 FIFO */
6 @# s6 Z8 @# A* C# J2 y - uint32_t FilterBank; /* 筛选器编号,范围0-27,CAN1是0-13,CAN2是14-27 */, Q+ Z) \9 o3 {, e# q- w
- uint32_t FilterMode; /* 筛选器模式 */- u' A: S6 _( c
- uint32_t FilterScale; /* 设置筛选器的尺度 */9 `& y- ?! Q: S6 ~: {0 M
- uint32_t FilterActivation; /* 是否使能本筛选器 */ W7 ^% B. M; w0 |7 l! z5 s
- uint32_t SlaveStartFilterBank; /* CAN2起始过滤器组 */+ ?6 E/ u0 ` j) T# t) `* h
- } CAN_FilterTypeDef;
复制代码
& J! g5 w, u/ s g$ o8 |
/ K& p6 ~; g7 _% S2.2 程序编写. G; X' `( g t; d
) B% L" d! C' R/ s. D: _
7 j5 U( L8 ]# B. j5 ]3 {⏩ 生成工程后,打开can.c文件,可见STM32CubeMX已经对位时序参数、其他基本参数以及工作模式进行了初始化。但是并没有初始化过滤器的相关内容,因此需要我们自己添加,并在CAN初始化时调用
$ k" h: U6 n. G2 Z4 b, B! y$ F4 l) ]- {$ B( @& _" G
8 f8 L4 @& L( H5 v& x- //下面的设置只使能了FIFO0,并不过滤任何消息! r3 V! H& U3 b) f& j5 P: j
- void CAN_Filter_Config(){; G& @8 U9 `! `3 G: S. F3 n& Y( V
- CAN_FilterTypeDef sFilterConfig;' H1 c: f& t& J$ }: g8 G8 ]; f9 |; H
-
9 D' C' V9 O( u: p. J# W - sFilterConfig.FilterBank = 0; //筛选器编号, CAN1是0-13, CAN2是14-27) \; ?5 b9 P' C# @% H4 x) ]2 b
- sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //采用掩码模式
' v u; K- e4 [6 i: k! u - sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设置筛选器的尺度, 采用32位. ?+ F$ i6 c- q9 M/ h1 _$ c
- sFilterConfig.FilterIdHigh = 0X0000; //过滤器ID高16位,即CAN_FxR1寄存器的高16位
* x j# j0 f8 ~, M2 V" D3 ?4 V - sFilterConfig.FilterIdLow = 0X0000; //过滤器ID低16位,即CAN_FxR1寄存器的低16位
' E& P/ D/ D/ C7 X8 w( o - sFilterConfig.FilterMaskIdHigh = 0X0000; //过滤器掩码高16位,即CAN_FxR2寄存器的高16位( N9 U S' r% a7 S+ r+ g% L8 Q
- sFilterConfig.FilterMaskIdLow = 0X0000; //过滤器掩码低16位,即CAN_FxR2寄存器的低16位
6 k% k: F! h) R( g0 `2 T8 T - sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; //设置经过筛选后数据存储到哪个接收FIFO9 p5 |1 ?' L' _8 Z) u" s( ^1 ~$ l
- sFilterConfig.FilterActivation = ENABLE; //是否使能本筛选器
" Y4 c1 O# v, ^ - sFilterConfig.SlaveStartFilterBank = 14; //指定为CAN1分配多少个滤波器组
) C( C. u1 q& M1 i( Q - + ~+ O5 ]5 D/ B0 s6 a
- if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
2 k) d0 d5 @% s+ L# ^8 x: [' j2 k: U - {
7 Z/ f3 L) {7 j% \+ k5 a" n - Error_Handler();% f6 ]7 h: Q( N' J8 B- P
- }1 M+ k u5 a! p+ X
- }
复制代码 & Z9 Q. f$ K4 C5 V
⏩ 编写发送和接收数据函数:此处将格式固定为标准数据帧,ID为12$ ?% W9 B5 `& k) J+ o+ m+ q2 u
7 _6 u& g y( }2 {8 |7 F2 _. G8 i3 \6 S' d2 M+ h) R' W
- uint8_t CAN1_Send_Msg(uint8_t *msg, uint8_t len){
; x a6 H2 q) `% M% J0 s - uint16_t i = 0;
0 m& q. l3 w; D1 U- v8 u8 B - uint32_t txMailBox;, r% V) L; Z* _* Q! j
- uint8_t send_buf[8];
0 l- P6 j4 A5 B4 x, q/ B8 l- g - 3 L" `8 F1 b- Y
- txHeader.StdId = 12;
& d, A& p, j& G0 }" p U; J% Z - txHeader.ExtId = 12;
" I" `( c3 |3 W2 T& y1 F6 R - txHeader.IDE = CAN_ID_STD;
+ K8 }" b! g1 e; e" [8 s - txHeader.RTR = CAN_RTR_DATA;4 p# C" q& g( {; f. X
- txHeader.DLC = len;
$ B+ j7 I2 Y; W E5 R -
. w) D) c! l3 e9 ? V/ d8 Z - for(i = 0; i < len; i++)
w, h" m q* h7 L, X5 G" s - send_buf[i] = msg[i];1 W4 R% T& M, n" ?# n, ?
-
. p8 Y! x' u, J& \ - if(HAL_CAN_AddTxMessage(&hcan1, &txHeader, send_buf, &txMailBox) != HAL_OK)
$ g; @2 G5 `& I, C2 ~ - return 1;
* L+ t8 V6 G- h& h* B4 \5 M" g - return 0;7 M" ^1 I/ j: b9 i) g
- }
6 D' @ O Z$ P1 i' g' c# Q - ' T% h/ A" N- R7 D4 D7 \. ?! r
- uint8_t CAN1_Recv_Msg(uint8_t *buf){
% ^1 f; m: d) F - uint16_t i = 0; # |6 u+ A6 q1 S5 A. o
- HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rxHeader, buf);
0 }$ g' V8 p3 R) \ g' Q0 | -
" E% }0 L- n+ C, Y; ~2 S% j1 ~ - if(rxHeader.IDE == CAN_ID_STD)
' {) ~+ r5 n0 ]6 V8 T$ V0 _- T8 g - printf("StdId ID: %d\r\n", rxHeader.StdId);1 e6 v! {" f B. P. Y9 c5 Z
- else% D: |7 n% @( N/ \
- printf("ExtId ID: %d\r\n", rxHeader.ExtId);
6 y1 q3 s/ D, O7 l7 [1 E- M3 h% N( K - + R* c* _# ]6 c; y: q
- printf("CAN IDE: %d\r\n", rxHeader.IDE);5 s! C' _& n+ }8 o q3 h/ l
- printf("CAN RTR: %d\r\n", rxHeader.RTR); g6 v: n; f* M+ f( `
- printf("CAN DLC: %d\r\n", rxHeader.DLC);6 T3 Y/ ^& ?; |0 O) @
- printf("Recv Data: ");
' W& I2 ^) B& N V1 T% u2 E -
& r- ]2 W$ X6 D V* Z# i - for(i = 0; i < rxHeader.DLC; i++)+ _4 F: j0 T2 @# a1 \1 h
- printf("%c",buf[i]);! f# ~% m) j0 ~: h, a! _9 e6 b
-
+ U g, K: p7 V: Z - printf("\n");) |7 w- g+ `. @8 H6 E3 e6 V
- return rxHeader.DLC;2 j& R: P! I: z h3 G
- }
复制代码 & m* E1 K4 a+ w/ N7 w% ]2 N" n
⏩ 默认Cubemx生成的代码并没有can start,没有调用HAL_CAN_Start(&hcan1) 来使能CAN,因此需要在CAN初始化代码中添加0 D |7 r o0 v! E3 S
$ j/ m/ D+ K, {" }9 [7 O* x$ C
6 j0 Q0 b% \+ k8 c8 R- void MX_CAN1_Init(void){' r% U1 N& [2 D. r' X% j
- ......
, F8 L) }2 \; {8 j" v/ [6 [ - /* USER CODE BEGIN CAN1_Init 2 */
. P F+ c, k) ^( n- B N - CAN_Filter_Config();
7 S, Y6 u) g- D - HAL_CAN_Start(&hcan1); ; A% S0 k9 B# y# b# l; v$ [
- /* USER CODE END CAN1_Init 2 */
9 q) l7 D% n y* u - }
复制代码 8 z: C) k9 i& [" Q( L/ ?
⏩ 主函数main.c中,代码如下5 @3 L3 q5 O# t4 h7 x
! p* L" q7 O4 s) n: s
9 |& y5 b7 n0 Y; y; f4 v0 h
- int main(void){
6 q9 I, z( g) r - HAL_Init();9 i8 J" S" i3 ?9 r8 W
- SystemClock_Config();
1 k& O# X4 L4 E4 F- t! \1 z: C - MX_GPIO_Init();! I: Z2 J3 _) u6 j% `
- MX_CAN1_Init();( x' X' T* C* V
- MX_USART1_UART_Init();1 t, E; B# w" `7 |0 `
- /* USER CODE BEGIN 2 */
# b% {3 o) |9 r+ r% `$ G5 y - uint8_t ret,i;# h e- R: h9 c5 f
- printf("CAN Testing....!\r\n");: z- M# ]2 u3 S( \0 C
- uint8_t txdata[8] = {76, 79, 79, 80, 66, 65, 67, 75};5 k1 E5 Y0 t' M# v
- uint8_t rxdata[8];* e' [9 H9 p7 \, |
- /* USER CODE END 2 */
! P/ Y1 j+ o% b; w8 I - /* Infinite loop */
; B6 a6 |( i4 q1 \: r7 W - /* USER CODE BEGIN WHILE */1 }/ U2 M) I1 q
- while (1)) E5 G) e; e7 h: z5 }4 C! L" m
- {, E( ?$ G! y, A) y$ B7 ~
- HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
/ C. Y2 u* v) s5 y, b- D2 l- @ - HAL_Delay(1000);. W: h& ~: R8 m8 d6 N/ |6 S: _
- printf("Start Send data...\r\n");
, X( X! k( o2 W! u9 z, b, B -
" {: H5 u6 ~* u. D9 _ - ret = CAN1_Send_Msg(txdata, 8);; n! N# z) ^1 g9 p z
- if(ret == 0)
* W) A( G0 [) o# j" x8 R - printf("CAN Send success!\r\n");! P' s: U3 v% H% J' Q5 B
- else 5 ?, c5 B: t- i, y2 x
- printf("CAN Send failed!\r\n");
_4 c' ~$ O1 Y) [) _9 M! D -
" `# Q3 S9 a7 k( r1 ^3 B - CAN1_Recv_Msg(rxdata);
+ Z( Q3 S8 Y _ - printf("+++++++++++++++++++++++++++++++\r\n");4 g, Y4 M! K7 i9 [3 p% G
- /* USER CODE END WHILE */( O) s( U; p! o5 {9 k9 s
- /* USER CODE BEGIN 3 */3 N$ m; M c, L
- }! B, C, `" C; N2 [
- /* USER CODE END 3 */ z# \$ o/ H7 m
- }
复制代码 # z$ i$ Y) l& X1 z
3. 下载测试
8 ?6 p; H: y( r# D1 U
$ G( Z6 k# r* j0 x: Y+ _. q4 ^9 Z, L8 |/ y# E9 W. i
编译无误后下载到开发板,可以看到系统运行时D1指示灯不断闪烁,串口不断的打印CAN环回测试的数据8 j* r) ?! T1 Y# E6 ^- R
4 I. _1 W9 Z4 @, [' ]5 w
c; l1 U5 F1 p5 R* d2 U
/ v! G# i9 z. A: w( r( B) E ?4 i
" E7 T3 u" T5 R7 J& q9 ^( u2 V& z) g转载自: 嵌入式攻城狮
/ J0 T' Y9 `2 S- j# y如有侵权请联系删除
9 n6 }' p( m% H
/ e+ b3 y& [$ I6 V3 Y) t
* K9 @4 w% U' L) U) J8 `: v6 j7 X/ }* u% W' A, Y( b& [3 q
) ^) Z/ [9 d" Q; M
|