本篇文章我将针对PWM控制电机与编码器读取电机转速的STM32CubeMX配置过程进行详细的讲解,让准备学习HAL库的小伙伴能够更好的理解STM32CubeMX如何配置。
* t: E9 ? S. {% `' |- V% q1 t7 x) C& D
1、硬件准备0 g/ N: H3 I& V% T# U! h. P' {
(1)所需硬件* b7 c, q- [! k- p) x1 R: X, e. ~
& U! o' }3 k8 w2 `5 U+ Y( ]/ s芯片:STM32F103RCT6
; O/ {1 C1 x3 ~7 l驱动:电机驱动板或TB6612(电机驱动芯片)
7 }- a1 f5 l+ n6 v电池:12V Libo电池
3 \$ ~, [. |2 ^1 K2 v" S电机:带编码器电机7 r3 c w( z" \/ X4 Z, G4 \% \# e3 u
轮子:我用的是麦克纳姆轮
" |. }1 E1 z, u; L(2)硬件连接:) R: m# ~* J9 s/ j1 ^9 R; N7 a2 @' X
8 ]" z% ~0 w# V F+ a, _, wPA8 —— 电机驱动板PWM1
9 T, X' ~' `5 b# B* c/ \' d5 jGND —— 电机驱动板PWM2* x" L% d$ ?; A8 l% _& F
(这里若要反转则PWM1接地,PWM2接PA8;如果在小车中两者都需接PWM引脚,正转:PWM1给一定占空比,PWM2占空比为0)
7 M) Z" J% z# i# z' OPA9(USART1_RX) —— 接串口TX
9 U+ ]) q( B3 m( b6 H" gPA10(USART1_TX) —— 接串口RX% H! o$ m) a3 Z+ `
PB6 —— 编码器A
5 m0 C' u" d$ ^! Q4 ^* jPB7 —— 编码器B
3 |- t, e0 o: H k6 L2、STM32CubeMX配置:
( z' |7 z7 p8 Q& |% S. a e; [1.1 所用工具:* P1 F* y9 a' j6 }& z6 p
! a0 M+ G$ ~& H
芯片:STM32F103RCT63 y& y! j. [, C5 G
IDE:MDK-Keil软件
$ M/ u9 \& w1 n9 `) B" eSTM32F1xxHAL库
0 A1 `+ @% q n! [) Z/ A2 U9 k# l( f! Q* |
1.2 知识概括:
) g2 W; f* P" s
: G) I5 s0 n, h1 O SSTM32CubeMX创建TIMx、USART例程& C7 V' e& C: f$ j
Keil软件程序编写7 k1 F0 f: ]4 U5 {
$ Y% |. B$ V: J5 ]
1.3 工程创建
) F/ \3 e9 s- k8 M1、芯片选择 o9 W: I& \, ?! _0 [2 T. U
芯片:STM32F103RCT6(根据自己的板子来进行选择)0 s1 {5 z! ~2 O9 t
+ S9 z& M- q+ I- y+ y6 U
3 a4 ?$ v* P. F. ?0 n
- T8 Q% d( t% y! X: p6 N
2、设置RCC+ W% |) k1 L8 _/ W @/ u* [5 T
设置高速外部时钟HSE 选择外部时钟源
3 ?. p' T! |3 b5 O
+ i- k$ Y4 X S4 h2 |4 e! G
& N: K. }+ _3 l) P( d5 z! X
h4 q" F1 d8 {% n
3、LED1配置) E# |: Q- s1 p( F S
我使用的板子LED1引脚为PD2,初始电平为高电平,目的是通过观察小灯的亮灭判断是否进入定时器中断/ B" D$ K7 i- h8 x, o* z& ~( A6 L8 L
/ R( i& ~0 H3 Q5 _% z
7 x! e; @: m4 _4 f3 w( K7 e* d: J5 b
4、USART1配置
$ i4 W. D# Q6 |- o 异步收发,波特率默认:115200 Bit/s2 Z4 H% ]3 g) i& M7 y
' `2 n( U# i; K$ v& @
0 d9 r& Z' Y$ g% ]
9 i5 `. A" P. c' o6 U( Y: T c, I5、PWM配置
, h3 {$ F8 R3 v( ?$ E; ?. H 使用定时器1通道1和通道4(TIM1_CH1和TIM1_CH4),频率10KHz
7 v$ d) R; E) e. F8 L4 ~
/ H0 m) _% {2 Q7 C
- W' \/ D' W" \' ]6 p; ]1 `
! j( R* p, X1 H' a! ^/ \( S6 b1 f# k& K3 v
6、定时器中断配置% ]2 q. h3 ?/ G5 b; b. `9 F# d
使用定时器2(TIM2),周期设为10ms,即10ms进一次定时器中断
/ h( ]) c) d1 k
4 Q4 e2 f% d1 u. r9 z& X: g
7 X4 H5 s# C6 l" C$ U' {. o; ^" e6 b# K' v3 l2 y& j
打开更新定时器中断
% T" p/ w) T- } r8 ^6 M/ m' p$ R3 \) V7 |: H# f, V. g6 f) |2 `
) l* e, D1 p( Z6 g' |" u8 K+ q% N
# [" x9 G! ]- ~% d" j7、编码器配置3 t5 m& H$ B0 `3 {
STM32自带编码器配置,使用定时器4(TIM4_CH1和TIM4_CH2),打开更新定时器中断
; n3 E5 A' U# r8 L
2 T& p ~; g- p3 Q$ N+ A
/ w _$ N$ P# X, }) W
3 j& G' }* E, u! Z. }8、中断优先级配置8 q3 p8 W/ b. s1 D: _5 ]3 m- T
因为编码器中断要发生在定时10ms中断内,故编码器中断的抢占优先级要大于定时10ms
5 M# Y C1 {# _/ M6 `5 H4 @0 }6 W+ e6 X, y0 w" A) E
0 `6 [* q9 [* I. i
- y6 l5 e1 @' I8 t7 p
9、配置时钟
0 y- s% C1 _4 K4 x, z* w+ T% @( R F1系列芯片系统时钟为72MHzs0 _, ~' F7 W) y8 W; C+ L( B
2 |- h0 \" N- Z% D" o8 I
7 t" {5 I/ N2 m& }! [; |" l
8 P! y7 ` Y1 C" w10、项目创建最后步骤7 `; F. A0 f8 X" l7 H
5 Z* w: y1 w# M' V8 u设置项目名称
, Q/ @: O& b+ d* k1 `+ l) F' I0 m选择所用IDE8 o4 j- U9 T0 C
) e% Q/ c6 u$ k/ N9 x
Z) {: e! g* z3 ?& F9 e( f( r
( f. B' J% n/ [- f) b
11、输出文件
" _3 @3 {; y, Q2 c. p R
2 V$ F! c; @; ~1 h6 Z②处:复制所用文件的.c和.h
" Y9 y1 U( u/ ^/ v0 N# l4 @③处:每个功能生产独立的.c和.h文件( K8 ]# \$ N- b T- Z
7 z, W3 ^/ E5 I) {- M k7 Y
" ?3 D! v3 i N G! j# Y0 S9 Q* W1 W* |# q5 |
12、创建工程文件9 r$ P; ?; b' Q% N; `. c% g
点击GENERATE CODE 创建工程# V+ j$ Y4 F* N' J
/ [: i6 l& t8 K0 a1 d W) f& ?
13、配置下载工具
& q6 J9 H2 F% B8 X& N0 p' j4 M+ i& P' \ 这里我们需要勾选上下载后直接运行,然后进行一次编译
) J# ~7 [9 {. |7 k$ ~2 c$ f! U, O6 {5 N2 F" t5 ^; Y K
( Z, v2 d) x& y! j
: h: m) a( g1 a+ N: J) k* ]
3、STM32源代码: t4 F6 k5 T o9 V1 g- }
(1)下面的代码之前有用过hal库的小伙伴应该都很容易理解,小政就不过多解释,有不懂的地方欢迎在评论区下方提问。在main.c加上:
1 n3 {' t. n, e+ b7 W* Y* H- y- n
- /* USER CODE BEGIN Includes */
4 n* b+ w8 g" N2 e0 { - #include "stdio.h"
# Y1 ^. K" H* Y4 ^' a- R - #include "math.h"
" i7 M8 j% H( ?- k; P - /* USER CODE END Includes */
复制代码- /* USER CODE BEGIN PV */
' N! U7 H. y! Z( i0 v8 o - unsigned int MotorSpeed; // 电机当前速度数值,从编码器中获取
% k+ o7 n3 Z5 h2 | - int MotorOutput; // 电机输出
3 B5 d7 x: d+ ]0 X/ H1 J - /* USER CODE END PV */
复制代码- /* USER CODE BEGIN 0 */
" t% E$ \+ J" g& T. j g - int fputc(int ch, FILE *p)0 z5 Y( U9 u O. q+ L4 G1 g7 ~& q" S' [
- {% f1 \' C- l, Y! z R7 i
- while(!(USART1->SR & (1<<7)));
. S$ i2 L& E1 d& ^5 x - USART1->DR = ch; E8 ~* P+ _) k2 O! z2 }! x
-
8 J, ] e2 c m. _ - return ch;5 h& I" k5 u4 |1 P) |. q
- }" R3 E5 W0 m# I
- /* USER CODE END 0 */
复制代码- /* USER CODE BEGIN 2 */
1 M) z0 {* C. H1 ~0 D4 m$ E/ h) ] - HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // TIM1_CH1(pwm)
d) B* _' G+ f5 Q( z& K - HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_1); // 开启编码器A# _! } B3 `+ A/ k0 u7 ]3 }
- HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_2); // 开启编码器B G4 m. ^+ n1 h! l j9 [3 z" |- j7 ]5 ~
- HAL_TIM_Base_Start_IT(&htim2); // 使能定时器2中断* G8 R4 ]$ Z# f: ~+ k+ T
- /* USER CODE END 2 */
复制代码- /* USER CODE BEGIN 4 */# e4 S2 Z- |: Z$ m. j+ V* x4 K v
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
0 z2 `8 m1 V+ E) b0 a- o% S - {5 O3 X! H% E- M9 U( |- S
- static unsigned char i = 0;/ T# b. O9 T$ `4 ?
- if (htim == (&htim2))
7 r+ {# l+ B( h# |) a [4 | - {7 p" m) _1 |! F+ k. n; t
- //1.获取电机速度. R) [" w+ O3 O8 `+ _
- MotorSpeed = (short)(__HAL_TIM_GET_COUNTER(&htim4)/18); & N! O* S/ A8 H. `' H$ N8 c
- // TIM4计数器获得电机脉冲,该电机在10ms采样的脉冲/18则为实际转速的rpm, y9 x# b! H1 s I8 b+ A$ O! r" s
- __HAL_TIM_SET_COUNTER(&htim4,0); // 计数器清零
, b+ n/ J) s8 j1 Q# F8 a# j; z1 S
& ]" h. E( N, }" ?- N
5 a( B( k" M2 j+ k! _* i' z2 s- //2.将占空比导入至电机控制函数
( N7 p; W" h) } V7 u% r6 u" [ - MotorOutput=3600; // 3600即为50%的占空比! y$ f" [" O$ [/ [( m% V$ y" J3 @
- __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, MotorOutput);
% Q. N8 O, ~, |5 o - MotorOutput=MotorOutput*100/7200; & Q: D* R7 i7 f* i# ]% m1 k
- // 占空比(按最高100%计算,串口打印)$ B7 x/ K5 F Z `5 J# J
- i++;
$ B, o& b+ d5 z - if(i>100)9 m; P2 U, W& B& T5 E
- {$ X) w5 S- P3 v
- // 通过观察小灯亮灭来判断是否正确进入定时器中断
- N9 q" B" z- q0 Y7 b3 B: L1 g - HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_2);9 Y% a, e' X7 c" U
- // 打印定时器4的计数值,short(-32768——32767) z" L, R/ ]5 _ p6 j
- printf("Encoder = %d moto = %d \r\n",MotorSpeed,MotorOutput);
4 q# k- T% [$ b0 ]$ x* E - i=0;
. Z/ a" m: D2 I, X - }% h2 D' }3 Z' j# P3 X" R, i
- }0 L' n+ z: M! Q! I' G5 \
- }1 s3 u6 ?. g" I
- /* USER CODE END 4 */
; A" e2 ~3 A/ z2 x2 A+ u( B( U' n# w
复制代码 : `; L9 d' x' I
(2)烧入STM32中,串口展示:
5 F# C/ B2 B y$ ^& D
% {0 {( N" G+ h: H1 a- s# B# n
3 M0 |# s: D' l0 p4 S
# M' B9 I) s; D" [" ^: N% E" m$ P(3)实物展示:# j9 o: F% `5 ?, v9 c4 |( n
/ W3 z. G- ^) B2 ^ ]- e D3 f) I a; q
, T9 y! Z) `8 [+ o" p2 N; [: J6 G! J8 `! D* A! h) j- O
* }4 T7 v! h8 y, _9 X
4、总结
" |! ?, h9 T7 T0 U9 E 以上就PWM控制电机转速与编码器读取电机速度的HAL库配置与keil编程,后续会推出利用PID算法闭环控制电机转速(HAL库)) y3 W. k1 L( o8 W j
- ~4 b2 N0 ?4 j, N5 c* M
/ Z- h2 V8 L4 t! x |