如何解决STM32G431输出PWM扫频消失问题

一、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,修改代码如下

#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变频率消失的问题。

作者:SineWave~

物联沃分享整理
物联沃-IOTWORD物联网 » 如何解决STM32G431输出PWM扫频消失问题

发表评论