这里总结一下初次认识PWM的时候遇到的一些问题,也希望能帮大家解决一些在使用CUBEMX配置PWM输出的时候遇到的一些问题
PWM认识
PWM(Pulse Width Modulation)简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在测量、通信、工控等方面。
占空比:可以简单理解为一个周期内脉宽时间与周期时间之比*100%,如20ms周期中,若脉宽为5ms,则占空比为25%。
通过PWM可以输出低于最大输出电压的电压值
对PWM有以上基础的认识我觉得就足够了
CUBEMX配置定时器输出PWM
PWM的输出主要用定时器完成,通过定时器的配置可以调控PWM的频率以及占空比,通过PWM占空比的设置就可以实现电机调速以及舵机转动等,当然PWM也可以用于控制LED灯的亮度等,下面以一个新手的视角讲解如何使用CUBEMX+HAL库控制舵机的转动
工程前期配置
一些基础的工程创建以及debug和时钟配置在这篇博文中有介绍,纯新手可以参考这篇博文完成一些前期的配置
定时器的配置
打开PWM通道
找到timers->TIM1->channel1->PWM generation CH1
这里的只要目的是打开定时器1的PWM输出通道,而CH1N表示PWM互补波形输出,这里我们不需要
如果需要同时控制多个舵机,也可以打开channel2,3,4,可以看见,tim1可以同时输出四路PWM波(不算互补波形)
注意事项
以下有几点注意事项:
1、TIM1挂在APB2总线上,也就是说TIM1的基础频率与APB2相同,通过查看时钟树可以知道APB2频率为72MHz
2、一般舵机的PWM周期为20ms,且脉宽范围在500μs-2500μs之间,其中500μs时舵机旋转到最小角度,2500μs是转到最大角度
3、对于一些参数的介绍
Prescaler/PSC(预分频):大部分时候定时器不需要总线那么高的频率,预分频可以将APB总线上的频率降低,具体降低后频率大小为 总线频率/(PSC+1),如若PSC值为71,APB2总线频率为72MHz,则输入tim1的频率为72/(71+1) = 1MHz
counter mode(计数方式):字面意思,就是决定定时器的计数方式,有向上,向下,中心对齐计数,这里不必深究
Counter Period(计数周期):指定时器中的数值累积到多少后算完成一次计数,大小为该数值+1,如若配置为19999,则表示定时器接收到20000个脉冲后结束一次计数,并开始下一次的计数,可以理解为计数的上限
auto reload preload(自动重装载预加载):这个可了解可不了解,具体的可以看看其他博主更详细的讲解
Pulse:表示PWM脉宽长度,Pulse/Counter Period *100%就表示占空比的大小
了解如上注意事项和知识点,我们就可以开始配置用于舵机输出的PWM了
PWM频率、占空比的计算与配置
预分频值设置为71,则定时器频率为72/(71+1)=1MHz,1*10^-6s计数值+1
计数周期设置为19999,周期为(19999+1)*10^-6=20ms
将auto-reload preload配置为Enable
占空比暂时不需要设置,注:此时占空比为500-2500时对应的PWM脉宽为500-2500μs.
HAL库函数与Keil工程代码编写
HAL库函数
启动PWM函数
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
//参数分别是时钟句柄、通道,示例
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
设置占空比函数
#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \
(((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCR1 = (__COMPARE__)) :\
((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCR2 = (__COMPARE__)) :\
((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCR3 = (__COMPARE__)) :\
((__HANDLE__)->Instance->CCR4 = (__COMPARE__)))
//参数分别为时钟句柄,通道,脉宽长度,示例
__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,500);
关闭PWM函数
HAL_StatusTypeDef HAL_TIM_PWM_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
//参数分别是时钟句柄、通道,示例
HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_1);
需要的基本HAL库函数如上就足够了,还有一些函数,想了解的可以去了解一下
//获取定时器计数值函数
__HAL_TIM_GET_COUNTER(_HANDLE_);
//设置定时器计数值函数
__HAL_TIM_SET_COUNTER(_HANDLE_);
//获取重装载寄存器的值函数
__HAL_TIM_GET_AUTORELOAD(_HANDLE_);
//设置重装载寄存器的值函数
__HAL_TIM_SET_AUTORELOAD(_HANDLE_);
//设置预分频器的值函数
__HAL_TIM_SET_PRESCALER(_HANDLE_);
//定时器更新中断启动函数
HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);
代码编写
打开keil
在main.c函数中添加如下代码块
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); //打开定时器通道
__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,500); //最低脉宽500,让舵机复位到0度
接好对应的线,我们可以看到舵机复位到了0度
接下来更改脉宽为1500,便会观察到舵机转动90度
这里角度与脉宽值的对应关系就是脉宽的500-2500对应映射到舵机转动角度的0-最大角度
比如我用的最大角度为180,则500-2500分别对应0-180度