|
STM32-CubeMX-实现CAN通讯
! Y- R# p {) C, _/ r4 _" `7 n; b- r7 x9 O+ \' _2 R0 U
首先要安装cubemx跟Keil5两个编程软件,然后打开cubemx软件,新建一个工程项目:* E: ]4 M- S' y5 ]4 d5 H0 ^( Z
F- C, ]& C" G0 M$ L. P" q 输入CPU型号:
2 d( a" B) c" C7 h1 `
8 c, E+ P7 o" v 在右下角双击CPU具体型号:
1 d' Q. q- _% P# g+ I
% W. @& ~) N; G# v s7 m1 W# E3 @: w
稍等片刻会打开如下对话框:7 [( x0 o* Y2 b9 [1 S
1 W2 I' B& t& j$ a
首先要配置系统的调试方式:我们选择SW方式," j' h2 U' y0 d3 F; u* n/ J
3 k( V$ C% j) G4 g, [
然后配置晶振源,这里选择的是外部晶振,8M,- t2 W0 P {! p* Q/ s+ b
. B! K- U z5 e
使能看门狗,
* q, L/ ]8 l8 O3 N
6 B4 q! M" d" [5 n. i; w; ~ 使能CAN,
$ F8 D5 f. ^+ A/ c1 s3 f* ?
6 C0 \9 a/ A' v4 {6 S
使能TIM2时钟源,采用内部时钟,6 q9 J# e8 l6 g: z; ]7 x) v4 q
' e! K) v) ]1 \+ Y6 X
配置完成后可以看到单片机管脚已经做了配置。+ D7 }' s, @1 u& w
% m- i: k7 B n5 Y n4 X- d 切换到Clock Configuration选项卡,配置时钟周期,这里配置为8M- Q8 `9 W0 L' p( x! H
& U* H( p" o6 K h
切换到Configuration选项卡,进入CAN配置选项配置波特率如下为500Kbps。) L+ u; }1 O$ G) q8 t( K/ k5 I
6 |+ Y9 C9 V$ Q1 `6 Q9 _2 { 配置接收中断。
7 J/ m" k6 R8 m& ?
9 K- K' @0 o, H O% e8 {: l) I 配置定时器如下为1ms定时器。
" W) W! C/ x4 f B7 @' P2 m# |& {3 A, }
$ ?4 Y3 J% G' t" x0 G* U. \ 配置定时器中断+ R+ i) G9 ]8 g: v$ ~- R( t
. C1 ~2 h" |& \9 T: G$ J# F 配置完成后点击保存,然后点击如下按钮,生成代码" |1 T5 s2 X( ~- J0 x9 @: D
0 u; I/ A6 }) w* y* l+ t( ~7 D 填写工程名称,路径,编程软件等等' J$ _8 q& P/ Q* r1 f# J
0 J: A2 c/ i- {" w$ o
选择生成代码的方式
4 C/ u- s2 ?: U
+ d! M6 e6 l3 M' y! d& g
等待……& S# b# X/ z3 l. s& u
之后点击打开项目。代码生成部分完成。
3 Q2 Z: C9 Z( A9 _4 G4 o, C+ T7 Q
" j- h+ r/ o% r/ s) K
添加一些特殊配置和逻辑代码,添加CAN的配置信息函数。
( G F2 p7 T/ J A3 e i
+ |, @; R! m8 c& E' K6 X5 }" D2 W9 O* Z: s: e0 K7 E* c+ q
- void Can_Config(void)% A, a- Y5 h; h3 K
- {
! ~7 _7 t) K4 ]7 l+ u# l8 b9 C5 j - hcan.pTxMsg = &TxMessage;7 {3 z# b6 {1 b/ C5 p7 n9 e
- hcan.pRxMsg = &RxMessage;5 L& D0 c7 j$ ~
- 2 f {( Y" w: z, ?
- /*##-1- Configure CAN1 Transmission Massage #####################################*/
/ |/ s' Z2 I3 w7 D) G) T9 w - hcan.pTxMsg->StdId = 0x123;, C' c/ O: W/ p3 x
- hcan.pTxMsg->RTR = CAN_RTR_DATA;! O% w! ?1 h( x2 r5 H
- hcan.pTxMsg->IDE = CAN_ID_STD;
% Q& Q, `% S H - hcan.pTxMsg->DLC = 8;) I" J. R% b: \% f
- % _4 o8 I- c8 J6 r' |3 M n
- /*##-2- Configure the CAN1 Filter ###########################################*/% ~: l; ~2 T; Z S( _9 V5 p6 U" L
- sFilterConfig.FilterNumber = 0;* n. P4 P* s$ Y9 s
- sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;, A* s8 b6 t( q% ?, T& }
- sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;! K5 @2 A% T. L
- sFilterConfig.FilterIdHigh = 0;
. r" M: E' z( g - sFilterConfig.FilterIdLow = 0;
& i! Q4 F; V; Y t/ Q1 Y - sFilterConfig.FilterMaskIdHigh = 0;
+ l& S* ?" M" {0 h- y) j - sFilterConfig.FilterMaskIdLow = 0;
$ F/ `7 S3 J' K; m- ] - sFilterConfig.FilterFIFOAssignment = CAN_FIFO0;
3 { t0 B4 T$ b* D/ c2 C - sFilterConfig.FilterActivation = ENABLE;
$ P4 L1 Y( V' L2 |) F3 y - sFilterConfig.BankNumber = 14;& a- p U: u% ]# {/ u( P
- HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);
* I( l$ K1 J; _; e+ @) h3 @: \# I - }
复制代码
* }; b+ \. l0 l% M0 |7 |4 N7 R
?( m: d7 ~( ~" d$ p7 L4 D) f+ K3 y7 H r& l9 F, U9 N- b
2 y7 U9 N7 l; A
在Main函数中while(1)之前调用一下即可,添加定时器启动函数:
! ^+ v, g, }" x8 w' {+ B" V5 a# ]) ~
! P) N' C5 r8 r2 b- HAL_TIM_Base_Start_IT(&htim2);
复制代码
' r* z! B( t6 a8 i7 |( ^
- o8 O: @* i5 G$ c: L7 u! fCAN接收中断启动函数:
2 {; r) a: j( o3 l
# w4 ?0 e& R; l
6 @4 C( b1 `9 j: h) g- " i( i( _2 P! j& k1 Q
- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);
5 g. E* F) ~ P- W) A* N - /* USER CODE BEGIN 2 */
7 r0 _8 j; S- Z3 e3 L - # a8 l0 ]8 Q2 d2 `6 p" H4 J/ N. g
- Can_Config(); //Can配置信息 ?' H9 w# z9 D) J
- HAL_TIM_Base_Start_IT(&htim2); //定时器启动6 K! ?& L2 [( @, G$ d. N! S
- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);//使能Can接收中断
; E6 i3 o3 p' C9 y - 4 X' P: V. k! o3 f6 I
- /* USER CODE END 2 */
复制代码 ' x9 A. S' R1 ?8 G6 H7 Y
& o# C! Y$ Y+ f" ?# _ 打开stm32f1xx_it.c文件,找到如下函数,添加接收中断启动函数:* l+ Z: k- D6 z! P( V0 q$ J, I/ g
9 X; ~+ ~$ S' P0 \& s* ^ _/ U2 e
5 Y+ S# e1 i( m1 S J) S- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);
复制代码
4 v* u' p; {8 k N' D R: m6 d# x, B. E
注意:接收中断启动函数使能一次只进一次中断,所以中断退出前要再次使能。1 W. `1 O% m4 H( {8 U; R- ?/ J
" A; y/ x% L" g( q
* G# s& G. C6 W, t* o
- 2 R( @8 C. M" U- N
- void USB_LP_CAN1_RX0_IRQHandler(void)7 C% H5 ]2 u9 V# _! E" i; _9 A
- {6 J* Y6 F1 t/ V, L8 B% E
- /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */6 P' z i2 m* ]0 Q
-
* o2 s+ q+ d% K0 ^6 [% W! m - /* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */5 K W. X* [ R+ U2 R
- HAL_CAN_IRQHandler(&hcan);% E+ s' m( ?; U, o) g6 w
- /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */: D, @, v7 v1 Y3 j; N6 ?
- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);//ʹÄÜCAN½ÓÊÕ9 S5 z1 `, g2 c8 j$ X; H
- /* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */
2 W* z& a9 C: E# p2 h9 F - }
复制代码 , A3 _7 j' Z8 e- @ T* T& K6 X/ E
& `& q. r! r. x& v9 ^添加CAN接收服务函数:
, q b/ \* l6 K2 A2 k2 e
! h& H( N; y8 y' y
' R8 |: [6 A9 l$ ^/ o1 V+ J3 A" J
2 U' d9 l: e+ c4 g- V6 a5 J3 l3 [ 说明:该函数在stm32f1xx_hal_can.c文件中已经有定义,它的定义方式如下:3 C( V2 B- C0 R: E- o& m3 ]
4 }- S7 u% r* |* m7 B3 _9 j
5 t: j0 M- C; J8 y- {
- __weak void HAL_CAN_TxCpltCallback(CAN_HandleTypeDef* hcan)
复制代码 ; y+ s9 @/ J( X ^' {0 k3 Y8 n
2 k/ u* u! U. B+ l/ W2 C6 i
函数前面的__weak关键字意思是如果有同样的定义,先执行没有__weak关键字的函数,所以当我们定义了HAL_CAN_RxCpltCallback函数后,编译器会先编译我们定义的函数,而忽略系统定义的该函数。当我们没有定义该函数时,系统会编译带有__weak关键字的函数。* s9 j5 z. \% D% U7 v- d7 w
0 ?! r+ u- H$ p; t- ^* G
" V1 I( G0 E2 B) V5 @+ H: o- : y$ @( L% H6 W+ f% r
- void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)) |7 Q+ V% c" i7 I1 o) a6 y5 c
- {" Y6 w1 T% X8 B
- unsigned short int speed;2 O8 Z" S" u5 e4 F: t8 X
- switch(hcan->pRxMsg->StdId)7 j7 q6 d9 Q% l
- {//根据ID处理数据
6 \& S6 M/ s& E - case 0x123://
0 b9 e( Z/ Z' T- y( l8 \ - /*在此添加数据处理逻辑*/, f3 V4 n1 T; W
- break;5 g' N( t' B3 P$ X2 a9 k
- default:% P$ f: g( W9 H
- break;# x& G# R9 N' r( I/ s
- }, i- L* H, j8 h+ W3 P
- }
复制代码
& a4 d5 x1 k: u, y- U; y: S' u7 V
添加CAN发送函数:+ l. k9 l& y& N9 {
/ Z4 `7 P7 |( {3 i$ c' F0 s
* j3 H; j' V* R- n/ D( J& s2 i- /* USER CODE BEGIN WHILE */
5 p! a3 n1 G+ v, x; l( l - while (1)
: z1 E# L* r4 W1 T8 Z3 j - {: u5 j0 ?6 @3 \% i; U( _
- HAL_IWDG_Refresh(&hiwdg); //喂狗函数0 [0 J6 [% t% k& E
- 9 I: U$ |2 C+ b2 U& o& L# i
- if(Can_Trans_Timer == 0)
7 v- {0 t* {* P9 e6 o$ q5 c, p7 B: h - {//每100ms发送一次数据$ a+ C, ]# z& g
- Can_Trans_Timer = 100;' m8 w! Y. d$ B* G9 ?# g+ t
5 U: \7 _9 E3 g& o
$ W( m: m: V# S6 Q- hcan.pTxMsg->StdId = 0x123;
$ B' ^4 t4 R. s x7 |8 ]" K0 @6 C - 8 J, w, W- J6 A, O* a
- hcan.pTxMsg->Data[0] = 'C';
/ j" B+ S5 H7 c& b - hcan.pTxMsg->Data[1] = 'A';
3 Z0 o4 P. R7 D) H& o- ` - hcan.pTxMsg->Data[2] = 'N';
( o. o; Y7 W+ S7 U, A c - hcan.pTxMsg->Data[3] = ' ';
+ v2 o! B3 ^% s. g, I - hcan.pTxMsg->Data[4] = 'T';
( @: Q- P4 L6 ~. L# X' S9 y, f - hcan.pTxMsg->Data[5] = 'E';
9 N8 m9 `5 ?9 y% F h. s4 ^ - hcan.pTxMsg->Data[6] = 'S';
- w9 J/ G) e. ]; g" W" Y8 g) w9 N - hcan.pTxMsg->Data[7] = 'T';5 s, g& g) K1 w% T! H0 Q( C. [
( v+ G5 S* C" J$ Y8 p. ]
' \9 v, }. C% W8 j* l @0 M- HAL_CAN_Transmit(&hcan, 200);//发送一帧数据7 @) {* {( c; O4 c% K# j
- }
, `& ~3 b0 v) x" N3 J6 I - }
; T* t0 ]% A/ v% }1 w - /* USER CODE END WHILE */
复制代码 2 u: W. s: U; P- t; g
+ Z9 Y+ t" O# Q ?. Y2 |- k9 F: v
在定时器函数中添加定时器代码:& J$ Z5 l$ o+ O: x0 i/ U& Z
) H8 [! v& D) u- b5 _& t% J
0 Y' f" A3 W1 S
- [ f) x$ Y: g( c& u( m- void TIM2_IRQHandler(void)# B' X2 y8 t j! n9 q7 S$ d- F
- {* ]: E. ?) f- `5 z; g+ t8 J7 g
- /* USER CODE BEGIN TIM2_IRQn 0 */% n+ i: B, O- R! G) ?6 Q) ^' f
- if(Can_Trans_Timer > 0) Can_Trans_Timer--;
9 b) @# S% h) H% [, u4 l - /* USER CODE END TIM2_IRQn 0 *// n' s3 y3 l# i8 Z, P
- HAL_TIM_IRQHandler(&htim2);
% a: Q# {/ L1 }/ M4 H - /* USER CODE BEGIN TIM2_IRQn 1 */+ H5 s9 h. E$ Y; B2 t
* i$ i6 C1 }& S) f3 g
/ m7 h! z# z7 g- /* USER CODE END TIM2_IRQn 1 */
@# ?, D z! w8 U+ U* l - }
复制代码 . q: E& ?! I6 ]1 a% _/ W" c9 d/ c
' y9 o! V6 O* F, C, F* z 说明:在往工程中添加代码时要注意,不要任意往里面添加代码,要在注释着USER CODE BEGIN的地方添加代码,这样在重新生成代码时才不至于将已经写好的代码覆盖掉,如下
1 l4 V! l# n; w0 l/ l( x' L' d r
# y' D$ l! S0 C+ d" A1 R; Z( k& e; a
6 T! j( H4 E- d# L- /* USER CODE BEGIN Includes */
复制代码 # B3 a, N: }. v, e" q- n8 q
& j9 V2 n7 _/ `+ c! F
9 a! ~9 h) z1 K& d0 q& ]" S; e# O( w$ E( K: h, g
; `' }5 ?. f5 F E! H9 i
6 c* s# v6 C& x8 q, c$ W5 G7 L+ {
% U, ~9 M0 v' k* H# o- [: \" J% ]( B! }1 q+ f& W' m
7 g, S& k$ e' X3 n, z# Y7 K# R4 R) W! m ]* @. Z
( ]# n( T$ E. I& m* _/ o. L6 x/ f7 `
# e b( x$ D3 G+ G. y) w; k3 X/ j/ s/ l0 ~+ E7 K) B
l, L2 `# A3 K/ W
|