zoukankan      html  css  js  c++  java
  • 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);
            }        
        }
    }
  • 相关阅读:
    PowerDesigner生成sql及说明文档
    Visual Studio 2005 Team System & UML
    检查数据库数据字段命名规范与合法性的脚本
    常用的快速Web原型图设计工具
    用户需求说明书模板
    数据库设计说明书
    Visual SourceSafe 命名约定和限制
    需求管理工具DOORS介绍
    C#编码命名规则
    数据库对象命名规范
  • 原文地址:https://www.cnblogs.com/xiangtingshen/p/10961869.html
Copyright © 2011-2022 走看看