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); } } }