一、PWM变频率消失问题
最近练习蓝桥杯嵌入式的题目,需要输出一个PWM扫频的信号,遇到了PWM变频率时有几率消失的问题, 下面来研究下原因和解决方案。
由于Keil怎么改设置都不肯给我看外设寄存器,下面用CubeIDE复现下PWM消失的情况,用ST-Link调试。
时钟倍频到170MHz,用TIM2_CH2输出PWM,定时器设置如图
定时器设置
PWM的Pulse设成500,默认输出1kHz 50%占空比的PWM。
测试代码如下,为了方便,我一般把初始化和主循环的代码写到自己建的UserTask文件里,在main.c里include之后,把UserTask_init()和UserTask_loop()两个函数分别放到while(1)的前面和里面即可。
#include <UserTask.h>
uint32_t Freq = 1000, Period = 999, OnTime = 500;
float Duty = 50;
uint8_t Dir = 1;
void UserTask_init(void) {
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
}
void UserTask_loop(void) {
if (Dir) {
Freq += 100;
if (Freq >= 10000) Dir = 0;
}
else {
Freq -= 100;
if (Freq <= 1000) Dir = 1;
}
Period = (uint32_t)(1E6F / (float)Freq - 0.5F);
OnTime = (uint32_t)((float)(Period + 1) * Duty / 100 + 0.5F);
__HAL_TIM_SET_AUTORELOAD(&htim2, Period);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, OnTime);
HAL_Delay(10);
}
上面的程序希望实现PWM频率从1kHz上升至10kHz,再从10kHz下降至1kHz,占空比保持不变的循环扫频。通过__HAL_TIM_SET_AUTORELOAD宏来直接设置定时器的自动重装值,改变定时器的周期,从而实现频率的更新;通过__HAL_TIM_SET_COMPARE宏来直接设置定时器的比较值,改变PWM的高电平时间,从而实现占空比的设定。
编译下载后,运行效果如下
PWM扫频消失
可以看到,PWM在上升到某个频率后消失,之后一直没有输出。
二、原因探究
查看STM32G4的参考手册,可以看到对于通用定时器的基础单元有这样的描述
我们使用的就是向上计数模式,当定时器的计数器向上计数至自动重装值后,计数器清零,开始下一个周期。那么这里就产生了一个问题,我们在定时器运行时设置自动重装值,如果当前的计数值大于新设置的自动重装值,会不会直接触发定时器清零呢?
下面在调试模式查看TIM2的寄存器
可见在启动后,自动重装寄存器(ARR)和捕获/比较寄存器2(CCR2)的值都在正常地循环变化,但是定时器的计数值(CNT)却远远大于了ARR值。说明计数器并没有清零,计数器的清零只发生在CNT=ARR的下一周期,如果我们设置的ARR值比CNT还小,那计数器会一直计数到溢出,此处TIM2是32为定时器,计数到2^32-1才会溢出,所以看起来PWM输出直接消失了。
三、解决方案1
继续查看手册,寻找解决方案
如果使能自动重装预装(ARPE=1),那么写入的新ARR值会存入自动重装预装寄存器,它不会立即生效,而是会在定时器更新的时刻,再从自动重装预装寄存器拷贝至自动重装影子寄存器。也就是说,我们写入的新周期值会在定时器的下一个周期生效,从而避免自动重装值小于计数值的情况发生。
在定时器的设置界面,将“auto-reload preload”设为Enable,再次进行测试,PWM输出正常
正常PWM扫频
在调试界面查看TIM2的寄存器,可看到此时CNT的值也正常地周期变化,不再不受控地增大
四、解决方案2
当然,使用自动重装预装的方式,PWM周期的更新会延迟一个计时周期,这一般不是大问题,如果一定想让周期立即更新,也可以采用程序判断的方式解决计数值超过重装值的问题。在定时器的设置界面,将“auto-reload preload”改回Disable,修改代码如下文章来源:https://www.toymoban.com/news/detail-846543.html
#include <UserTask.h>
uint32_t Freq = 1000, Period = 999, OnTime = 500;
float Duty = 50;
uint8_t Dir = 1;
void UserTask_init(void) {
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
}
void UserTask_loop(void) {
if (Dir) {
Freq += 100;
if (Freq >= 10000) Dir = 0;
}
else {
Freq -= 100;
if (Freq <= 1000) Dir = 1;
}
Period = (uint32_t)(1E6F / (float)Freq - 0.5F);
OnTime = (uint32_t)((float)(Period + 1) * Duty / 100 + 0.5F);
__HAL_TIM_SET_AUTORELOAD(&htim2, Period);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, OnTime);
if (__HAL_TIM_GET_COUNTER(&htim2) > Period) {
__HAL_TIM_SET_COUNTER(&htim2, 0);
}
HAL_Delay(10);
}
上面的程序在设置自动重装值为新的周期值后,用__HAL_TIM_GET_COUNTER(&htim2)来读取当前计数值,如果它大于周期值,那么直接调用__HAL_TIM_SET_COUNTER(&htim2, 0);来清零计数器,也解决了PWM变频率消失的问题。文章来源地址https://www.toymoban.com/news/detail-846543.html
到了这里,关于解决STM32G431输出PWM扫频消失问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!