赞
踩
一、原理
1、PWM(Pulse Width Modulation)脉冲宽度调制
一种利用脉冲宽度,即占空比实现对模拟信号进行仿真的技术,即是对模拟信号电平进行数字表示的方法
2、占空比(Duty Cycle)
指在一个周期内,高电平时间占整个信号周期的百分比,即高电平时间与周期的比值
占空比=Tp/T
其中Tp指一个周期内高电平时间,T表示的是周期
3、定时器输出比较功能与实现(PWM)
(1)PWM的工作原理
*STM32中每个定时器有4个输入/输出通道:TIMx_CH1-TIMx_CH4
*每个通道对应1个捕获/比较寄存器TIMx_CRRx,将寄存器值和计数器值相比较,通过比较结果输出高低电平,从而得到PWM信号
*脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器确定频率、由TIM_CRRx寄存器确定占空比的信号
(2)原理流程及示意图
*在PWM的一个周期内,定时器从0开始 向上计数,在0-t1时间段,定时器计数器 TIMx_CNT值小于TIMx_CCRx值,输出 低电平;
*在t1-t2时间段,定时器计数器 TIMx_CNT值大于TIMx_CCRx值,输出 高电平;
*当定时器计数器的值TIMx_CNT达到 ARR时,定时器溢出,重新从0开始向上 计数,如此循环。
(3)实际输出电平
实际输出的电平是由输出极性和电平是否有效共同决定的。
举例:如果此时电平为有效,且配置为输出高级性,那么实际输出的电平是高;如果此时电平为有效,且配置为输出低级性,那么实际输出的电平是低。
STM32中PWM共有两种输出模式:PWM1和PWM2
假设此时配置为PWM模式2,输出高级性,如图所示,当TIMx_CNT<TIMx_CCRx的时候,是无效电平,那么输出为0,即低电平;当TIMx_CNT>TIMx_CCRx的时候,是有效电平,那么输出为1,即高电平。
初始状态下:
PWM1 Low ——高
PWM2 High ——高
二、实验案例1
内容及要求
(1)利用TIM2的通道2输出PWM信号,控制LED发光管H40亮灭;
(2)无按键操作时,将PWM周期、高电平时间交替显示在4位LED数码管上(单位
ms),交替显示频率1Hz左右;
(3)按键操作更改PWM周期或占空比,并将更改结果实时显示在4位LED数码管上:
利用KEY1(减少)和KEY2(增加)调整PWM周期; KEY3(减少)和KEY4(增加)
调整PWM占空比(0-100%);PWM最小周期为100ms,最大周期为1000ms。
程序流程图如下:
部分代码演示(仅供参考):
1、定时器配置
- #include "timer.h"
- #include "stm32f10x.h"
-
- void TIM2_PWM_Init(u16 arr, u32 psc)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- TIM_OCInitTypeDef TIM_OCInitStructure;
-
- // 配置PWM输出通道,开启TIM2时钟-----------------------------------------------------------------------------
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能定时器TIM2的时钟
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); // 使能GPIOA和AFIO时钟
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // GPIO引脚设置PA1
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出模式
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA
-
- // TIM2初始化------------------------------------------------------------------------------------------------
- TIM_TimeBaseStructure.TIM_Period = arr; // 设置自动重载值
- TIM_TimeBaseStructure.TIM_Prescaler = psc; // 设置用来作为TIM2时钟频率的预分频值
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 设置时钟分割
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // TIM向上计数模式
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 初始化TIM2;
-
- // 设置TIM2_CH2的PWM模式,使能TIM2的CH2输出--------------------------------------------------------------------
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; // 设置定时器模式:PWM1模式
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 比较输出使能
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 设置输出极性
- TIM_OCInitStructure.TIM_Pulse=0; // 设置占空比,由于main函数里会重新初始化,这里取0
- TIM_OC2Init(TIM2, &TIM_OCInitStructure); // 初始化TIM2 OC2
- TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); // 使能TIM2在CCR2上的预装载寄存器
- TIM_ARRPreloadConfig(TIM2,ENABLE);
-
- TIM_Cmd(TIM2, ENABLE); // 使能TIM2
- }
2、按键配置
- #include "stm32f10x.h"
- #include "key.h"
- #include "sys.h"
- #include "delay.h"
-
- //按键初始化函数
- void KEY_Init(void) //IO初始化
- {
- GPIO_InitTypeDef GPIO_InitStructure;
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);//使能PORTA,PORTC时钟
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //KEY2 KEY1 KEY4对应引脚
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
- GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC1,2,3
-
- //初始化 WK_UP-->GPIOA.0 下拉输入
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//KEY3对应引脚
- //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0
-
- }
-
- //按键处理函数
- //返回按键值
- //mode:0,不支持连续按;1,支持连续按;
- //0,没有任何按键按下
- //1,KEY0按下
- //2,KEY1按下
- //3,KEY2按下
- //4,WKUP按下 WK_UP
- //注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
- u8 KEY_Scan(u8 mode)
- {
- static u8 key_up=1;//按键按松开标志
- if(mode)key_up=1; //支持连按
- if(key_up&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0))
- {
- delay_ms(10);//去抖动
- key_up=0;
- if(KEY1==0)return 1;
- else if(KEY2==0)return 2;
- else if(KEY3==0)return 3;
- else if(KEY4==0)return 4;
- }else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1)key_up=1;
- return 0;// 无按键按下
- }
3、主程序设计
- #include "stm32f10x.h"
- #include "timer.h"
- #include "delay.h"
- #include "key.h"
- #include "exti.h"
- #include "74HC595_LED.h"
-
- u16 ccr=5000; // 设置高电平时间为5000(预分频后为500ms)
- u16 arr=10000; // 设置定时器设置自动重载值(即周期)为10000(预分频后为1s)
- float pulse=0.5; // 设置占空比
- uint8_t u8DispBuf[5]={0,0x3f,0x06,0x4f,0x66};
-
-
- //------------------------------------------------------------------------------------------
-
-
- // 主函数设计-------------------------------------------------------------------------------
- int main(void)
- {
- Init74HC595(); // 数码管初始化
- delay_init(); // delay函数初始化
- KEY_Init(); // 按键初始化
- EXTIX_Init(); // 外部中断初始化
- TIM2_PWM_Init(pulse*arr,7200-1); // 定时器输出PWM波初始化
- TIM_SetAutoreload(TIM2,arr); //更新定时器初始周期为1000ms
- while(1)
- {
- int i=0;
-
- TIM_SetCompare2(TIM2,ccr); // 配置高电平时间
- ccr=pulse*arr; //更新ccr的值
- TIM_SetCompare2(TIM2,ccr); // 配置高电平时间
-
- for(i = 0;i <100;i++) // 数码管显示arr/10的值,即显示周期,显示0.5s
- {
- u8DispBuf[4]=(arr/10)%10;
- u8DispBuf[3]=(arr/100)%10;
- u8DispBuf[2]=(arr/1000)%10;
- u8DispBuf[1]=(arr/10000)%10;
- DispUpdate();
- }
- for(i = 0;i <100;i++) // 数码管显示ccr/10的值,即显示高电平时长,显示0.5s
- {
- u8DispBuf[4]=(ccr/10)%10;
- u8DispBuf[3]=(ccr/100)%10;
- u8DispBuf[2]=(ccr/1000)%10;
- u8DispBuf[1]=(ccr/10000)%10;
- DispUpdate();
- }
- }
- }
-
-
-
- // 按键实现外部中断来调整周期和占空比--------------------------------------------------------------------
-
-
- // 外部中断1服务程序
- void EXTI1_IRQHandler(void)
- {
- delay_ms(10); // 消抖
- if(KEY2==0 && arr<10000)
- {
- arr+=1000;
- TIM_SetAutoreload(TIM2,arr); // 重装arr
- }
- EXTI_ClearITPendingBit(EXTI_Line1); //清除LINE1上的中断标志位
- }
-
- //外部中断2服务程序
- void EXTI2_IRQHandler(void)
- {
- delay_ms(10); // 消抖
- if(KEY1==0 && arr>1000)
- {
- arr-=1000; // 周期减少500(即减少50ms)
- TIM_SetAutoreload(TIM2,arr); // 重装arr
- }
- EXTI_ClearITPendingBit(EXTI_Line2); //清除LINE2上的中断标志位
- }
-
- // 外部中断0服务程序
- void EXTI0_IRQHandler(void)
- {
- delay_ms(10); // 消抖
- if(KEY3==0 && pulse>0)
- {
- pulse-=0.2; //占空比-10%
- // TIM_SetCompare2(TIM2,ccr); 此处不用重设高电平时间,在main函数里会进行重设
- }
- EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位
- }
-
-
- //外部中断3服务程序
- void EXTI3_IRQHandler(void)
- {
- delay_ms(10); // 消抖
- if(KEY4==0 && pulse<1)
- {
- pulse+=0.2; // 占空比+10%
- // TIM_SetCompare2(TIM2,ccr); 此处不用重设高电平时间,在main函数里会进行重设
-
- }
- EXTI_ClearITPendingBit(EXTI_Line3); //清除LINE3上的中断标志位
- }
三、呼吸灯
实现原理:
1. 视觉暂留
人眼在观察景物时,光信号传入大脑神经,需经过一段短暂的时间,光的作用结束后,视觉形象并不立即消失,这种残留的视觉称“后像”,视觉的这一现象则被称为“视觉暂留”。
2.呼吸灯原理
利用人眼的视觉暂留效应,将定时器的定时周期设定为20ms,通过PWM调制,改变占空比控制一个周期内高低电平的持续时间比例,并将PWM波输出给LED灯。
在这个定时周期下可以体现人眼的视觉暂留效果,此时人眼无法分辨高低电平的变换,因此人眼看见的LED灯始终处于亮/灭的两种状态。但可以通过改变高低电平持续时间的比例,即改变占空比,调节LED灯的亮暗程度。
当占空比低时,高电平持续时间短,在视觉暂留下体现出灯的亮度较低,反之则较高。通过持续循环改变占空比,即可以实现呼吸灯的效果。
代码演示(仅供参考):
1、定时器配置(同上)
2、呼吸灯主函数代码
- #include "stm32f10x.h"
- #include "timer.h"
- #include "delay.h"
-
-
- // 主函数设计-------------------------------------------------------------------------------
- int main(void)
- {
- u8 pwd=1; //用于切换渐变状态
- int CCR=0; //比较寄存器值CRR
- delay_init(); // delay函数初始化
- TIM2_PWM_Init(200-1,7200-1); // 定时器输出PWM波初始化,一个定时周期为20ms
- while(1)
- {
- if(pwd) //状态1:递减
- {
- CCR++;
- TIM_SetCompare2(TIM2,CCR); // 配置高电平时间
- delay_ms(10); //使LED灯亮一段时间,让实验结果更加明显
- if(CCR>200)pwd=0;
- }
- else //状态2:递增
- {
- CCR--;
- TIM_SetCompare2(TIM2,CCR); // 配置高电平时间
- delay_ms(10);
- if(CCR==0)pwd=1;
- }
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。