STM32-CubeMX-实现CAN通讯
/ c: d6 X a0 d K1 p0 @/ ~' k6 P) }; h
首先要安装cubemx跟Keil5两个编程软件,然后打开cubemx软件,新建一个工程项目:5 U4 B- `) i" `4 Z2 X l, S' T5 _
7 j" O+ V2 {6 i6 ~. M7 a 输入CPU型号:; Z5 p/ ~2 U% ~( L7 _; y+ G- f" z
' P- M0 }& k a) Y3 {6 T- r9 M' s
在右下角双击CPU具体型号:. U- S$ z- U/ Z/ B( ]# o' W2 R
( k; m" Y! Q1 k; D6 ?* ^- {& L 稍等片刻会打开如下对话框:
* F# _$ ]) M5 w: }& G4 k6 A; k
3 J! I# E2 x6 K5 o8 f% _# ~2 P
首先要配置系统的调试方式:我们选择SW方式,' x( L! p# z$ d m; G9 Q. L
, S( M) \ K9 D 然后配置晶振源,这里选择的是外部晶振,8M,
5 B) o- I. H4 u. n. f- u
/ ~# X1 M: U6 G" s
使能看门狗,% @" R8 F1 ]6 I8 g3 y
) w4 l2 i) I! Y 使能CAN,) l8 ?/ ]$ G6 t* [
' l7 M1 q5 B1 t1 b0 Q, I7 N9 N' L 使能TIM2时钟源,采用内部时钟,
& @8 T1 g# R% W* }" R
- D5 ^7 `1 ~+ u/ s& C' o; Q
配置完成后可以看到单片机管脚已经做了配置。+ ?. q1 i6 u [ R3 y( j
* s+ z1 |' A; j, y$ ~ 切换到Clock Configuration选项卡,配置时钟周期,这里配置为8M+ h4 C) [4 N$ s
- O. H* s6 R4 r1 i- o" } 切换到Configuration选项卡,进入CAN配置选项配置波特率如下为500Kbps。+ `9 c+ F& G$ `6 \2 ?
" v! C, n. h) J: C
配置接收中断。2 }+ @7 f. D; D: p
3 [5 I4 i" q, ]1 Q9 c 配置定时器如下为1ms定时器。
* Z3 f% K9 t y3 v, |" n) V
8 t6 z7 L) u$ @& l& c( A% X& y 配置定时器中断4 p3 s$ X% E1 B3 C! \" `. K3 Y
( O8 }, ]' R% K! s& @) |. g8 y" L4 A 配置完成后点击保存,然后点击如下按钮,生成代码9 T# W2 s4 V$ V3 g1 L D: x
4 ]$ q8 z4 e3 ^) j# p9 |
填写工程名称,路径,编程软件等等
9 N2 s) C* R' J6 l8 q
9 _# E; \1 t2 t+ d1 V 选择生成代码的方式0 d: M3 F G5 Y6 V) t" l
- [; s M' N* O1 b# i/ a0 W0 V
等待……
) I# z4 q8 R$ V+ ~4 s4 d 之后点击打开项目。代码生成部分完成。
/ y* F1 L$ V' S L
, I8 {# k5 A/ K9 o* c 添加一些特殊配置和逻辑代码,添加CAN的配置信息函数。$ i' A; c7 h9 B: q1 e% X
0 N, `+ j0 }/ E6 [$ p' n* P0 k% Q' f% ~5 D& r! X) c
- void Can_Config(void)
* ^9 a7 o n- z' C& f - {
& [$ y0 X# x9 H& ]$ M* u* G% f7 S - hcan.pTxMsg = &TxMessage;
! E% j% H" A5 V7 J: E - hcan.pRxMsg = &RxMessage;, O& }: N, g( C1 l
- 7 S. c, A$ q. w8 T/ A$ D9 u0 ^
- /*##-1- Configure CAN1 Transmission Massage #####################################*/, b( K4 |9 ^7 j6 s
- hcan.pTxMsg->StdId = 0x123;
6 A0 l. [3 ~3 j. s' l* j - hcan.pTxMsg->RTR = CAN_RTR_DATA;
! J8 `- u- }4 e- A - hcan.pTxMsg->IDE = CAN_ID_STD;
3 d1 F! U; Q. X! u2 J+ E% N - hcan.pTxMsg->DLC = 8;8 L6 n% o3 J. R6 f8 `
- , u2 f5 O2 M9 i5 x" w: [
- /*##-2- Configure the CAN1 Filter ###########################################*/% {4 L2 R0 e( K3 G9 W2 e$ }% X
- sFilterConfig.FilterNumber = 0;
( G. z0 x4 f1 d2 K* h) i1 O7 k3 V- n$ } - sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
1 ^" t! p" Q9 l A7 G - sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;0 l/ I! t { w4 e) [) ?
- sFilterConfig.FilterIdHigh = 0;
0 ~' s$ i9 Z! q7 L. ? - sFilterConfig.FilterIdLow = 0;; d( i4 g" [/ Q ]4 p! f& K
- sFilterConfig.FilterMaskIdHigh = 0;1 _/ B: z; V; y& J/ ?
- sFilterConfig.FilterMaskIdLow = 0;
, o2 l9 u" I9 W" [ - sFilterConfig.FilterFIFOAssignment = CAN_FIFO0;' ~" Z. b9 t. `9 }
- sFilterConfig.FilterActivation = ENABLE;
2 `! t3 |9 G( } - sFilterConfig.BankNumber = 14;! W7 M! J! ~8 Z' P
- HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);9 Q6 J) v3 J6 `5 e% M1 ?* u$ E, e
- }
复制代码 2 ]# d- J! T9 [7 R9 ^, Z; f! q6 M
) G3 B( f5 I$ q8 g4 t
; N+ y/ p4 m% p$ @
- |& w7 }' g' O3 }, y在Main函数中while(1)之前调用一下即可,添加定时器启动函数:: X2 ]5 z8 d0 t7 @
0 R7 {* |" y1 s& D
2 y) Z* N* q1 l, G- HAL_TIM_Base_Start_IT(&htim2);
复制代码
9 J' h/ [; }- u3 _. S1 i/ k( f2 {0 t( e8 A- a- K+ C
CAN接收中断启动函数:
7 q- r, [2 B2 B
! {) o! x/ F/ {7 K/ T! _* W
, e% D" f5 N9 X" g+ u
( s5 ]" n- E U- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);% ?0 U, q- E- R. f
- /* USER CODE BEGIN 2 */
: b e5 G, a; X9 F4 i: H5 d - / K4 o6 F& u9 k" e2 @% ]
- Can_Config(); //Can配置信息
% i- U3 a% d- O# H6 Z! s8 W - HAL_TIM_Base_Start_IT(&htim2); //定时器启动
, d# ?" _0 [5 F/ [. c z - HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);//使能Can接收中断
- k _7 L9 r. n/ n5 O A -
8 \1 ~& e' c& {' x( J - /* USER CODE END 2 */
复制代码 ; Z8 p5 S% w, H
: y3 a1 @9 U( x+ o# Z+ [ 打开stm32f1xx_it.c文件,找到如下函数,添加接收中断启动函数:
8 |6 F6 z, N& R, J8 ]
1 \1 X4 ^' a ^9 R
. L% m2 b9 _4 n8 V: b: I- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);
复制代码
% X0 L. L6 W M% A7 f, Y' G y) E; j6 ]8 [
注意:接收中断启动函数使能一次只进一次中断,所以中断退出前要再次使能。 T) c) @' ^5 d$ |+ S
/ n \0 ]2 V0 \" I8 }5 h) f
! R) }( ~; G+ A$ E- , c4 y" P$ P2 W0 r7 ~% p
- void USB_LP_CAN1_RX0_IRQHandler(void)
5 J {- w* ?* }/ k% q - {, r& m" u. Z% m+ Z [
- /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */
/ P9 }0 y5 |- }5 l/ k -
# S% V6 V3 A' T7 }+ P - /* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */
) ^9 M/ ^" u' d0 c' q - HAL_CAN_IRQHandler(&hcan);
) f9 I9 \" F+ |" M7 J. O* ]# N1 } - /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */7 s' K/ n2 G4 W+ B
- HAL_CAN_Receive_IT(&hcan, CAN_FIFO0);//ʹÄÜCAN½ÓÊÕ( J i% p3 ~% m3 m2 L' k
- /* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */- y, u# T9 M& ]
- }
复制代码 / F" _/ c4 g& ~6 i# _
: `8 o+ n+ Q z7 g; O4 R
添加CAN接收服务函数:
. U# G$ `) \& m. P* h. N
& m# E: \& O9 ?9 q2 C0 q7 a2 Y/ Q8 t3 \4 K4 r( k$ R, T: f7 U& V* Y
3 c6 H) N+ C1 \0 S& ]0 A% v
, h0 q$ q/ H; ?" A$ Q: U& C% |+ R 说明:该函数在stm32f1xx_hal_can.c文件中已经有定义,它的定义方式如下:) W" v, N( c3 h" R) p
" @+ {! K" o& M% B8 y
8 i! x$ F3 J) i- __weak void HAL_CAN_TxCpltCallback(CAN_HandleTypeDef* hcan)
复制代码
, [6 r' ^. p! M( b8 T$ m! w% {- t+ c
函数前面的__weak关键字意思是如果有同样的定义,先执行没有__weak关键字的函数,所以当我们定义了HAL_CAN_RxCpltCallback函数后,编译器会先编译我们定义的函数,而忽略系统定义的该函数。当我们没有定义该函数时,系统会编译带有__weak关键字的函数。5 E( t" b& y8 `. d3 v+ ~, W
9 [5 Y" {% W6 J1 t( u* M
: e# }: |- H" i7 z( l3 M. N
/ J: b' q3 V* d8 ]7 w+ l! H- void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)1 q, ] u6 z2 D8 T, q
- {; {- `" j9 B& B) ~4 U; h9 e
- unsigned short int speed;
, A& _6 N8 e! u5 ?7 P - switch(hcan->pRxMsg->StdId). y2 \6 {, t$ D4 E ]4 M. f! N
- {//根据ID处理数据9 }: S7 w9 B% p
- case 0x123://
9 o# ^' f5 l- p7 r. ?! O - /*在此添加数据处理逻辑*/1 Z' q* Z w. ]; v5 D) |8 u
- break;8 h$ W1 \) }& d. R& }1 z9 n
- default:
* V0 r. Q% x; D+ {! k# ^% H - break;
* T8 A: W$ a1 A2 g4 j" q9 ~ - }7 B4 w2 c6 B' m8 A
- }
复制代码
# y9 ~# S+ o2 ]3 T- ?& k8 s6 |3 b8 N3 \- W4 k# ]" h3 @
添加CAN发送函数:$ b; |5 y- y5 ^" d V
+ s- Q/ ~$ O9 s( Q) _0 [. v1 ~$ V, M4 [( i1 p6 c
- /* USER CODE BEGIN WHILE */- Y( `8 s8 G! F
- while (1)
, ^, D }/ n7 ^2 p \& h - {9 s6 ^$ i" f- |: ?- j
- HAL_IWDG_Refresh(&hiwdg); //喂狗函数3 K, |2 y6 D: o4 n' t( O
- 5 h" H1 M; P G$ H7 ~$ b1 b
- if(Can_Trans_Timer == 0)5 m8 P& G, {& }9 v1 k: t% L
- {//每100ms发送一次数据( E* P, P7 q7 ~/ P; N
- Can_Trans_Timer = 100;) t- }' X/ u9 f4 ]
, E. F; u: }# Z4 h. a! n6 {+ ~
! p d+ T8 F+ Z5 |+ r+ x! r- hcan.pTxMsg->StdId = 0x123; f& c5 _/ h$ S6 o. x: \# f! v! u# F
- ~6 @2 ~5 ~+ h' ~
- hcan.pTxMsg->Data[0] = 'C';4 _) C* a( g) R( k
- hcan.pTxMsg->Data[1] = 'A';
) l; n b+ d+ K; s8 [- A0 V - hcan.pTxMsg->Data[2] = 'N';
! L9 ]9 ?* k2 k3 { - hcan.pTxMsg->Data[3] = ' ';
7 `8 E1 F6 B1 E - hcan.pTxMsg->Data[4] = 'T';2 j K8 a+ O& v& O: C8 r
- hcan.pTxMsg->Data[5] = 'E';
( h: ]# c3 O1 S+ Z9 { - hcan.pTxMsg->Data[6] = 'S';& p% o0 z5 v9 v$ i( w8 Z% @9 n
- hcan.pTxMsg->Data[7] = 'T'; t- {' [7 U* G1 p/ A
- 3 N* _6 Z' e( L& Z) {& K
5 q# z; f4 w; W# D i. S- HAL_CAN_Transmit(&hcan, 200);//发送一帧数据
+ F) v5 g8 |9 a$ h! H' u - }
2 b, C- K! r6 v; x, S# ~$ u3 y# f - }6 D7 ~5 z3 C, D
- /* USER CODE END WHILE */
复制代码
2 C6 P( f4 ^8 |* x0 `6 i: ^3 P2 R
在定时器函数中添加定时器代码:4 J" A: \0 e. |7 i5 D
G& A3 L* f2 d8 B( \9 Y% }
6 I4 W4 _ P$ O0 r" }
0 G1 z1 U0 s, }$ I b# D9 |3 ~4 n- void TIM2_IRQHandler(void)
3 |* g* s4 Q8 ], Z ]9 t3 J& k. k) b - {
7 l. `3 r# J- k - /* USER CODE BEGIN TIM2_IRQn 0 */
, z# j8 x% H# i# s7 o; h - if(Can_Trans_Timer > 0) Can_Trans_Timer--;" U; G; _1 I8 j
- /* USER CODE END TIM2_IRQn 0 */9 v4 A: _" R! B3 X
- HAL_TIM_IRQHandler(&htim2);
8 l @ U! f- ` K- { - /* USER CODE BEGIN TIM2_IRQn 1 */9 c' F. w( J/ x" v* d U
' }% U% w. {' K9 Y- , I; [; k) r( g% P3 s5 m% [+ \
- /* USER CODE END TIM2_IRQn 1 */
2 K& d2 j8 \$ E9 I( a - }
复制代码 4 Z% Y( w" r% s, P q5 e
" t8 u& d: h5 _& c- Z
说明:在往工程中添加代码时要注意,不要任意往里面添加代码,要在注释着USER CODE BEGIN的地方添加代码,这样在重新生成代码时才不至于将已经写好的代码覆盖掉,如下: c K9 Z7 v/ m+ o2 ]! N
2 ?- O) C1 }5 a; [7 M
! `; }4 t- F$ M& {- [/ T4 Q4 E
' P9 D; w k3 c8 j W8 z% p6 T- /* USER CODE BEGIN Includes */
复制代码
$ @8 j- Q/ u" l# G/ @/ a: @
: w- p* b8 W9 c: Y" O' @7 M: N% S: H
( W! Y1 Y. u" N% H
8 D6 ?, K# Y& X7 a( K8 l- Y: v# W. x0 ^6 T/ r
1 F; i; A9 M/ |3 n$ C' C
$ R6 P% j7 f0 ?; T
: l# ?. x8 |% \7 j7 F6 {9 D7 t$ @' R
2 g' q6 r/ X6 o& S5 Z4 c: h, |! A- T& s# T! Q6 b- q, m; P
; Z! m; H( R9 b2 {9 |' T
! H& s, ~! _1 F
) {( v- Z6 g$ l2 h* G |