STM32 Timer (3) PWM代码的实现

理论知识基于上一篇文章 

STM32 Timer (2) 定时器中断代码框架

3.1 PWM分频系数和参数的含义

定时器周期公式T = (arr+1)(psc+1)/f; f = (APB1 *2).

//
定时器基本参数: // ARR: 自动重装载值; // ftim: 频率 //PWM参数: // CCRx: 比较值 对于定时器参数更深入的理解 从(APB1 * 2)来的时钟, 经过psc参数进行预分频,会是这样的效果:(假设psc = 108000) (54 *2)MHz/psc = 108Mhz/108000 = 1Khz, 转化为周期T1 = 1/1khz(s) = 1ms, 也就是说,每一个时钟跳动,对于定时器输入来说,都是1ms,当然这是建立在psc = 108000的情况下. 也就是说, psc=108000的时候,定时器每经过1ms,计数器就++ 或 -- 一次. ARR是自动重装载值,也是定时器的设定值, 假设arr = 500; 就代表着当计数器的值到达500的时候, 就触发更新中断,一次定时器的任务就结束. 这两个参数一起使用的时候,就能得到完整的一次定时任务的时间, 每1ms更新一次技术, 更新500次的时候触发中断, 就等于 1ms * 500 = 500ms; 每500ms触发一次定时中断. 在这个理论的基础上,我们来分析pwm的频率是一种什么概念. 假设定时器触发中断的总时间为T, 定时器频率为f1; 计数器设置为500不变, 那么分析psc对定时器的影响. 当psc= 108 时, f1 = 108Mhz /108 = 1Mhz, 1Mhz 是什么概念, T1 = 1/f1(s) = 1us; 也就是定时器每1us更新一次计数器. 这样子触发一个完整的中断T只要 500us;,下面我将数据对比一下
psc(分频系数) f1(定时器频率) T1(计数器更新时间) arr计数次数 T(定时器完整中断时间)
108   108Mhz/108 = 1MHz 0.000001s = 1us 500 500us
1080 108Mhz/1080 = 100KHz 0.00001s = 10us 500 5ms
10800 108Mhz/10800 = 10KHz 0.0001s = 100us 500 50ms
108000 108Mhz.108000 = 1Khz 0.001s = 1ms 500 500ms

 

从图片个表格可以看出来, 预分频系数直接关系着频率, 也就是直观感受上的 快 和 慢.
当预分频系数越小, 一个定时周期就越短, 因为计数器 咻的一下就把500次心跳更新完了. 
当预分频系数越大, 一个定时周期就越长, 因为缓慢的心跳让计数器的更新动作也变得缓慢.

重装载值控制控制着计数器的阈值然后影响着 一个完整的定时器中断周期, 在预分频系数一定时,假设每1ms更新一次计数器
arr = 500 , T = 500ms;
arr = 1000, T = 1000ms;
结合CCrx来看是什么效果. 假设CCRx分别是arr值的一半, 
当arr = 500的时候, 每250ms电平翻转一次;
当arr = 1000的时候, 每500ms电平翻转一次;
肉眼看到的led效果就是: 一个快速地flash, 另一个慢慢地闪烁.
所以呢,在pwm中, 有三个重要的参数,一个是频率,一个是arr重装载值,一个是CCRx比较值
频率控制着计数器的变化速度,频率越快,计数器变化就越快, 周期就越短,一下子就走到了重装载值的尽头.
arr重装载值同样起着跟频率一样的作用. 

CCRx比较值呢就是改变占空比的作用. 
这个放在led上的效果就是,
占空比越大, led越亮.
占空比越小, led越暗.
将两种效果结合起来就是:
频率越高, led迅速地从暗到亮
频率越低, led缓慢地从暗到亮

放在蜂鸣器上的效果就是:
频率改变声音的音调(Hz), 占空比改变声音的响度.

4.2 代码框架

//1. 开启TIM时钟和配置PWM输出GPIO,配置GPIO时选择复用功能
__HAL_RCC_TIM3_CLK_ENABLE(); //使能定时器 3
__HAL_RCC_GPIOB_CLK_ENABLE(); //开启 GPIOB 时钟
HAL_GPIO_Init(GPIOB,&GPIO_Initure); //配置GPIO

//2. 初始化 TIM3,设置 TIM3 的 ARR 和 PSC 等参数。
HAL_TIM_PWM_Init();

//3. 设置 TIM3_CH4 的 PWM 模式,输出比较极性,比较值等参数。
HAL_TIM_PWM_ConfigChannel();

//4. 能 TIM3,使能 TIM3 的 CH4 输出
HAL_TIM_PWM_Start();

//5. 修改 TIM3_CCR4 来控制占空比。
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>

#define THREAD_STACKSIZE    200
#define THREAD_PRORITY      5
#define THREAD_TASK         5


TIM_HandleTypeDef hpwm;    
TIM_OC_InitTypeDef TIM3_CH4Handler;

void TIM_SetCompare4(uint32_t compare)
{
    TIM3->CCR4=compare;
}


void TIM3_PWM_Init(uint16_t arr, uint16_t psc)
{
   
    hpwm.Instance           = TIM3;        
    hpwm.Init.Prescaler     = psc;    //设置psc预分频系数
    hpwm.Init.CounterMode   = TIM_COUNTERMODE_UP;    //设置计数方式
    hpwm.Init.Period        = arr;                    //设置重装载值
    hpwm.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;    
    HAL_TIM_PWM_Init(&hpwm);    //初始化PWM定时器
    
    
    TIM3_CH4Handler.OCMode = TIM_OCMODE_PWM1;           //设置为小于比较值为有效电平模式
    TIM3_CH4Handler.OCPolarity = TIM_OCPOLARITY_LOW;    //设置有效电平为低电平(LED需要)
    TIM3_CH4Handler.Pulse = arr/2;                      //设置比较值
    HAL_TIM_PWM_ConfigChannel(&hpwm, &TIM3_CH4Handler,TIM_CHANNEL_4);    //配置信道
    
    HAL_TIM_PWM_Start(&hpwm, TIM_CHANNEL_4);    //开启pwm定时器输出
    
}

static void pwm_timer_entry(void *arg)
{
    uint8_t dir = 1;
    uint16_t led0pwmval = 0;
    
    //psc = 108 表示每次心跳为1us, 500次更新一次计数器, 共500us一个周期
    //这种效果就等于疯狂刷新定时器, 不停监听占空比的变化
    //总觉得有点丧心病狂
    TIM3_PWM_Init(500-1, 108-1);    
    
    while(1)
    {
        rt_thread_mdelay(10); //每10ms更新一次占空比.
        if(dir)
        {
            led0pwmval ++;    //每次更新的精度为1,越小表示越平滑
        }else
        {
            led0pwmval --;
        }
        if(led0pwmval > 500)    //500 和 arr设置为一样,表示占空比从0到最大
        {
            dir = 0;
            rt_thread_mdelay(10);
        }
        if(led0pwmval == 0)
        {
            dir =1;
            //rt_thread_mdelay(10);
        }
        TIM_SetCompare4(led0pwmval);   //更新占空比的值
    }
}
    

int pwm_timer()
{
    rt_thread_t tid;
    tid = rt_thread_create("pwm_timer", pwm_timer_entry, RT_NULL,
                           THREAD_STACKSIZE,
                           THREAD_PRORITY,THREAD_TASK);
    if(tid)
        rt_thread_startup(tid);
    return 0;
}
MSH_CMD_EXPORT(pwm_timer, kis_pwm_timer);
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* htim_pwm)
{

    if(htim_pwm->Instance==TIM2)
    {

        __HAL_RCC_TIM2_CLK_ENABLE();

    }else if(htim_pwm->Instance == TIM3)
    {
        GPIO_InitTypeDef GPIO_InitStruck;
        __HAL_RCC_TIM3_CLK_ENABLE();
        __HAL_RCC_GPIOB_CLK_ENABLE();
        
        //gpio
        GPIO_InitStruck.Pin = GPIO_PIN_1;
        GPIO_InitStruck.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruck.Pull = GPIO_PULLUP;
        GPIO_InitStruck.Speed = GPIO_SPEED_HIGH;
        GPIO_InitStruck.Alternate = GPIO_AF2_TIM3;
        HAL_GPIO_Init(GPIOB,&GPIO_InitStruck);
    }

}
原文地址:https://www.cnblogs.com/kmist/p/11669272.html