STM32-CubeMX-实现CAN通讯 ' o$ U9 @7 x% {9 l- Q ?; g4 r, X
~' P, X7 w5 \首先要安装cubemx跟Keil5两个编程软件,然后打开cubemx软件,新建一个工程项目:
5 t( P C y! W* Y8 y: Z! C
- g" E: G/ `: w( N4 H" r+ U
输入CPU型号:
9 g. _ N4 f$ n! \
, P5 @' n9 E- l. j 在右下角双击CPU具体型号:' Z" D3 u/ c0 C
2 @. }8 D8 ?1 K* Y- Q6 l
稍等片刻会打开如下对话框:' ~7 B1 O& o5 h$ b: r& g
5 q( b/ t: p0 y1 c 首先要配置系统的调试方式:我们选择SW方式,
8 |; T4 W+ J* j6 d: x
8 n3 B l2 a4 E" T$ `- d 然后配置晶振源,这里选择的是外部晶振,8M,
- r# }4 `: U) C& X
. s: W9 ]& e h* A
使能看门狗,/ T" M% G& @0 f8 Y; {2 ~3 P
$ J! r% _% |9 V" F0 _! K, L 使能CAN,7 v# T( h! Z/ e0 Y
: J! P; j$ A5 \7 a# [+ o& x
使能TIM2时钟源,采用内部时钟,) U# H. u* k% S5 K8 F- s3 W8 P
& f) k+ Y0 D5 y! |/ ` 配置完成后可以看到单片机管脚已经做了配置。
: o4 u4 e( A9 r3 U8 h: @. T
. B6 M8 h5 f+ L: C$ j2 }
切换到Clock Configuration选项卡,配置时钟周期,这里配置为8M# L& k9 I3 Z) G
* u% U. p' V9 U' o t1 w 切换到Configuration选项卡,进入CAN配置选项配置波特率如下为500Kbps。
* w, \2 i5 Q2 B( S5 \; T# p" r
% Z( a: ]! j. G( O. E8 G8 u3 y 配置接收中断。
0 f# Z' Y, H& p- O& c
3 X, I7 S* J- a3 {
配置定时器如下为1ms定时器。 l" h/ G9 `; u8 R
$ R) E: y4 D# o% z2 R 配置定时器中断$ C# O: A0 I5 x2 v4 {
3 h! M3 j( c9 O0 B, @# ~( | 配置完成后点击保存,然后点击如下按钮,生成代码
. [# `* @) F. _5 g# b
6 R: X! s1 Q. Y5 ]+ T2 l
填写工程名称,路径,编程软件等等
; K9 m5 S) W) L1 o4 b2 i0 F7 f3 \1 f
- @' H+ Y, v# d: o, c6 z
选择生成代码的方式( y2 R6 r6 n+ |# f+ R$ S
; h5 `) z2 z! t- G7 p, ^
等待……
5 u1 p+ B0 J5 c+ M% p 之后点击打开项目。代码生成部分完成。, w1 i y; c8 `: @* i
$ Z% y% Y6 [+ c+ D3 c b( V$ g
添加一些特殊配置和逻辑代码,添加CAN的配置信息函数。
5 x: g5 C5 b ?2 q, N7 m' |' X4 P6 c8 K& }5 E
* O. z% t( R6 K# p5 f* r- V$ [
- void Can_Config(void)1 k( @2 g6 n8 G5 X8 `$ C' B, X: y
- {: o( K d3 l5 `. R* s
- hcan.pTxMsg = &TxMessage;
5 Z6 F* v9 K, { - hcan.pRxMsg = &RxMessage;3 Q' U$ B) N }6 Z9 t, \
-
. O" ~+ x0 `6 T6 B - /*##-1- Configure CAN1 Transmission Massage #####################################*/5 m+ B( k$ Z- p# |
- hcan.pTxMsg->StdId = 0x123;5 k* y) U) V; G4 F8 v i* B8 u9 s! I
- hcan.pTxMsg->RTR = CAN_RTR_DATA;
0 E; k f. d& Z3 u# U - hcan.pTxMsg->IDE = CAN_ID_STD;
& `+ L2 o, ]# m - hcan.pTxMsg->DLC = 8;
2 v J& F# }8 W* r+ h8 b -
2 a" C) E) q) e! j; E \4 q- D - /*##-2- Configure the CAN1 Filter ###########################################*/
" f1 `- e$ e W% X- b - sFilterConfig.FilterNumber = 0;
0 I# h; G. m) X9 V6 j0 g& l% q) ? - sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
1 i8 b# P3 @1 e' F, j# i - sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;$ a0 i7 i& @( ]: i
- sFilterConfig.FilterIdHigh = 0;/ o0 x3 z! b7 w/ |9 n H& G
- sFilterConfig.FilterIdLow = 0;
2 H) s% I2 q# a, E- M - sFilterConfig.FilterMaskIdHigh = 0;
Z2 E" b+ A! Y4 W" N) `) [, @9 g2 E - sFilterConfig.FilterMaskIdLow = 0;( T2 D) k ?* j& y3 {0 y2 H8 k
- sFilterConfig.FilterFIFOAssignment = CAN_FIFO0;6 |# C" i! q+ Q6 a3 i
- sFilterConfig.FilterActivation = ENABLE;
; L' Z4 X6 ]3 e3 L% N* V4 | - sFilterConfig.BankNumber = 14;0 Q0 ~" y' B7 v
- HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);
+ } S' A$ o7 d' N0 Z c - }
复制代码
. W3 i5 V2 I( ^# ~" ^. }; H6 H$ @
/ z1 |) f0 r# b4 r1 h$ ?5 Z
, s/ |4 y. n9 G0 A' K4 X% r
在Main函数中while(1)之前调用一下即可,添加定时器启动函数:3 M4 n; w" J" w4 T3 P9 X0 e3 I2 O D
+ Q( x, h4 N* W# q5 d
# E; ]+ I" p; g2 j: H- HAL_TIM_Base_Start_IT(&htim2);
复制代码
$ w4 |( u9 y' c0 m* S
% z4 l* ~& P- N. J' o2 W; ?CAN接收中断启动函数:
# k/ ^% N5 l; U2 V1 Q3 n( E. s5 \
* u, F/ q8 T% Q3 @. h/ i
7 G' g! M4 }4 ~1 ]4 ?; |& K0 w- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);
3 \4 O+ Z/ i L3 ?- t2 D9 J1 Q$ O O - /* USER CODE BEGIN 2 */ \8 B) O# D7 T' }- S
-
, W: F6 U4 {7 [3 Q - Can_Config(); //Can配置信息
" t, l6 z# K& k. C5 @ - HAL_TIM_Base_Start_IT(&htim2); //定时器启动( L3 t. L. |/ h; ? x
- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);//使能Can接收中断7 D; }2 Z) V" w1 g; R- y
- ) B2 o8 w/ c) y, o
- /* USER CODE END 2 */
复制代码 - X) d( d1 p$ T
" |. e# B% x/ w# S+ U8 k/ n 打开stm32f1xx_it.c文件,找到如下函数,添加接收中断启动函数:
5 ~; I* o8 ]& W, d5 I% O" c/ {( o, N# u' w/ C0 P+ [
- m- l: y& \; A* l# V, R- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);
复制代码
, V) V9 i7 b$ Y" M1 i E8 `1 G6 ^) l. C
注意:接收中断启动函数使能一次只进一次中断,所以中断退出前要再次使能。
/ o* v8 U9 d* Z1 t" J1 I; k
$ [# p0 P5 B V; T6 z$ E+ C0 t5 E+ `( A3 }2 x+ m9 R# P
7 v" b3 S8 m6 F H- void USB_LP_CAN1_RX0_IRQHandler(void)/ q w$ }8 w ]- e7 }6 D5 \
- {
5 J& R* v7 m( e" k, M8 I& x8 }9 E - /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */$ K' M& E9 ~! ~% z* g# J
-
7 ?- E9 u: ?5 S3 J$ U" C0 f - /* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */
6 ^ U+ j2 t6 J! f3 C& Y, C' Q - HAL_CAN_IRQHandler(&hcan);/ ^ v4 W8 J C& w' u' r+ a
- /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */
( {5 E! J4 ]0 ^0 A# Y1 t2 M9 K+ A - HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);//ʹÄÜCAN½ÓÊÕ V% U: W ]+ t5 U! s
- /* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */% v: S6 k! p8 |. h3 S$ ? E4 Z
- }
复制代码
- J5 O& y" u% x; w/ y8 ^! I- O2 `5 W! w6 _$ p+ q
添加CAN接收服务函数:
5 F7 j* {: U& Z+ Q9 f ~2 X. E5 o7 c; b# \) T0 h# Q4 l7 y, b/ Q) f0 ~# A; L
4 g7 y% e+ }6 h3 h; O
1 {( G3 w, T0 o/ o* D& Q9 E* [$ p, j: H" O2 Z" a, T$ Z
说明:该函数在stm32f1xx_hal_can.c文件中已经有定义,它的定义方式如下:# K9 X# N$ G$ i1 `
c. H! Q6 c9 W* _
1 ~; I G& f& A/ l- __weak void HAL_CAN_TxCpltCallback(CAN_HandleTypeDef* hcan)
复制代码 % |9 D% D1 T* W7 D
' V O8 y1 t. s2 ]! g- N1 B) c
函数前面的__weak关键字意思是如果有同样的定义,先执行没有__weak关键字的函数,所以当我们定义了HAL_CAN_RxCpltCallback函数后,编译器会先编译我们定义的函数,而忽略系统定义的该函数。当我们没有定义该函数时,系统会编译带有__weak关键字的函数。
5 d) w; m6 ^( b) B3 g, b- H7 D ^4 J6 z5 r, X
! f4 a! d) _0 s' S& ~
- * T- z$ K( d5 y' Y& s, P, s/ W
- void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)5 n! M5 R4 F: s! }1 p, ~+ h
- {$ `/ Q% _% s' u% p M F; B. J1 @
- unsigned short int speed;
2 {9 M. l* D- `. |! ] - switch(hcan->pRxMsg->StdId)
: d s9 u# o& d- L0 `1 F - {//根据ID处理数据$ O, u( e% L- e }. T9 e& \
- case 0x123://4 C4 Q: O' v7 h" `
- /*在此添加数据处理逻辑*/
/ A! a$ I0 s% ^- I - break;, N# G/ [9 C, O* H4 J; j
- default:5 V9 {3 W% s. n! l: H
- break;
# H9 J8 L' d+ `/ m- l - }
6 _, n, E, I- L. Q6 d: ? - }
复制代码
( ^7 h0 @; I! y m s9 s+ z' z
% `* p. I. I) v/ Q4 u+ m# L, b: g 添加CAN发送函数:) R9 w$ c! D4 q+ e" i
# m( M* F& Z t3 m) V; z
9 n& I$ p; Q( X7 r& ?- /* USER CODE BEGIN WHILE */+ E9 R$ u5 Y7 B0 B9 v' [/ t9 q
- while (1)% O# _9 r. w; _9 W
- {
0 y5 U4 C: S( Z( Y: W& }/ {0 S - HAL_IWDG_Refresh(&hiwdg); //喂狗函数4 Y& t! ^ \3 F8 N1 @" y
-
3 Z' G7 v2 a. D: s6 i - if(Can_Trans_Timer == 0)
! p6 g7 S- `- p - {//每100ms发送一次数据7 t0 i- a2 g6 |1 ?0 T( K P
- Can_Trans_Timer = 100;
r" u/ F" g1 a& X
3 I0 w4 `, o0 }# E9 |7 `" ?
; L2 I# m6 w- [. w7 c- hcan.pTxMsg->StdId = 0x123;( |/ p* g- ]6 @5 }0 v1 c1 `
- 7 g0 J; v" o' p' a! A
- hcan.pTxMsg->Data[0] = 'C';" n$ b7 G* X8 I& H
- hcan.pTxMsg->Data[1] = 'A';
5 m- c @! a& M. ]5 G0 Q' W - hcan.pTxMsg->Data[2] = 'N';6 Q* h" F7 e$ `$ e5 q% \! W8 |, I
- hcan.pTxMsg->Data[3] = ' ';7 i Z6 C0 T/ Y- T
- hcan.pTxMsg->Data[4] = 'T';2 f, Z# H6 n5 v, Y6 X4 P* ?4 m: J
- hcan.pTxMsg->Data[5] = 'E';
) a, W) t* u2 N# o5 z/ m - hcan.pTxMsg->Data[6] = 'S';9 Q& C" u0 e) C3 k$ n
- hcan.pTxMsg->Data[7] = 'T';
8 {! e% O' [4 z Z1 m* l& H8 ?! Z( F
. `7 S4 E3 S2 |! I6 b. y' V
' Y( e T6 g7 L0 K9 u1 ?- HAL_CAN_Transmit(&hcan, 200);//发送一帧数据/ ?' P _8 j; O5 G& B+ ]
- }( W% t$ c9 \9 N& k( I
- }
" a3 @* ^ j* I0 M4 N | - /* USER CODE END WHILE */
复制代码
4 D4 n l5 N; G$ c8 x3 I/ J* F5 J! ? ? H
在定时器函数中添加定时器代码:
1 q6 F# Y% K; A5 L
5 B# N% m- S* N% }1 Y
0 l3 [# L% E. }* W% R! }- % g" z. K3 ]+ c
- void TIM2_IRQHandler(void)' W5 @$ D! E& J; s* D0 k
- {, R' n/ [% d, x/ L# v% I
- /* USER CODE BEGIN TIM2_IRQn 0 */
+ j7 s# R& q0 K8 ~: `" o - if(Can_Trans_Timer > 0) Can_Trans_Timer--;! A, h& x$ I9 a8 I: K$ k" M( x% W
- /* USER CODE END TIM2_IRQn 0 */. X, b, y. G7 s8 l
- HAL_TIM_IRQHandler(&htim2);* f' V6 Y) c6 ]- }% e* W% n3 Y
- /* USER CODE BEGIN TIM2_IRQn 1 */- S2 U, \" D% e0 s" w
; c/ L, H- W, ?+ v- " {! E( k9 z4 K% ^
- /* USER CODE END TIM2_IRQn 1 */2 N. |0 E& P0 a! p0 x2 u9 C, H
- }
复制代码
$ \6 A l5 |$ E
- J) C" {# T1 |" ^ 说明:在往工程中添加代码时要注意,不要任意往里面添加代码,要在注释着USER CODE BEGIN的地方添加代码,这样在重新生成代码时才不至于将已经写好的代码覆盖掉,如下. W' G% N j- r |% o: f& A
5 A! K% \! T5 x: Q* B' X/ l7 `. B
- % G% X" b1 b/ y: e% `4 ^3 U+ ^
- /* USER CODE BEGIN Includes */
复制代码
; x1 w2 l% X( c0 E: h9 X# _2 k9 J4 M% P2 j
z# j: l+ G3 @0 S" Q/ ]4 V- k
& C, k3 g3 v# _6 L, }6 L
2 ~; z' e3 W7 r9 z$ ^, l8 H1 J$ a5 o2 E4 b4 X/ J
1 w: A% _' G( R m2 K8 e# I! {5 K! X6 A) p7 k% L
5 @* `) f7 I# J6 U( i" `' Z
7 Z# w3 A% o- _7 v0 X( ]. |8 K
, l$ C6 F0 I% l7 w
# S0 n$ g) g$ s3 d2 E: X
1 _, A7 }% j: A3 f/ i7 K4 H
" o$ V9 c: P+ l/ d- M3 j( f |