CAN基础知识介绍文中介绍了CAN协议的基础知识,以及STM32F4芯片的CAN控制器相关知识,下面将通过实例,利用STM32CubeMX图形化配置工具,来实现CAN通讯的环回测试; T1 j& O. g" L' o" p1 P A L
9 }/ v& p/ G8 L! h: F( W
1. STM32CubeMX配置, j: s# Q6 y' p
: `( m7 }. \0 ^) z1 N
0 P, V1 t" E; D# L& C& ?. u8 z⏩ CAN是挂载在APB1总线上,设置PCLK1时钟频率到最大45MHz& n/ c9 y9 Y- s: U
& k- y. P9 k I( e. Z
. F+ O- u) [! G; ?0 ~/ g" o1 h0 T
! e" Q Y+ u# R" ]/ ]
! o; z- d, R" n: m& C( Q _⏩ 激活CAN1,配置位时序参数,其他基本参数以及工作模式(此处设置为Loopback环回模式)
) u v7 P. x1 j4 ^8 ^' s4 R( \" d2 V' a% V0 S- B f% g
) l* v5 E5 U! q6 C2 Y" h
0 a# b! b6 u* N. d& s7 \
+ p7 ~7 ]; [) R3 A& h( {2 iCAN波特率的计算公式:只需要知道BS1和BS2的设置,以及APB1的时钟频率,就可以方便的计算出波特率。比如设置TS1=8、TS2=6和BRP=6,在APB1频率为45Mhz的条件下,即可得到CAN通信的波特率=45000/6/(8+6+1)=500Kbps; l, _) T; z& [5 f. p
& F- m* g" o7 v, }: S' ^
2 ?4 E& b3 M0 Q& R
7 N. t$ w% w1 r% i; g' e4 M5 E& a! R: t
⏩ 激活USART1作为调试串口,配置相关LED对应的GPIO引脚作为指示灯2 {% Q; J9 X }8 m, I
* ]0 F4 W8 ]! }0 u) N
5 E# g- Y' |+ n; s9 s, J2. MDK-ARM编程
& D3 q* W# y; n0 |1 s; s) p5 l$ h" L5 u$ z. W- U: \
2.1 几个重要的结构体3 E/ u' k5 a# X& t6 w1 S/ ~( z7 e
$ ^1 v' ^# [& E6 Q6 J6 f d7 ]: e m3 V( h
⏩ 主控寄存器 CAN_MCR:负责管理 CAN 的工作模式0 A1 L3 x* e6 s$ V; {: ^
- typedef struct
+ \/ M9 _. f- O; H - {2 ]) M8 H$ }4 z; R$ w6 H8 k- ~
- uint32_t Prescaler; /* 配置 CAN 外设的时钟分频,可设置为 1-1024*/7 x L& Q4 C" o" r) I# }( e7 Z
- uint32_t Mode; /* 配置 CAN 的工作模式,回环或正常模式 */" |( j# x4 v& ?* y# u& G% M' }" {; S
- uint32_t SyncJumpWidth; /* 配置 SJW 极限值 */. Q) b: O9 E. [1 T
- uint32_t TimeSeg1; /* 配置 BS1 段长度 */
& L0 I* Q4 {* |* D' Q/ v: i - uint32_t TimeSeg2; /* 配置 BS2 段长度 */
' x3 U4 x/ g3 L9 o9 Q+ p5 ^ - FunctionalState TimeTriggeredMode; /* 是否使能 TTCM 时间触发功能 */. ~2 F3 C7 \! f, i0 e) Q( u! R* K
- FunctionalState AutoBusOff; /* 是否使能 ABOM 自动离线管理功能 */
0 l4 C$ b" K a* y& O$ N - FunctionalState AutoWakeUp; /* 是否使能 AWUM 自动唤醒功能 */" e! q, x) Y# ]. ?
- FunctionalState AutoRetransmission; /* 是否使能 NART 自动重传功能 */
7 x* j; F [. c* _0 [- T a - FunctionalState ReceiveFifoLocked; /* 是否使能 RFLM 锁定 FIFO 功能 */
$ M& A- }2 I, N, { - FunctionalState TransmitFifoPriority; /* 配置 TXFP 报文优先级的判定方法 */5 P. R* h* ?* F, o! z
- } CAN_InitTypeDef;
复制代码 ) j. C, O: r" O8 ]1 {
⏩ 发送及接收头结构体:主要用于构造发送报文,以及接收报文。收发发文时,需要自定义头结构体变量) O7 q4 Q1 X. n9 B8 m) J! R
% u' \1 X* v) I2 P/ F) @, E
+ T5 k. ?5 Z/ E- typedef struct8 N# C0 j6 B# S! F
- {
`2 f) d8 Q4 K) Y' F6 ^8 R8 m - uint32_t StdId; /* 存储报文的标准标识符 11 位,0-0x7FF. */9 `9 h+ u4 ?. Y( L: k+ E1 K. }4 t
- uint32_t ExtId; /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */
7 x7 }5 g$ l" _$ _7 ]# I1 ^: ] - uint32_t IDE; /* 存储 IDE 扩展标志 */
* j- k" U5 |2 | - uint32_t RTR; /* 存储 RTR 远程帧标志 */
- R' }5 x- F& y- h ^5 A - uint32_t DLC; /* 存储报文数据段的长度,0-8 */
- A% H7 p+ j& i* Z - FunctionalState TransmitGlobalTime;
* g$ }$ a; c) R3 h) d& S" y - } CAN_TxHeaderTypeDef;5 [8 h- ^# e$ F& ~; t
- & _# _; V$ c1 J: Z2 b% w$ X
- typedef struct
! t) |- [% z# M Q9 z5 q6 E5 r - {
/ v! p" [" ^+ T7 y7 u - uint32_t StdId; /* 存储报文的标准标识符 11 位,0-0x7FF. */% S0 D8 N0 r4 R7 I2 A7 W
- uint32_t ExtId; /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */
, |! i& [0 _* T3 `+ _; T - uint32_t IDE; /* 存储 IDE 扩展标志 */" [0 J2 D8 V1 S, Y
- uint32_t RTR; /* 存储 RTR 远程帧标志 */
^: c& z9 o4 U% S/ d - uint32_t DLC; /* 存储报文数据段的长度,0-8 */
$ h0 e' E# Y; Y0 M. S! U - uint32_t Timestamp;
4 `2 d: h0 k( I) d - uint32_t FilterMatchIndex;
% L# `& g$ v8 ?7 Q: q) t* ? - } CAN_RxHeaderTypeDef;
复制代码
5 s# L) l" H9 D% O# Y$ D& _! P) k: s2 U/ C' N
⏩ 过滤器结构体:STM32CubeMX不会初始化过滤器的相关内容,需要自己添加
# N4 w+ P3 M" G1 ?/ h4 D0 ~2 @5 [/ {* H& |3 b$ C
* M* g- ?4 U7 q$ F6 n
- typedef struct
, n' v' {5 O8 w1 I. W2 t/ F - {
$ i! Q# P( D% O) T$ V - uint32_t FilterIdHigh; /*CAN_FxR1 寄存器的高 16 位 */
6 @8 x, O) g# ^' L* f - uint32_t FilterIdLow; /*CAN_FxR1 寄存器的低 16 位 */" |4 [1 N! M, n9 i Z# j' f
- uint32_t FilterMaskIdHigh; /*CAN_FxR2 寄存器的高 16 位 */0 Y* G7 k, ]% p* a9 ~" v- @6 i
- uint32_t FilterMaskIdLow; /*CAN_FxR2 寄存器的低 16 位 */( n. Y1 n3 n u' e3 Y
- uint32_t FilterFIFOAssignment; /* 设置经过筛选后数据存储到哪个接收 FIFO */' \# C1 A$ p: t3 Z
- uint32_t FilterBank; /* 筛选器编号,范围0-27,CAN1是0-13,CAN2是14-27 */
3 g0 s" ~9 j p7 W k: i- ?* X. c - uint32_t FilterMode; /* 筛选器模式 */. h7 h6 q0 p5 q) Z+ r2 w( S
- uint32_t FilterScale; /* 设置筛选器的尺度 */$ k' ?8 h( e8 x* a
- uint32_t FilterActivation; /* 是否使能本筛选器 */
5 Z! `) d) i, s, i% c - uint32_t SlaveStartFilterBank; /* CAN2起始过滤器组 */
( Z: [* Q2 S; p6 Z- j - } CAN_FilterTypeDef;
复制代码 / I9 E. o* z9 ] r* b! V
& o6 O3 o8 Q3 k' ~- R: L8 v" |! l
2.2 程序编写3 {9 c2 D# ?" D2 O
; J( [$ q# @ V7 P4 [' j. y" k" c+ N% W
⏩ 生成工程后,打开can.c文件,可见STM32CubeMX已经对位时序参数、其他基本参数以及工作模式进行了初始化。但是并没有初始化过滤器的相关内容,因此需要我们自己添加,并在CAN初始化时调用7 y! }" w: c4 k% _0 c: K
& E' n( e4 B- ~$ J5 x# j
# C9 D2 x- M9 n) `: f- //下面的设置只使能了FIFO0,并不过滤任何消息
) e( y9 W! H: m. V& l$ _, L - void CAN_Filter_Config(){
! k+ b! k& o. P% y - CAN_FilterTypeDef sFilterConfig;% w m! ^2 A# Y7 h" J* A
- ' T5 T# F- C0 o
- sFilterConfig.FilterBank = 0; //筛选器编号, CAN1是0-13, CAN2是14-27
+ E3 C* c8 D: n; O3 L: g$ D - sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //采用掩码模式
) k0 V8 s. P3 l7 }9 K* C) | - sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设置筛选器的尺度, 采用32位
6 [; n- C4 h: u - sFilterConfig.FilterIdHigh = 0X0000; //过滤器ID高16位,即CAN_FxR1寄存器的高16位
0 V& e1 B9 w7 k8 a& h5 G" [% x - sFilterConfig.FilterIdLow = 0X0000; //过滤器ID低16位,即CAN_FxR1寄存器的低16位# }" p( y- f/ S" l4 G
- sFilterConfig.FilterMaskIdHigh = 0X0000; //过滤器掩码高16位,即CAN_FxR2寄存器的高16位& [1 {0 d: @+ S8 O% d3 s! Z
- sFilterConfig.FilterMaskIdLow = 0X0000; //过滤器掩码低16位,即CAN_FxR2寄存器的低16位. [; u! O, X% s: f
- sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; //设置经过筛选后数据存储到哪个接收FIFO3 ~. }3 U/ Y3 m% B5 X
- sFilterConfig.FilterActivation = ENABLE; //是否使能本筛选器
& P% `0 [: t- Q% f% i- n+ R - sFilterConfig.SlaveStartFilterBank = 14; //指定为CAN1分配多少个滤波器组
2 X2 ^% r8 {% `, c -
9 n; I0 F. V! D% ` - if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)5 h/ B5 g, x! E5 d& a5 q+ J
- {* P+ h! A/ a" n5 Y( n& A( ]8 [
- Error_Handler();: z2 w E4 z5 H$ W8 q
- }
' o. W& @& |3 v$ x" \7 ^. \8 e' } - }
复制代码 + v. g% V1 B: F) t5 Q7 c
⏩ 编写发送和接收数据函数:此处将格式固定为标准数据帧,ID为12
' Y# y8 ~( \9 Z1 ]2 j( r0 P0 a2 ?( x6 T5 B9 D! p
& y# j/ \; a; l- uint8_t CAN1_Send_Msg(uint8_t *msg, uint8_t len){
. f0 f; h1 N; r9 [" e2 p - uint16_t i = 0;9 G6 m+ b2 o5 \
- uint32_t txMailBox;1 }5 U9 B; ?/ d1 _' @7 |
- uint8_t send_buf[8];/ T- r: U$ ]( f/ T
-
# p; m2 X2 f6 f. v: K% o2 F9 n - txHeader.StdId = 12;9 w: B. f; c% _. E9 q
- txHeader.ExtId = 12;! K2 r1 e) ^& Q% p# `3 P
- txHeader.IDE = CAN_ID_STD;
7 ~$ R; a0 ?7 l5 k+ C1 z2 K - txHeader.RTR = CAN_RTR_DATA;5 Y6 U p, w8 y; o1 ]+ Q
- txHeader.DLC = len;
! p& g% L2 a" `. {+ H$ `4 k" Z -
" }# r1 x) s1 @ - for(i = 0; i < len; i++). M/ V) s( m' _& b
- send_buf[i] = msg[i];
% w/ A# t0 N9 @+ f- b6 ?4 t( C -
& B! }, l+ R* k+ @' L3 ] - if(HAL_CAN_AddTxMessage(&hcan1, &txHeader, send_buf, &txMailBox) != HAL_OK)
/ M. |! g/ U0 K- B9 M' U/ A; [$ s - return 1;
# E2 a4 {% s$ Y5 ~ - return 0;6 K9 p, I- l9 B+ B$ F5 u3 ~! ]) U
- }
P/ @ v' f8 G1 e3 E( `+ r
# h# k, x B+ x: B* |; |- uint8_t CAN1_Recv_Msg(uint8_t *buf){
, w; ~# k1 P; c! Z& ?1 T* [% r - uint16_t i = 0;
: w% W' L0 d2 }6 N5 u+ H& ?1 u - HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rxHeader, buf);# m5 D0 k4 s9 G1 G# s
- + v n6 z( Z) }1 @
- if(rxHeader.IDE == CAN_ID_STD)$ R$ P2 K: s5 } b
- printf("StdId ID: %d\r\n", rxHeader.StdId);
) y1 P O9 x' \7 J2 c8 M - else
# f! f$ Q0 q( X7 w- B& F - printf("ExtId ID: %d\r\n", rxHeader.ExtId);
2 E% P; G& X- h+ q9 h0 C - ( j; h1 a: ~( Z
- printf("CAN IDE: %d\r\n", rxHeader.IDE);: b. f* H I' o& W: o$ I7 Q* ?6 x. M
- printf("CAN RTR: %d\r\n", rxHeader.RTR);& J1 L9 v6 M% y: R, P% V
- printf("CAN DLC: %d\r\n", rxHeader.DLC);
, t$ s- A( ~/ ~# E - printf("Recv Data: ");
( J9 F' V$ T% L* A9 V: I$ k -
* ]2 V/ O' V" h% [8 l1 U( H7 d7 E - for(i = 0; i < rxHeader.DLC; i++)
3 \' j( b. h! W+ E, e - printf("%c",buf[i]);
8 V+ x# T" E$ o( T# v4 n0 n- g -
8 R$ ]/ L/ U4 i1 |3 C: d6 O7 S - printf("\n");
' W- R* P/ p! l - return rxHeader.DLC;# ^0 E: V, {0 H, N! a
- }
复制代码 ! J3 q5 D/ q, e" g0 N/ a
⏩ 默认Cubemx生成的代码并没有can start,没有调用HAL_CAN_Start(&hcan1) 来使能CAN,因此需要在CAN初始化代码中添加
, B% Y; _6 ^; @# h8 H) H8 P
& H" ^! q" S$ ~7 P. Q6 o( i( |3 o) P/ P
- void MX_CAN1_Init(void){& J7 B$ T5 l t- T4 i/ J: D) ~
- ......
, F7 ]/ k5 n4 F - /* USER CODE BEGIN CAN1_Init 2 */
1 \4 e% f! A2 C - CAN_Filter_Config();' p9 N9 F- l$ C3 D5 J& ~
- HAL_CAN_Start(&hcan1); 4 k$ O; h1 U0 e0 }
- /* USER CODE END CAN1_Init 2 */
3 r8 u: c# @5 O, v - }
复制代码 - G$ {' S/ Q: P+ `' S( x- }3 _
⏩ 主函数main.c中,代码如下$ ^) _ Y3 m, b. p* |
- x. ^; G3 b* K# u
0 h- g; y4 Y8 ]- int main(void){
' `2 ~6 L3 k$ w5 J& Z* M# C+ G# U! y - HAL_Init();
; e' k q' t0 \9 } - SystemClock_Config();5 |- [# u6 y/ @/ S
- MX_GPIO_Init();
8 s! m( |+ O. x1 y$ R - MX_CAN1_Init();
5 u/ s2 [1 M) [2 N/ } - MX_USART1_UART_Init();
9 _, N$ ^" h* ?7 l0 N# I" o - /* USER CODE BEGIN 2 */
, e- Y6 \- n8 B9 V* T3 R - uint8_t ret,i;) }; U9 h8 h: Q0 n- g0 a# G
- printf("CAN Testing....!\r\n");
/ ?6 Y3 g( p1 w" P: J; V - uint8_t txdata[8] = {76, 79, 79, 80, 66, 65, 67, 75};, ~) |' p4 F, P/ J. w
- uint8_t rxdata[8];
0 H9 v, Q% i* |0 Q4 |6 s& I - /* USER CODE END 2 */
7 n2 L0 M( o1 T - /* Infinite loop */
0 Q; Y- ^1 r2 Q* h9 ^1 l9 g - /* USER CODE BEGIN WHILE */
1 ?5 ~4 R% ~/ g3 P% m - while (1)& O" ~8 ^: L+ c# ?, w D2 W
- {
. j* z4 C/ |" k2 e" i; U - HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
- t; N2 J6 v/ t3 P7 |1 \) B. M - HAL_Delay(1000);
( T/ B$ O# f5 v/ y& s g - printf("Start Send data...\r\n");
! r' P! S+ A ^0 z -
2 v: M9 N. h+ t o+ x0 a - ret = CAN1_Send_Msg(txdata, 8);
# T5 a, v' c8 q0 b. @ - if(ret == 0)
, n$ c6 C, Z* G L$ T - printf("CAN Send success!\r\n");
m$ X- ?( J8 _% R. M" c: Q9 s - else
4 I9 A* l& V1 m. L1 Z m5 @ - printf("CAN Send failed!\r\n");
4 v) b4 L' s+ t - 0 V% T) H/ M+ C6 {" \1 z
- CAN1_Recv_Msg(rxdata); 3 I/ k* V, G, `, x L( _
- printf("+++++++++++++++++++++++++++++++\r\n");! g: J, D: X& G0 `
- /* USER CODE END WHILE */
, ~/ k, k; E" p) T! }8 C% } - /* USER CODE BEGIN 3 */) s9 h0 v9 R- X. L. h6 k
- }
/ Q/ u+ b5 h, X. D3 Q) ^ - /* USER CODE END 3 */5 \9 o* q! m. t' R' o
- }
复制代码
' `6 U; C Z$ ^8 r1 z4 }3. 下载测试$ a4 o3 ^( u$ N% |( K* N) C h
7 w) L- h) Y! D4 _) z8 A0 O u
, u' d+ u3 B# j2 X
编译无误后下载到开发板,可以看到系统运行时D1指示灯不断闪烁,串口不断的打印CAN环回测试的数据
8 Y/ L8 i( G9 e c
( I# Y1 H; k/ B4 j+ @
: y. h) W1 l& b* B
: B) U3 z; R4 d5 p
9 B! i, J4 S- A* o" o
& T* ~' T3 @/ @/ ?转载自: 嵌入式攻城狮
6 e) I8 J- \$ t! a! ^8 Q, G; J如有侵权请联系删除
8 z/ U( R% T z* L: Q5 n6 u! O& W0 b2 O" G0 r
2 v3 m M) X% [; k$ F
" j' M$ `) D! J8 ~& {3 R
1 V- k4 \) V3 f5 d3 I4 E |