PWM

一、PWM概述

 

1、定义

PWM(Pulse Width Modulation),脉冲宽度调制。

脉冲:方波,频率

宽度:高电平的宽度,占空比

 

 

 

2、用途

控制灯光的亮度(手机/平板/显示器背光灯)

电机的转速

....

 

3、灯光的控制细节

频率要大于25Hz,灯光的亮度的变化是跟着占空比而变化,占空比越大,灯光越暗;占空比越小,灯光越亮(因为led灯是低电平点亮)

 

二、程序设计

1原理图

    

不同的定时器,输出通道的数目都有所不一样。

 

2.     配置频率

 

/* TIM3 clock enable ,定时器14时钟使能*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE);
/* Time base configuration ,定时器基本配置*/
TIM_TimeBaseStructure.TIM_Period = (10000/100)-1;                    //定时时间的设置[非常重要],中断频率为100Hz,也就是定时时间为10ms
TIM_TimeBaseStructure.TIM_Prescaler = 8400-1;                             //预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;              //时钟分频,也称之为二次分频,当前是1分频,说白了不分频,不降低频率
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;        //向上计数的方法
TIM_TimeBaseInit(TIM14, &TIM_TimeBaseStructure);                   //初始化

 

3.     配置PWM输出功能

 

 

 

/* PWM1 Mode configuration: Channel1 ,让PWM的通道1工作在模式1*/
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                                    //PWM模式1,在递增模式下,只要 TIMx_CNT < TIMx_CCR1,通道 1 便为有效状态(高电平),否则为无效状态(低电平)。
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;    //允许输出
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;              //有效的时候,输出高电平
TIM_OC1Init(TIM14, &TIM_OCInitStructure);
 
TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);                       //自动重载初值,不断输出PWM脉冲
TIM_ARRPreloadConfig(TIM14, ENABLE);                                                              //自动重载初值使能
 
/* TIM14 enable counter,使能定时器14工作 */
TIM_Cmd(TIM14, ENABLE);

 

 

4.     配置引脚的复用功能

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);

 

/* 配置PF9引脚为输出模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;                                       //第9根引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                                   //复用功能模式,使用引脚的第二功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                                  //推挽模式,增加驱动电流
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;                       //设置IO的速度为100MHz,频率越高性能越好,频率越低,功耗越低
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;                           //不需要上拉电阻
GPIO_Init(GPIOF, &GPIO_InitStructure);
//将PF9引脚连接到定时器14
GPIO_PinAFConfig(GPIOF, GPIO_PinSource9, GPIO_AF_TIM14);

 

5.     计数值与比较值之间的关系

 

         使用PWM控制LED0、LED2、LED3产生渐亮与渐灭的控制,模仿手机的通知灯。

提示:定时器1属于高级定时器,使能输出的时候要额外添加以下函数,

TIM_CtrlPWMOutputs(TIM1,ENABLE);

该函数用于设置定时器1的BDTR寄存器,详细描述如下:

如果控制LED灯的频率在20Hz、30Hz、50Hz、100Hz,效果是怎样的?

答:当尝试去调整频率低于30Hz的情况下,可以看到灯在闪烁。

 

tim14_pwm

#include <stdio.h>
#include "stm32f4xx.h"
#include "sys.h"

GPIO_InitTypeDef            GPIO_InitStructure;

TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
NVIC_InitTypeDef         NVIC_InitStructure;
TIM_OCInitTypeDef          TIM_OCInitStructure;

void delay_us(uint32_t nus)
{
    SysTick->CTRL = 0;                         // Disable SysTick
    SysTick->LOAD = (SystemCoreClock/8/1000000)*nus; // 计数值
    SysTick->VAL = 0;                         // Clear current value as well as count flag
    SysTick->CTRL = 1;                         // Enable SysTick timer with processor clock,当使用21MHz的时候,1;当使用168MHz的时候,5;
    while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set
    SysTick->CTRL = 0;                         // Disable SysTick
}


void delay_ms(uint32_t nms)
{
    uint32_t m,n;
    
    m = nms/500;
    
    n = nms %500;
    
    //m个500ms的延时
    while(m--)
    {
        SysTick->CTRL = 0;                         // Disable SysTick
        SysTick->LOAD = (SystemCoreClock/8/1000)*500; // 计数值
        SysTick->VAL = 0;                         // Clear current value as well as count flag
        SysTick->CTRL = 1;                         // Enable SysTick timer with processor clock,当使用21MHz的时候,1;当使用168MHz的时候,5;
        while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set
        SysTick->CTRL = 0;                         // Disable SysTick    
    }

    //不足500ms的延时
    if(n)
    {
        SysTick->CTRL = 0;                         // Disable SysTick
        SysTick->LOAD = (SystemCoreClock/8/1000)*n; // 计数值
        SysTick->VAL = 0;                         // Clear current value as well as count flag
        SysTick->CTRL = 1;                         // Enable SysTick timer with processor clock,当使用21MHz的时候,1;当使用168MHz的时候,5;
        while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set
        SysTick->CTRL = 0;                         // Disable SysTick        
    
    }
}



void tim14_init(void)
{
    /* TIM14 clock enable ,定时器14时钟使能*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE);


    /* Time base configuration ,定时器基本配置*/
    TIM_TimeBaseStructure.TIM_Period = (10000/100)-1;            //定时时间的设置[非常重要],中断频率为100Hz,也就是定时时间为10ms
    TIM_TimeBaseStructure.TIM_Prescaler = 8400-1;                //预分频值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;        //时钟分频,也称之为二次分频,当前是1分频,说白了不分频,不降低频率
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;    //向上计数的方法
    TIM_TimeBaseInit(TIM14, &TIM_TimeBaseStructure);            //初始化

    /* PWM1 Mode configuration: Channel1 ,让PWM的通道1工作在模式1*/
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                //PWM模式1,在递增模式下,只要 TIMx_CNT < TIMx_CCR1,通道 1 便为有效状态(高电平),否则为无效状态(低电平)。
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;    //允许输出
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;        //有效的时候,输出高电平
    TIM_OC1Init(TIM14, &TIM_OCInitStructure);

    TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);                //自动重载初值,不断输出PWM脉冲
    TIM_ARRPreloadConfig(TIM14, ENABLE);                            //自动重载初值使能


    /* TIM14 enable counter,使能定时器14工作 */
    TIM_Cmd(TIM14, ENABLE);
}


int main(void)
{
    
    uint32_t pwm_cmp=1;
    
    //配置系统定时器时钟源,当前是使用168MHz/8=21MHz
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
    
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);

    /* 配置PF9引脚为输出模式 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;                    //第9根引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                //复用功能模式,使用引脚的第二功能
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                //推挽模式,增加驱动电流
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;            //设置IO的速度为100MHz,频率越高性能越好,频率越低,功耗越低
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;            //不需要上拉电阻
    GPIO_Init(GPIOF, &GPIO_InitStructure);

    //将PF9引脚连接到定时器14
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource9, GPIO_AF_TIM14);
   
    //定时器14初始化
    tim14_init();
    
    while(1)
    {
        //渐亮还是渐灭的过程?渐灭
        for(pwm_cmp=1; pwm_cmp<100; pwm_cmp++)
        {
            //占空比 = pwm_cmp/频率
            TIM_SetCompare1(TIM14,pwm_cmp);
            
            
            delay_ms(20);
        
        }

    }

}

 

注意高级定时器tim1 和 tim8 高级定时器的不同使用

#include "stm32f4xx.h"
#include "sys.h"
#include "stdio.h"

GPIO_InitTypeDef                  GPIO_InitStructure;
NVIC_InitTypeDef                 NVIC_InitStructure;
TIM_TimeBaseInitTypeDef          TIM_TimeBaseStructure;
TIM_OCInitTypeDef                  TIM_OCInitStructure;

void delay_us(uint32_t nus)
{        
    uint32_t temp;             
    SysTick->LOAD =SystemCoreClock/8/1000000*nus;     //时间加载               
    SysTick->VAL  =0x00;                            //清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;         //使能滴答定时器开始倒数      
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));            //等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;         //关闭计数器
    SysTick->VAL =0X00;                               //清空计数器 
}

void delay_ms(uint16_t nms)
{                     
    uint32_t temp;           
    SysTick->LOAD=SystemCoreClock/8/1000*nms;        //时间加载(SysTick->LOAD为24bit)
    SysTick->VAL =0x00;                               //清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //能滴答定时器开始倒数 
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));            //等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
    SysTick->VAL =0X00;                               //清空计数器              
} 



void tim1_init(void)
{
    
    /* 使能GPIOE的硬件时钟 */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);

    /* GPIOF Configuration: TIM1 CH3 (PE13) 和 CH4 (PE14) */
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_13 | GPIO_Pin_14  ;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;            //复用功能,使用引脚的第二功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
    GPIO_Init(GPIOE, &GPIO_InitStructure);

    /* Connect TIM pins to AE13 */
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_TIM1);
    
    /* Connect TIM pins to AE14 */
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_TIM1);
    
    /*定时器 TIM1时钟使能  */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    
    /* Time base configuration,100Hz*/
    TIM_TimeBaseStructure.TIM_Period = (10000/100)-1;                    //定时计数值,100Hz
    TIM_TimeBaseStructure.TIM_Prescaler = 16800;                        //预分频值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                //再次进行1分频
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;            //向上计数
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

    /* PWM1 Mode configuration: Channel3 Channel4 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

    
    TIM_OC3Init(TIM1,&TIM_OCInitStructure);                             //TIM1通道3初始化
    TIM_OC4Init(TIM1,&TIM_OCInitStructure);                             //TIM1通道4初始化

    TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);                   //自动重载初值,不断输出PWM脉冲
    TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable);                    //自动重载初值,不断输出PWM脉冲
    TIM_ARRPreloadConfig(TIM1, ENABLE);                                    //自动重载使能    

    /* TIM1 enable counter */
    TIM_Cmd(TIM1, ENABLE);

    //使能TIM1PWM输出,参考帮助文档TIM/TIM_7PWMOutput
    TIM_CtrlPWMOutputs(TIM1,ENABLE);
}

void tim14_init(void)
{
    /* GPIOF clock enable */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);

    /* GPIOF Configuration: TIM14 CH1 (PF9) */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 ;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;            //复用功能,使用引脚的第二功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
    GPIO_Init(GPIOF, &GPIO_InitStructure); 

    /* Connect TIM pins to AF9 */
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource9, GPIO_AF_TIM14);

    /* TIM14 clock enable */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE);


    /* Time base configuration,100Hz*/
    TIM_TimeBaseStructure.TIM_Period = (10000/100)-1;                    //定时计数值,10Hz
    TIM_TimeBaseStructure.TIM_Prescaler = 8400;                            //预分频值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                //再次进行1分频
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;            //向上计数
    TIM_TimeBaseInit(TIM14, &TIM_TimeBaseStructure);


    /* PWM1 Mode configuration: Channel1 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM14, &TIM_OCInitStructure);


    TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);                    //自动重载初值,不断输出PWM脉冲
    TIM_ARRPreloadConfig(TIM14, ENABLE);                                //自动重载使能                        

    /* TIM14 enable counter */
    TIM_Cmd(TIM14, ENABLE);

}

int main(void)
{
    uint32_t pwm_cmp=0;

    //系统时钟的时钟源=168MHz/8=21MHz
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);


    //定时器14初始化为PWM,使用PWM通道1,当前频率为100Hz
    tim14_init();
    tim1_init();


    while(1)
    {
        //当前比较值自加,看到的是渐灭的过程
        for(pwm_cmp=1; pwm_cmp<100; pwm_cmp++)
        {
            //设置TIM14通道1比较值
            TIM_SetCompare1(TIM14,pwm_cmp);

            //设置TIM1通道3比较值
            TIM_SetCompare3(TIM1,pwm_cmp);

            //设置TIM1通道4比较值
            TIM_SetCompare4(TIM1,pwm_cmp);

            delay_ms(15);
        }

        //当前比较值自加,看到的是渐亮的过程
        for(pwm_cmp=99; pwm_cmp>0; pwm_cmp--)
        {
            //设置TIM14通道1比较值
            TIM_SetCompare1(TIM14,pwm_cmp);

            //设置TIM1通道3比较值
            TIM_SetCompare3(TIM1,pwm_cmp);

            //设置TIM1通道4比较值
            TIM_SetCompare4(TIM1,pwm_cmp);


            delay_ms(15);
        }        
    }
}

 

posted @ 2019-06-02 07:56  狂奔~  阅读(2736)  评论(0编辑  收藏  举报