STM32-CubeMX-实现CAN通讯 Q0 c2 F& j9 ~0 }4 ~5 X9 {+ p
2 R; ?( [# r4 A! [4 N" l+ H
首先要安装cubemx跟Keil5两个编程软件,然后打开cubemx软件,新建一个工程项目:; u. b# |( D: Y9 T# O! z# E2 Y( D
% v# V! J) [3 N/ D" F/ _
输入CPU型号:1 a. d/ N/ B. n6 ?
! i4 y! R. z r
在右下角双击CPU具体型号:4 J5 ]2 I9 p8 [3 l. k S% s
& S- `$ c* T$ h& L' w6 p5 A* r: W
稍等片刻会打开如下对话框:
, U/ k$ A; j% q- U0 Z& B7 O: Q
3 d& B: G* F, s* a9 r* w 首先要配置系统的调试方式:我们选择SW方式,7 V$ F. S, { `
6 }4 {1 |) k4 l9 @. V( N1 C 然后配置晶振源,这里选择的是外部晶振,8M,% ^$ D2 K. x& a
7 }' h. u7 y* u6 m( {/ C
使能看门狗,
5 F! B! |2 i5 ], @7 O2 R; F
' I5 A7 D- y7 [. I6 C! n
使能CAN,
* h% L3 a. x( z! z2 F, h
' N6 c. t1 M# N; B5 `
使能TIM2时钟源,采用内部时钟,! A1 N! F3 c; Q+ U. Z/ V
7 D, k* ^( v3 V; y( H
配置完成后可以看到单片机管脚已经做了配置。
4 V' E9 T# Y! C( `$ v% ~3 W& ^& Z2 w+ l
9 |! i. ~* |+ H f; d
切换到Clock Configuration选项卡,配置时钟周期,这里配置为8M
0 D, N8 H2 O! @4 D! M1 W1 N
4 e. h. ]' ?) M6 u! w
切换到Configuration选项卡,进入CAN配置选项配置波特率如下为500Kbps。
" ~) \ {; t/ u. q% ], ?: K8 k
- W0 G# T5 S2 H6 a1 p, F5 B
配置接收中断。4 ~) ^/ E9 y6 U) f
* O: o9 d' E) ^3 H" D. I
配置定时器如下为1ms定时器。! }$ u5 D- w% n) h6 n/ ~
; ]0 X1 d8 s8 f- A- ~' ^& R: Z! f+ h
配置定时器中断
8 p: G) ^/ `' W. c0 h7 r+ h+ L' a
# H6 q9 Q& ^& U9 e: \ 配置完成后点击保存,然后点击如下按钮,生成代码
7 o) p, Y% P* S3 r% ]2 U
. F% A3 e+ C N4 B3 B 填写工程名称,路径,编程软件等等
; [1 \: E6 _3 a% A: P. `" A
6 z5 F% M5 Q$ z( R1 J j
选择生成代码的方式
% M2 S+ T0 S8 I
+ r* }0 J5 N" ?) Y! q 等待……
+ M. t9 R3 E. C0 X* l. D* X7 { 之后点击打开项目。代码生成部分完成。6 }& `6 |9 f3 P5 a1 ~
+ d2 X8 h' p6 V2 ~$ K, B
添加一些特殊配置和逻辑代码,添加CAN的配置信息函数。
( R% j3 O9 y, e
% s3 v3 l0 }" ^% ~0 f1 G. y$ K/ A1 P8 c) e7 Y( X7 k7 |( U
- void Can_Config(void)
* k! O$ p/ r8 U7 }8 G2 W - {
! J R- k' Z7 a9 o - hcan.pTxMsg = &TxMessage;
" x" N# _9 U4 i/ w# D5 H2 @. A. X+ g" i - hcan.pRxMsg = &RxMessage;
& Y U! Z3 V9 A9 ?# ^, U - 6 `+ {/ z7 l) A# l' a4 N% `( @; P @
- /*##-1- Configure CAN1 Transmission Massage #####################################*/
' |5 R* y' z+ \) I+ ]5 ` - hcan.pTxMsg->StdId = 0x123;, N( I6 E" j& v; C3 o
- hcan.pTxMsg->RTR = CAN_RTR_DATA;1 Q: n/ P1 X, f/ Z6 s
- hcan.pTxMsg->IDE = CAN_ID_STD;9 i5 _& ?( q+ ` C5 E4 y+ x% C- c
- hcan.pTxMsg->DLC = 8;8 S# J% t# J$ |, ~7 Q* Q
- 1 L0 f4 e5 L7 p7 f+ n5 ?
- /*##-2- Configure the CAN1 Filter ###########################################*/
5 [/ r, W8 L1 `9 x1 D - sFilterConfig.FilterNumber = 0;
( P+ U X/ \/ G% C! a - sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;9 P8 c- d/ `$ E3 N s* A5 [
- sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;/ S) @3 o4 O$ s' k+ @- K9 a3 I" ]2 h
- sFilterConfig.FilterIdHigh = 0; w" k* {, }! S' M) e, c; v( a( S5 t, D
- sFilterConfig.FilterIdLow = 0;/ e8 r: [* g y3 G3 D
- sFilterConfig.FilterMaskIdHigh = 0;
5 y5 ?1 z* H. M( y5 P9 \5 \ - sFilterConfig.FilterMaskIdLow = 0;
0 I3 `. c, I) u1 d7 Z" m' C - sFilterConfig.FilterFIFOAssignment = CAN_FIFO0;+ ]9 ?" W F1 E) q5 A, K. p
- sFilterConfig.FilterActivation = ENABLE;- B" ?& ^6 A' f' u# g
- sFilterConfig.BankNumber = 14;. K! b# k) e. J3 i# T
- HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);
! T" ] H# S1 I& X$ S - }
复制代码 , d. k0 }! ?& g- \
' e/ s% c' P/ |: v
( d% W! W1 {% Y- r3 y$ f0 v0 D% m, z
在Main函数中while(1)之前调用一下即可,添加定时器启动函数:
! z4 |+ [" h: Y% Y1 ]. ^5 y- x" }; B9 f/ D7 w
3 r& v5 ~. K4 V+ x
- HAL_TIM_Base_Start_IT(&htim2);
复制代码
& x9 W" O6 m4 P3 @* j, g) X2 q$ @2 c* v2 K! k
CAN接收中断启动函数:/ `" d8 p1 J% }+ |+ v0 F7 a
/ l- l, E2 Z& v/ O+ R5 n
) u& u8 e( }" ~& F: J
1 z1 C3 P& R/ r( I }. a- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);
* I. o1 e: E: ]& i5 X/ `3 c - /* USER CODE BEGIN 2 */7 t/ G8 Z% v, K. E1 {" X
- 9 w, h f. h- k$ w# Q" \
- Can_Config(); //Can配置信息
9 h9 @2 j3 ?4 T8 O( u+ \ - HAL_TIM_Base_Start_IT(&htim2); //定时器启动
3 g! N( r5 @$ B5 b0 W/ k5 R - HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);//使能Can接收中断
8 ]( d" ]8 e4 o9 w( ]7 u0 S -
6 d: F. s* u1 B* Q8 C( J - /* USER CODE END 2 */
复制代码 5 C* l$ H2 w" T( k
) J: o4 L/ X8 Q8 W, |1 @* w 打开stm32f1xx_it.c文件,找到如下函数,添加接收中断启动函数:3 z% W& V: b7 v2 D; b
& Z' z6 B O: c& q+ O+ D: M6 _
- l; ^3 R; `; t" C* o: D- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);
复制代码 # s9 S( L& y. {8 a% z% f% @/ h
% c% P3 C$ ~4 C1 x5 e 注意:接收中断启动函数使能一次只进一次中断,所以中断退出前要再次使能。
: P4 D, v6 \$ V/ }2 R, G' R, I. U L+ U# f6 n2 v% Z E
8 ?6 B1 |. S, s& H2 N- 0 W _ k+ ~5 R( X* u
- void USB_LP_CAN1_RX0_IRQHandler(void)/ I# ^, l3 N) o9 `" H
- {2 M* X3 @& M# r" S6 W
- /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */% T4 L% }) s+ q. Z H4 P# w
- 1 i& l) }# c {
- /* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */
2 ?! b+ h8 f8 d6 f4 q+ u: x - HAL_CAN_IRQHandler(&hcan);
- o$ y( y4 I" m+ y8 e# l, z' m - /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */
* g( N6 f% R/ a - HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);//ʹÄÜCAN½ÓÊÕ
7 @3 j" `# ]0 @3 k, j s - /* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */6 ]: e3 F6 _6 s
- }
复制代码
- R3 S: K/ K: h( \9 Z5 x6 \+ a6 A' ?# u8 N5 `* x
添加CAN接收服务函数:
9 k+ w( U( K; V0 c: L+ V" p* R' A# Z( Q
& W( d9 A% j+ i/ B+ d9 Y; h, ~
2 c9 T. z3 v! d/ X+ y
" Q8 t% c3 N$ [9 t- B0 N
说明:该函数在stm32f1xx_hal_can.c文件中已经有定义,它的定义方式如下: z4 V* g" `8 k
7 {/ V& Z6 U: O+ }, r, w
; f. K, F' s( f0 P- __weak void HAL_CAN_TxCpltCallback(CAN_HandleTypeDef* hcan)
复制代码
. }3 q/ N; `! N/ e! }* M' K
# @9 C v% P3 R函数前面的__weak关键字意思是如果有同样的定义,先执行没有__weak关键字的函数,所以当我们定义了HAL_CAN_RxCpltCallback函数后,编译器会先编译我们定义的函数,而忽略系统定义的该函数。当我们没有定义该函数时,系统会编译带有__weak关键字的函数。; Y- V! r/ b! u5 R3 f
" g: q1 H. ~" ^/ e$ D: z9 r$ _: e D! G
$ n3 {, F4 P2 g1 s- void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)$ [2 ?7 c7 ^6 f1 K3 O3 z/ m
- {
& `4 t" X K- A - unsigned short int speed;! s D3 e# S- D, q5 r, h/ M0 l
- switch(hcan->pRxMsg->StdId)" C6 [9 `1 F7 {2 H$ n, a- `
- {//根据ID处理数据3 h5 N8 o, K9 F+ \0 z; Q+ Y
- case 0x123://
% q6 w* _$ p, M8 u# S a# M) W9 A - /*在此添加数据处理逻辑*/, q/ q7 M2 K0 I' ~. `* v! ?' n4 @
- break;. }* Q6 f( O1 l/ H0 } m3 `% f
- default:. f2 ^6 d- o! o/ ^3 Q
- break;) `3 R: g* |; `) m
- }' v' ~6 D# R0 O3 K" U! q2 L' S
- }
复制代码 * Y' t, `1 R8 T$ q$ A& ?
0 E5 Q- c6 g% R8 f. e' s& _
添加CAN发送函数:
( d/ r" j6 q: j# d5 W
7 J! Q; W8 Z6 D1 b! i& ^4 @" o& H4 V0 d4 J6 X; d5 }
- /* USER CODE BEGIN WHILE */& S& _2 A3 y c6 D
- while (1)
& N, S+ C, ~1 {/ t+ Y! y - {( d- y3 V! @, r3 H( z' q0 Q/ j8 i
- HAL_IWDG_Refresh(&hiwdg); //喂狗函数/ L+ w; `/ R7 \! h4 {/ @
-
$ Z! w( U$ o% B: o" C: a - if(Can_Trans_Timer == 0)2 m. O1 f; h) Z1 S( I; o
- {//每100ms发送一次数据0 W- c5 ~3 p4 w" I3 e6 u# M
- Can_Trans_Timer = 100;
( e) \. K+ v* G) a+ i% f - % @( ], b6 E- W( }& J) f% O
- % M7 }5 S% V, N6 o; n( j( I9 v+ u
- hcan.pTxMsg->StdId = 0x123;; v- q3 G/ }- N8 U u' y
- . T% p. A6 t' b9 ~9 Z; ~1 v
- hcan.pTxMsg->Data[0] = 'C';, T- k4 n. {4 L4 k8 a& r" A9 n+ O- c" w
- hcan.pTxMsg->Data[1] = 'A';' `% {/ o3 n( d0 X1 i5 l
- hcan.pTxMsg->Data[2] = 'N';& `4 ~# E2 S8 v& z: e. _
- hcan.pTxMsg->Data[3] = ' ';
( V, ]* c% q& }4 n# l - hcan.pTxMsg->Data[4] = 'T';
7 Q0 X! K: ?6 N - hcan.pTxMsg->Data[5] = 'E';0 ^/ P7 X* V, g: U" m% i. J" X
- hcan.pTxMsg->Data[6] = 'S';
7 g9 d$ L5 r" m0 T - hcan.pTxMsg->Data[7] = 'T';5 Q& ~; y$ \: E7 |, V C/ L- p% ?4 L
: j! w, y9 P# {( _- 1 i+ F" p. x# ~' R5 i! J- F; f
- HAL_CAN_Transmit(&hcan, 200);//发送一帧数据6 d' T- T7 s8 @2 M6 D
- }. x; n: ~- E) g4 O; s
- }/ ?6 i2 n1 s7 [
- /* USER CODE END WHILE */
复制代码
1 f# w7 M& a" W: I; S
$ [7 X9 D3 l$ z8 ?! C4 S' r& U在定时器函数中添加定时器代码:8 ]& g# \- \" F, C0 U
* v' z: y. h2 V9 q$ w( E4 m2 B1 {; Z- q2 g) s3 H1 v+ U8 K8 ^4 B; ~
6 f- b, A$ t6 [- ]! J' ^, H- void TIM2_IRQHandler(void)
* d/ d; ?% s3 D4 L( ~6 v8 n - {& k$ b8 ~! ~+ m, A( I* U6 h
- /* USER CODE BEGIN TIM2_IRQn 0 */+ ?( N; W! a; `! L; N- X3 U
- if(Can_Trans_Timer > 0) Can_Trans_Timer--;
: R% l; J( N* h - /* USER CODE END TIM2_IRQn 0 */
4 k' \; T! j2 C2 a% c% i$ R* u* h - HAL_TIM_IRQHandler(&htim2);
$ l1 ~8 \% e' x - /* USER CODE BEGIN TIM2_IRQn 1 */
8 D* M- }$ }4 Z& z
% k: U: |- O; X( l9 S
/ a6 V) ]2 Q7 Y7 i# u) e# @6 N- /* USER CODE END TIM2_IRQn 1 */2 a! T" ?( x6 R0 A3 G; d
- }
复制代码
6 z% V/ f2 c( {1 _5 L2 s
9 v6 G5 L- O! F5 e0 P8 ] 说明:在往工程中添加代码时要注意,不要任意往里面添加代码,要在注释着USER CODE BEGIN的地方添加代码,这样在重新生成代码时才不至于将已经写好的代码覆盖掉,如下( w$ C$ ]7 E& \; N
' ^$ _( ], Y% }# o' T, x! C
0 N' i& n' o/ M5 s5 A+ \- 3 c, Z6 H& P5 D! o: S: R' z
- /* USER CODE BEGIN Includes */
复制代码
1 N7 ?2 `2 ?$ H6 ?* ?/ n3 z
( E( S# m, |# k) ^+ T$ k9 n: t" B3 z, a0 x. q
" v" ?4 ~. H: s6 R3 Y
3 y' W1 H5 ?5 P, j" O; G( S: W. M; h9 L% |0 h
2 c# j0 s/ p$ Y& C- Y2 \8 {: [; F1 I( v# u: ?+ ?
; A2 l# p% E' }5 z( C
$ D5 J% [7 I- a
+ h: r0 L: v/ E1 ?. s$ o5 v" ^: r) @: I: Y* b7 F1 G" x9 A
7 M( x3 b4 Q7 I9 x2 N+ A
3 D# I6 C- P( m |