项目需求6 X% _8 P6 A; `9 m$ U* C, c
在制作一个使用全向轮的机器人底盘,对于全向轮,电机的精度是影响效果的重要因素。所以使用了步进电机,使用步进电机的优点是可以不使用编码器,开环控制即可达到高精度的效果。
% n" s: h, s! g4 f2 o* w5 `9 \0 c% l2 p3 d2 b
" w8 B: \" c+ p1 {调整占空比或者调整周期
4 `+ S. |$ k. W4 i! V; N 众所周知,PWM有两个重要参数,周期与占空比。
2 H- O5 | L2 x4 B 步进电机的运动方式是,每收到一个脉冲,就旋转指定的角度。因此影响电机速度的唯一参数就是PWM的频率。以下附图两张来说明调整占空比与调整周期的区别。
4 U0 q6 p, g% S% c
, z% [6 j, a) H1 b/ Y/ `* G3 ~: ]6 H) V5 h5 V
周期固定,调整占空比8 }3 |- t6 ?: C) @# S5 S
上图中有4个PWM,它们的周期是一样的,不同之处是拥有从80%至20%的占空比。可以看出,无论占空比为多少,在1s内,它们产生的高电平的数量是一样的,即无论占空比为多少,PWM的频率都一致。因此也就无法调整步进电机的速度。因为步进电机的速度仅与PWM的频率有关。
: V, H' o7 B( T1 {. o3 f6 o3 l; c# K4 I! \ _ Z& q
/ M0 u9 z0 m0 d0 d. Z9 h& \1 q占空比固定,调整周期
7 c+ S; \' h0 j3 F2 |8 m" q 上图中有4个PWM,它们的占空比都固定为50%,却拥有不同的周期。可以看出,即使占空比固定,只要动态调整周期,PWM就能在相同时间内产生数量不同的脉冲。因此,固定占空比,动态调整PWM周期,即可达到控制步进电机速度的效果。
# D- h0 w; m9 Y6 W( x, P$ r
5 O) i1 P" g! ]$ V, U# C0 d! h# ^/ P2 w* b: `
基于STM32的实现
* S9 z2 W; R, ]4 O 在STM32F1中,定时器具有PWM模式,可以用来产生PWM。但是,STM32的PWM模式,只要确定了时基单元(即确定了PWM周期),改变输出比较寄存器,仅可改变PWM的占空比。
6 O; U1 z. [# O' A8 W, J 若需要改变PWM周期,需不停地改变定时器的时基单元。但时基单元与硬件相关,不适合频繁变更。因此,步进电机的调速,不适合使用STM32下定时器的PWM模式来控制。/ D8 q1 E9 L; h7 L! z
在多方查找资料后,确定了一种利用输出比较精确控制PWM周期的方式。
/ u( c" R" D! T O$ O
# e. B& Y! \! X8 D; W+ p5 z* d0 Y* w9 c# ]0 A6 I w+ ? e
利用输出比较产生频率可变的PWM3 c1 h! q( }) I' j8 {% w2 g- f# ?
利用输出比较产生频率可变的PWM,原理简单介绍如下:
0 z% o& @ {4 V" n# n: \5 z 首先配置定时器时基单元,确定脉冲最小长度单位CK。
_( c1 m, W1 t' _# i0 V% c 然后开启定时器的输出比较,设置模式为翻转模式,并开启输出比较中断。/ ], H. J5 P# O4 f+ u( _
将定时器内计数器CNT当前值,加上脉冲长度X(单位为CK),写入输出比较寄存器。
, H6 [1 Y. i4 ~) {3 v5 o4 w 在X个CK后,将会触发输出比较中断。同时电平翻转。, s6 z2 r9 U1 ] Z* b; w- \4 S
在中断中再次将当前计数器CNT的值,加上脉冲长度X,写入输出比较寄存器。: a) r$ C) ? A
在X个CK后,将会触发输出比较中断。同时电平翻转。
. y& c8 Y; Y* J 在中断中再次将当前计数器CNT的值,加上脉冲长度X,写入输出比较寄存器。+ J2 h5 ] I' `* B! j
如此往复……( i7 F' I. ]+ G
以此即可得到一个占空比为50%,周期为2X个CK的PWM。
- }/ l. R, t8 a3 K7 z) B( {% }# n' t0 d5 ]! L. R$ e4 C* s# x* P% l
) ?0 S l4 {3 T5 t. ]9 \确定最小单位CK,配置时基单元
$ b% @- r7 s# ^+ b 首先确定一个最小的间隔CK,规定PWM的高电平长度和低电平长度的单位都是CK。
+ L0 Z0 K; R: J- Y9 _/ d 即高电平的长度一定是CK的整数倍。低电平亦然。' i$ I0 R6 r0 Z! P7 Q3 I( P) m
然后配置定时器的时基单元,通过CK的长度确定预分频系数。
; U" ?6 \- ]! Q" ~ 已知STM32F103的主频为72MHZ,则时基单元中预分频系数为- v [' o" k8 J* `4 t; b
( b5 Y$ Y$ ?* b% D* {. `% I0 a
& @6 Z6 t# K/ k S) q, @2 Y6 l
PSC = 72M / (1/CK)
* G7 w/ Y4 h0 O( W7 P# _* R1 ^. ? 例如,rtz所确定的CK长度为10us(0.00001s),即可得出方程。# W5 C1 s+ E+ n$ K5 }5 v8 B4 C" W
预分频系数确定为720后,由高速晶振产生的72MHZ的时钟信号被720分频,得到100000HZ的时钟信号。即时钟信号每秒变动100000次。每次10us。同时可将重装载值设定为0XFFFF(16位定时器的最大值)。因为本次使用的输出比较模式不使用更新中断,该值可随意设置。1 C% Y# l/ z7 m5 C( j" h3 Q0 M9 L5 L
- TIM_TimeBaseStructure.TIM_Period = 0XFFFF; {4 R6 X2 w6 F; [% \
- TIM_TimeBaseStructure.TIM_Prescaler =720;
% J; B+ D) z3 f7 Y - TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
! }3 P4 x3 S' ^- `5 t6 k7 r - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;& q& p3 V$ i" O Q0 c5 b( d
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
复制代码 开启输出比较通道,设置输出比较模式为翻转模式,并配置NVIC,开启输出比较中断,配置输出比较通道:
+ h4 B, g% p8 c+ f& W- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
x8 X7 R: o$ y0 |! E9 \+ ] - TIM_OCInitStructure.TIM_Pulse = 500;0 M9 v& @1 c9 K7 A- r k
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
% j- o8 d- e4 C+ h/ s) W+ J - TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;/ g6 r6 I( V1 f& D" X- a
- TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
( G6 o8 h; w. V3 b/ [ - TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
0 V }- O T9 t) h3 A - TIM_OC1Init(TIM2, &TIM_OCInitStructure);
复制代码 开启输出比较中断,配置NVIC优先级:
0 A. S" U* E* d/ d4 v/ t! C
$ [% T1 S5 u& }4 Z) c( U) n- TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);" C! C- h1 `) F( G2 p( P* u: q. [
- NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
$ x! }7 _5 I# }( z0 u - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;) L1 c( v: {7 I0 t m" G
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
0 r1 |* W. `5 ], s6 g - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;5 y9 y& `& j! P
- NVIC_Init(&NVIC_InitStructure);
复制代码 编写中断服务函数
' K y3 H2 d7 ^: W8 u* v* L 在输出比较中断中,唯一要做的事情就是把当前CNT的值取出,加上脉冲长度X,写入输出比较寄存器。
$ w+ q2 s; W& y; i 当计数器达到0XFFFF(之前设定的重装载值)后,再加一会自动变为0.
' s; ~( a' K. ` 例如,当前CNT值为0XFFFF,脉冲长度为5,很明显,输出比较寄存器应设置为0X0004才可触发下一次中断,而不是0X10004,这样会造成溢出。
D) e% A/ w# }6 R, L 因此将CNT的值与脉冲长度相加后,需要取0XFFFF的余数后,再写入输出比较寄存器。
- a z7 U9 o0 i
$ J; W& V& C9 W1 n* s5 s- int t_m=5;//低电平和高电平的长度3 E Q5 I& f Y: }! n- `
- void TIM2_IRQHandler(void)
4 J6 z8 p* S) o - {
2 L/ S; w: y; i - if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) {. g; c# @+ ]) }
- TIM_SetCompare1(TIM2,(TIM2->CNT+t_m)%0XFFFF);
. i% D8 Q7 T: m1 v1 y - TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
: D' R2 i% B, e* @ - }
1 ^ I4 w- `2 a* ? _9 p6 a - }
复制代码
2 U" L# l1 n# P4 j v
/ }& r1 i$ {0 Z" P
8 b- ^7 \7 [8 d% _( N3 I
! P& }# z" R% B/ S$ W; z+ B- f; X2 G! P7 z0 ?
" H* l( S9 x5 I* [# J* a
7 F) v' `3 R8 S! H$ P% \1 ~: S1 v! Q
|