STM32-CubeMX-实现CAN通讯
$ |2 }+ K1 n5 _: d: k5 i$ v/ @ ^# }' W( G+ w. z" d( M" X
首先要安装cubemx跟Keil5两个编程软件,然后打开cubemx软件,新建一个工程项目:
6 B; } n/ P% L s: U2 K$ Z* l
: d# h+ N9 H, v3 Q. z; Y" ^
输入CPU型号:0 b, f6 e5 s+ G7 j. F
; \: ~/ k$ c4 }
在右下角双击CPU具体型号:
; m( Z4 W0 n/ T( Q
0 o8 k" s; A% o/ J) @8 I; @ 稍等片刻会打开如下对话框:
+ G: i) Y& }; Q4 D, o9 M, `" S9 Q
( P- n4 O$ Z8 |) C
首先要配置系统的调试方式:我们选择SW方式,
: A5 \6 p2 z' Z: {, {
6 S: g: I% A% l& s! j 然后配置晶振源,这里选择的是外部晶振,8M,* @9 X* P- ?4 v0 a% g
: l: q% _; m6 M
使能看门狗,
& }1 o: v# c% _# x+ S5 j( X
* Q0 T/ {4 Z) u9 j$ Z. c+ ? 使能CAN," q8 a; W8 k# d ^* I5 A* _; ^4 g9 K% e' _
1 ^# q" M* Z- R
使能TIM2时钟源,采用内部时钟,9 I8 e Q, _; Y) F( Z. X. c
- {. J- e: _; g) b! }/ c
配置完成后可以看到单片机管脚已经做了配置。) a7 x, v5 ?' \) V+ m. I! ` u
# U$ `% O' I$ M ^* N 切换到Clock Configuration选项卡,配置时钟周期,这里配置为8M
# o) E( _& E: J9 s! k
3 ^: H8 Q9 W* a% ?7 L! R
切换到Configuration选项卡,进入CAN配置选项配置波特率如下为500Kbps。* A6 ^2 G- y) j; R: n
! a! t9 t* d8 u+ T
配置接收中断。
6 g3 q$ Q0 |1 z j! }
7 W) `& J4 C3 }; b0 Q! w 配置定时器如下为1ms定时器。6 u* K% [3 S$ _2 G/ e) j% f0 B: `
; \% q$ v% n- Q0 |& p
配置定时器中断( h- Z5 a) h3 x- V* l+ p1 i
$ [4 t3 {$ f8 k" B& ?& O/ C
配置完成后点击保存,然后点击如下按钮,生成代码2 W" D2 _+ D- g' P w% Q+ y
7 H M9 g3 ?- g' H( ~; M3 ^
填写工程名称,路径,编程软件等等. q# B3 s0 y& U% y6 G) p
! x% G7 p2 a8 f \( n' j2 I5 L
选择生成代码的方式
( }6 F6 {3 z- b
1 r0 U. K+ L) t/ q, f/ d5 j
等待……
- B4 }. W) W; E% c) M 之后点击打开项目。代码生成部分完成。
7 k. E3 T7 W" }8 O& N$ J
6 r0 U/ @$ P4 L: W7 ~' Y. p 添加一些特殊配置和逻辑代码,添加CAN的配置信息函数。# v! \; m3 | @: L3 L6 A
5 J% H, @) L1 D1 ~/ ^$ w( p0 R. K% m1 F1 m( [4 p
- void Can_Config(void)
: E: f8 r( J( Q+ I: r! [ - {
. y( f+ w+ _, i: t" F - hcan.pTxMsg = &TxMessage;" w f |) b8 h4 A7 A+ N' X
- hcan.pRxMsg = &RxMessage;
' Z% E8 M3 D6 J1 r2 Z -
, s9 P) M e( ^ - /*##-1- Configure CAN1 Transmission Massage #####################################*/
: S! `* u$ c2 ` - hcan.pTxMsg->StdId = 0x123;& l' `" ~2 L2 ]; P: ?& [3 Y
- hcan.pTxMsg->RTR = CAN_RTR_DATA;
+ r4 S* H% t! L4 P: R - hcan.pTxMsg->IDE = CAN_ID_STD;9 a2 k% Y" r' h. ^+ q9 h- g
- hcan.pTxMsg->DLC = 8; I! w+ |1 X! D1 w
-
( q5 R3 d L9 D4 j - /*##-2- Configure the CAN1 Filter ###########################################*/# F5 c1 p! I0 d& _2 p: |
- sFilterConfig.FilterNumber = 0;
9 Z& E( V5 E( B4 I$ I - sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;; O' _8 Z9 U# _3 L; Y
- sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;
. ^0 B- a9 y! S: u: Q4 i - sFilterConfig.FilterIdHigh = 0;
+ _% l' ], \. P4 N' d - sFilterConfig.FilterIdLow = 0;
( @/ p d$ e/ n1 [ - sFilterConfig.FilterMaskIdHigh = 0;
) F% k- i/ b+ d9 ?- S0 p - sFilterConfig.FilterMaskIdLow = 0;* \1 f4 n& M: \" d, Y
- sFilterConfig.FilterFIFOAssignment = CAN_FIFO0;
& d3 ]) _9 H; q$ K( y" O8 Z - sFilterConfig.FilterActivation = ENABLE;0 s; g4 c C8 A( g$ W: N$ E
- sFilterConfig.BankNumber = 14;
! j0 h |- p) D1 T& r$ w - HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);2 ]3 _* k* J2 Q+ v. p
- }
复制代码
! V! ~2 P+ e4 R/ _4 `$ G8 n
+ T B# g$ W, } C3 e/ T" w* p X0 J- Q. T& o9 ^
% o3 |2 h( F& _在Main函数中while(1)之前调用一下即可,添加定时器启动函数:
\8 N: R/ z! d. l1 h" _0 f! t% f+ u: h+ a3 A7 u8 v
% x) E6 N' [5 P3 P, G
- HAL_TIM_Base_Start_IT(&htim2);
复制代码
7 E0 d& {7 J0 K N; }! y
" N8 J, R8 Y# j5 I/ ICAN接收中断启动函数:6 K9 k' t0 g( ]. O
: _3 c6 V( E! ?3 h: L8 w* W$ J. c3 ` H# N* s1 P! Z7 I0 l& _
- 2 B; \) [& k6 k( L% O' K! V2 c
- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);; y. S$ R/ Q% j# N" b l$ F2 j- t8 `# U
- /* USER CODE BEGIN 2 */% g* _6 g& N' ^# e8 a
- 9 L# x& B3 T6 i0 Y; R6 W
- Can_Config(); //Can配置信息
8 E, H- C3 c3 H4 G - HAL_TIM_Base_Start_IT(&htim2); //定时器启动
$ b- J) r% z6 T& L - HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);//使能Can接收中断! k C7 |" O3 I4 w- A/ F$ M2 Y% x
- 6 ?3 I% R1 b+ ?2 q
- /* USER CODE END 2 */
复制代码 * i$ J H8 M8 W- P" X# m
8 y; A4 U& {5 ]
打开stm32f1xx_it.c文件,找到如下函数,添加接收中断启动函数:- n9 y, L# x1 W% |3 c$ _
! U: y9 G) b0 B6 b7 O
( B7 {! k7 w; Q R& S8 h4 y5 w3 j- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);
复制代码
* k4 q0 v! ?! K$ P0 Q/ f9 Q
2 H$ ~/ o) n3 i2 m" `+ I6 g 注意:接收中断启动函数使能一次只进一次中断,所以中断退出前要再次使能。
' ]# `* T# c5 ^" l1 i" [. G! z
2 V7 Y7 N& m+ c2 T6 n: \
* ~9 b* s- b9 q) A# H
' \; k* Q8 K [: h- void USB_LP_CAN1_RX0_IRQHandler(void)
2 d+ |6 ?2 r. m6 Q& N - {
- Z6 p* L+ U, n) z* z! ` - /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */# l. n; a* b) f3 v9 b) y
-
1 n L2 R" S, z. a# K - /* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */
" e3 {% y. t9 D7 g0 B$ S' R - HAL_CAN_IRQHandler(&hcan);4 z* f3 t! m# L, {0 Z
- /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */
8 a6 o& L0 T0 N7 ^0 d - HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);//ʹÄÜCAN½ÓÊÕ4 P* ~( q) n2 j
- /* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */. _$ q. S8 o' I( N# Z% O+ M- w9 m7 E f
- }
复制代码 , z! W( B5 H' L
1 G5 Y- P& ~' M8 q B9 u
添加CAN接收服务函数:0 ?0 k: n- n j t+ ~* E
; f9 O' k3 c9 K* y x
9 S3 _0 I2 e& ^, m# r' U& X2 O, Z
, v: V! C1 b% u/ H; w! i6 s# E+ Z. V0 H
说明:该函数在stm32f1xx_hal_can.c文件中已经有定义,它的定义方式如下:
: a7 r( b- h3 ]$ ?4 j& o1 F1 P' }
7 `0 y3 _: Q6 {; c) _- __weak void HAL_CAN_TxCpltCallback(CAN_HandleTypeDef* hcan)
复制代码 , Y9 h! r$ p/ r( \9 p+ ^
5 }% v! N6 h& w4 ]8 V
函数前面的__weak关键字意思是如果有同样的定义,先执行没有__weak关键字的函数,所以当我们定义了HAL_CAN_RxCpltCallback函数后,编译器会先编译我们定义的函数,而忽略系统定义的该函数。当我们没有定义该函数时,系统会编译带有__weak关键字的函数。( d' R1 R: z) |8 ?) h
- Z& l+ B' }1 S# d0 O, s
1 W6 N( y1 G; o: m5 X5 v6 o
- 8 @: e+ d, o0 G/ e+ d
- void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)' t0 F4 z$ p; k# T9 T- ?; A
- {
( e5 O9 i; X5 z1 C% ]% @8 X2 } - unsigned short int speed;0 r$ i1 Q5 f( R2 a) v4 ~6 N1 u
- switch(hcan->pRxMsg->StdId)* }' T, b: b- ?- Y+ P3 `
- {//根据ID处理数据
2 F9 d2 o, c$ s8 X - case 0x123://# u1 _: s) E z( C& b/ n( X4 L
- /*在此添加数据处理逻辑*/( m# Q! q" X0 K, L d
- break;
/ l3 Y: `+ j- A; F( Z6 d/ i - default:. b2 O. n% \4 X2 h% ]+ q; z8 B
- break;6 Y& k8 I$ [+ ]6 \
- }7 M' k0 {! C" v. A; `
- }
复制代码 , }) J" c# E' V. k. q& j, N" l
& M& d+ O) t7 Z& ?0 G- L, ]: Q2 {
添加CAN发送函数:
1 S" G, x+ |8 Q. h- L
' i" V' c( j4 M b& M: z- F4 y6 N1 W' D; {+ `6 x
- /* USER CODE BEGIN WHILE */, m+ W* a7 F! W' Y2 G$ c' b; h
- while (1)
$ U, T3 Z9 X" e+ i4 k! e4 v& ]3 c- ]+ m - {# ]. [; L) d# G1 [2 R* @7 h
- HAL_IWDG_Refresh(&hiwdg); //喂狗函数
: _* k0 `. V4 V1 ]: X8 C -
+ Q: R" y V$ ~% ~ - if(Can_Trans_Timer == 0); U- V6 `5 Q+ C5 k F# [0 Q
- {//每100ms发送一次数据
8 N' u [- E$ c- {4 t - Can_Trans_Timer = 100;; y+ a' Z- l* G+ X I* {
8 L8 e$ M4 ?8 P' k ?$ |$ X7 p- c
* ?$ Y: M% ]7 R8 m- j: e- hcan.pTxMsg->StdId = 0x123;
( p5 @; ^9 v3 P2 U: c -
/ c" l! _" t: g. Y1 X - hcan.pTxMsg->Data[0] = 'C';
* x# i" {. ?7 i - hcan.pTxMsg->Data[1] = 'A';' l3 Y+ A1 e5 c8 n7 m5 g4 f' c
- hcan.pTxMsg->Data[2] = 'N';
6 n; h1 F! g3 K1 V4 a! _ - hcan.pTxMsg->Data[3] = ' ';8 S& P9 q& d! j; g( k: e
- hcan.pTxMsg->Data[4] = 'T';
+ M" P# o: q0 t4 H - hcan.pTxMsg->Data[5] = 'E';
, W7 K# e4 F6 P" V6 c4 \ - hcan.pTxMsg->Data[6] = 'S';
* p/ [1 h, \+ D2 g" `6 | - hcan.pTxMsg->Data[7] = 'T';# F# ]: e# a* s* }8 ?
- ' F8 ^2 J) A& U2 e
- # ~& A! A ?. O* q4 J- \; }
- HAL_CAN_Transmit(&hcan, 200);//发送一帧数据5 J6 z1 b+ H, ~- X6 [
- }
E8 ~, C; b% i- [1 k' M/ G1 Z - }) [& F* }; h! U# z: e
- /* USER CODE END WHILE */
复制代码
& l7 r& c1 S- P* N4 K, }
0 M- T4 k; u2 Y9 }在定时器函数中添加定时器代码:2 m! v1 W w. H" V
2 g; i% P5 O9 y. X+ j8 X4 _/ o
& n% r0 q* }* D
) B' A0 w, f% D6 |" n, u- void TIM2_IRQHandler(void)
4 l8 C& |& z4 a - {
$ d3 W5 }' T8 ?' ^ - /* USER CODE BEGIN TIM2_IRQn 0 */; p" E2 T: f k* j
- if(Can_Trans_Timer > 0) Can_Trans_Timer--;
5 I6 I. N% R# b* q z - /* USER CODE END TIM2_IRQn 0 */, h9 C/ i! }0 [& H2 q
- HAL_TIM_IRQHandler(&htim2);
$ Z$ z. o1 S3 d# H5 Q7 @, H! b( i - /* USER CODE BEGIN TIM2_IRQn 1 */8 k# v; b2 F$ y4 J Z( X
0 Y0 Z; V6 b6 x# n) j6 h$ j, @$ S8 h- D! Q2 t" v3 l3 p. i5 h
- /* USER CODE END TIM2_IRQn 1 */3 I) }3 C8 c* o( n& G" |
- }
复制代码 ' O) @2 x% k, B, @9 |) x( M3 M- n2 z
# D7 x/ o- M' \2 G% J" }/ q 说明:在往工程中添加代码时要注意,不要任意往里面添加代码,要在注释着USER CODE BEGIN的地方添加代码,这样在重新生成代码时才不至于将已经写好的代码覆盖掉,如下
/ d" {& t% H9 k3 W' h9 r5 J! _9 }5 n0 {9 ^' K
+ e6 y6 Q( q0 O
6 g! y" k' k! b& {/ t4 V. i: K; y8 h- /* USER CODE BEGIN Includes */
复制代码 8 Z, l1 w y# a4 ?2 {: X
9 L& Y7 {& ?4 o# J+ Q6 J2 G2 j F- A2 U9 P/ I) h1 Z4 n
! Y- N0 s8 d. V, P% ^/ ~- p7 l) V2 i, s
0 w9 v3 v4 q Y4 E, P) k; c/ p* n! J6 d9 B% z3 ?8 D
0 r" |& r- N. y* m& L5 u0 N n4 O" d4 a3 L5 o2 _# @, `
0 i( N% c9 S& k! A& A- L* o
6 [& R4 e7 v* S
7 C& F- d4 S3 c+ u
: F* w; @, Y& @6 ^
7 Y8 W* \% G* i" u5 t |