【经验分享】STM32通过PWM控制电机速度
做STM32智能小车的实验中会用到定时器PWM输出,来改变直流电机的转速。分享本文了解如何通过PWM实现对电机速度的控制。
) t4 z x- R$ N1 z% [' c
" T, a( t0 F, A; I! D3 z' YPWM控制电机速度的基本原理 PWM(Pulse Width Modulation),也就是脉冲宽度调制。3 ~( S" m& y: W) x( _' m
PWM中有一个比较重要的概念,占空比:是一个脉冲周期内有效电平在整个周期所占的比例。 为了实现IO口上电压的持续性变化,可以调节PWM的占空比。这也能够使外设的功率进行持续性变化,最终控制直流电机转速的快慢。如何调节PWM波形的输出就是重点。3 P1 p+ W8 D% h& ~' {- p; j
PG4WHG[ZYL_3MR5EN5A0JCC.png (56.5 KB, 下载次数: 4)
下载附件
保存到相册
2022-6-4 21:22 上传
0 w. n8 N4 b& p v K: r- k
上图中的ARR是我们给定时器的一个预装载值,CCRx的上下变化是产生PWM波的关键。我们假设ARR大于CCRx的部分输出为高电平(即t1-t2、t3-t4、t5-t6),ARR小于CCRx的部分输出为低电平(即0-t1、t2-t3、t4-t5),则改变CCRx的值就能改变输出PWM的占空比。因此,想要控制PWM的输出波形,重要的就是如何设置ARR与CCRx这两个寄存器的值了。3 v5 l4 [! a- R, Y! i, f& ?4 Q% T
STM32定时器中断 为了便于理解接下来关于PWM应用的内容,先插一段定时器中断的知识。 产生定时中断是定时器的用法之一,与定时器用来进行PWM输出和输入捕获相比,定时器中断更容易理解、掌握。
; l+ ~" _4 d+ Q A4 Y: E原理简介- ^4 }% l; S6 @; O
使用通用定时器进行中断的原理,其实和开发板Systick定时器进行中断延时很相似(Stm32入门——Systick定时器),即:用psc(预分频系数)设置好定时器时钟后,arr(预装载值)在每个时钟周期内减1,当arr减为0时触发中断然后进入中断处理程序进行中断处理。以下代码为例:& q1 e! M. k: U& K7 _" I
AZ4BSQ~WY4Q4P}9KB5ADH.png (59.08 KB, 下载次数: 4)
下载附件
保存到相册
2022-6-4 21:22 上传
5 [* W8 b: _' `' zRCC->APB1ENR|=1<<1复制代码3 v$ F. h* S$ F' Z7 e# p7 B( e
解释一下上面这行代码,由于定时器3(TIM3)是挂在APB1上的外设,所以要打开APB1,这里的预分频器值psc是来设置TIM3的时钟频率的,如果系统时钟(SYSTICK)频率为72MHz、psc为7199,则TIM3的时钟频率就为:72MHz/(7199+1)Hz = 10KHz //这里的“+1”是手册中规定的。复制代码
& X6 a4 a) J6 z9 D! C4 o 10KHz是什 么意思呢?就是一秒钟会产生10K个周期,那么一个周期的时间长度就是1/10KHz,如果你想将定时器中断的时间间隔设置为0.5秒,那么你将arr设置为5000即可,因为arr每减1就需要一个周期的时间,减5000次就经过了5000*(1/10KHz)=0.5秒。TIM3->DIER|=1<<0复制代码9 \1 V0 w& B5 t6 C; k ]9 o
再解释下上面这一行,设置允许更新中断,即arr减到0以后可以触发更新中断,还有其他类型的中断。MY_NVIC_Init(1,3,TIM3_IRQn,2);//抢占1,子优先级3,组2复制代码
# q* R9 P0 \/ c3 d 看上面这行代码,中断优先级有抢占优先级和响应(即子优先级)优先级两种,抢占优先级即:若程序1正在使用CPU,这时如果程序2要求使用CPU,并且程序2的抢占优先级高,则CPU被程序2抢占;若两者抢占优先级相同,则就算程序2的响应优先级高于程序1,CPU也不能被抢占;若程序1正在使用CPU,程序2和程序3的抢占优先级等于或低于程序1,且程序2的响应优先级高于程序三,则待CPU空出后,程序2先运行,程序3最后运行。TIM3_IRQn是指定将要运行的中断处理程序号。“组2”是设置中断优先级分组的,这是因为寄存器提供了四位来设置优先级,组2代表的是前两位给抢占优先级,后两位给响应优先级。
- [" Y5 h, w0 y- n3 HPWM模式、有效电平 前面介绍完中断,再说一下PWM工作原理。 假设上图中ARR大于CCRx时输出为高电平,ARR小于CCRx时输出为低电平,但在实际运用中可能并非如此,有可能是相反的情况——ARR大于CCRx时输出为低电平,ARR小于CCRx时输出为高电平,至于到底是哪种情况,还要看PWM是哪种模式、有效电平又设置的是何种极性了。模式1:ARR小于CCRx时输出为“有效”电平,ARR大于CCRx时输出为“无效”电平。: ~' P U: L4 y# F( e
模式2:ARR小于CCRx时输出为“无效”电平,ARR大于CCRx时输出为“有效”电平。' e0 Z V) f4 t# A
0 h; z% A; K9 _0 `3 U" n
这里说的是“有效”和“无效”,而不是“高”和“低”,也就是说有效电平可高可低,并非一定就是高电平。PWM模式、效电平极性,需要程序员自己配置相关的寄存器来实现。通过下面的代码来讲解。TIM1_PWM_Init(899,0);//不分频。PWM频率=72000/(899+1)=80Khz复制代码5 ^) B8 l4 a: Q. }9 B0 x4 q
上一小节讲过关于定时器参数的设置。使用定时器1的通道1来输出一路PWM波,这里的899设置的就是ARR的值,至于那个0是用来设置TIM1的频率的,不分频就代表TIM1的时钟频率和系统时钟相同,这里假设为72MHz。void TIM1_PWM_Init(u16 arr,u16 psc)9 ]+ ?/ u; v/ U) T1 b) f
{ ' H3 D5 W% d/ a# L" ^4 s
//此部分需手动修改IO口设置1 w& ^! I7 g5 n# c$ n! ]7 f; [
RCC->APB2ENR|=1<<11; //TIM1时钟使能
+ ~- H9 f3 ?* A8 m# V& D' i) [ GPIOA->CRH&=0XFFFFFFF0; //PA8清除之前的设置
" q1 ?# q$ i0 b3 i. N GPIOA->CRH|=0X0000000B; //复用功能输出 3 r# C, J$ T5 i5 r/ q. Y
! j8 t- d" L, x1 {& H TIM1->ARR=arr; //设定计数器自动重装值
6 n" M! B$ O: E: g0 H TIM1->PSC=psc; //预分频器设置- S6 w$ l6 O: P, T5 w$ O8 u8 ]
& z5 j2 O8 u0 v! I% O
TIM1->CCMR1|=7<<4; //CH1 PWM2模式
6 |$ a! w4 w6 ~, m4 I \# ?+ V TIM1->CCMR1|=1<<3; //CH1预装载使能
; Z# {. O E' p& [1 g3 g TIM1->CCER|=0<<1; //OC1 输出使能 ; e6 Z# l6 I( ]# H. u
//TIM1->CCER|=1<<1;
4 F' X$ v5 F9 c0 U" L* M/ o6 H k6 c% U
TIM1->BDTR|=1<<15; //MOE 主输出使能
. N8 C7 N5 m5 H, v, z0 D( ?0 o( d9 M. w- @ s
TIM1->CR1=0x0080; //ARPE使能
; Z. C' ~ z, d! O `9 w4 P TIM1->CR1|=0x01; //使能定时器1
( m! u& f6 m+ k- ]8 e}复制代码: _' ^% m+ W3 n) p' X
下文具体分析上面的代码。
8 X9 o7 _1 P) A& U% N 前面4-6行是用来配置GPIO口的。TIM1->ARR=arr; //设定计数器自动重装值/ E+ |$ a: d' H5 P0 ?8 K8 `
TIM1->PSC=psc; //预分频器设置复制代码. s3 L7 O& Y7 v3 H" ?" @9 |# J/ R
这两行就是我上门提到的设置定时器的频率和重装载值。TIM1->CCMR1|=7<<4; //CH1 PWM2模式
8 s+ k4 l, h; d: ~ B! UTIM1->CCMR1|=1<<3; //CH1预装载使能' }2 r; A% b( w2 N' A! F3 d
TIM1->CCER|=0<<1; //OC1 输出使能复制代码6 Y9 I( n, e( \* S: w7 R
这三行是用来设置PWM输出模式和设置通道的,通道是什么呢?简单地讲就是输出PWM波的GPIO口,代码一开始不是设置了PA8这个GPIO口嘛,这个PA8就是通道1。使用通道的话要先进行输入输出方向、通道使能的设置。TIM1->CCER|=1<<1;复制代码, e& H0 }8 ^5 F- y
这行代码是用来设置“有效电平”极性的,根据手册,当TIM1->CCER[1]这位置1时,有效电平为低电平,置0时有效电平为高电平,而默认情况下置0。TIM1->BDTR|=1<<15; //MOE 主输出使能复制代码
% J P, ^/ X6 z" t3 a$ O 这行代码只要对高级定时器进行设置,普通定时器无需设置。TIM1->CR1=0x0080; //ARPE使能复制代码
% w0 W$ A! h- H" N9 q3 z/ W 这行代码是用来使能ARPE,ARPE是什么呢,就是当它被置1时,你自己设置的CCRx会立即生效,如果它被置为0,那么你自己设置的CCRx值不会立即生效(可能之前ARPE已经有值了),而是当之前设置的CCRx生效后才会使用你最新设置的CCRx值。$ P* M8 y; I; D" H8 d8 r
上面的代码里没有对CCRx进行设置,这是因为CCRx常常是一个变化的值,你可以在主函数中用一个for循环+if判断语句对它进行++或–的操作,从而达到连续改变CCRx值得目的,例如
3 g# q. l9 j9 K
K9G9)7RLP9W@YEY$R)6ECP1.png (17.39 KB, 下载次数: 3)
下载附件
保存到相册
2022-6-4 21:22 上传
0 G* R' ?) o8 y! {1 }2 `
PWM波的周期是由定时器时钟频率和预装载值两者决定的,预装载值就是ARR。 预装载值PSC设置为899,那么,当定时器的当前值val从0增加到899时,一共经过了900个时钟周期,这900个时钟周期会产生一个PWM波形,也就是说900个定时器时钟周期才相当于一个PWM周期,那么PWM的频率就为72MHz/900=80KHz,周期为1/80KHz。
1 i5 T2 }9 ^, I9 T