本帖最后由 dabing89 于 2018-10-14 19:04 编辑
用定时器来模拟实现PWM实现呼吸灯的效果--20181012
众所周知,PWM的应用是及其广泛的,现在很多高速的单片机内部都集成了硬件PWM,使用起来也很简单,配置好频率和装入计数值就可以工作了,但是在一些低成本的场合,我们选择的单片机没有硬件PWM功能模块,但是我们还存在这个需求怎么办呢?这个时候,我们需要用PWM来模拟实现他,但实现PWM必须要了解PWM的原理,这里我们先了解下。
PWM全称是脉冲宽度调制解调,比如1个小灯,按照500MS亮一次,500MS灭一次,周期是1S,频率是1HZ,在这里,1个周期说明白了就是2个方波,有高电平和低电平组成,在周期固定的情况下,我们通过不断的调整高电平所占的整个周期比例,即所谓的占空比,就可以实现小灯”不是那么亮“的效果,如果连续起来,就可以实现呼吸灯的效果了,先来用定时器0实现小灯500MS闪烁的效果,通过DEBUG来看下波形。我们把下面的代码拷贝进去,看下现象
单片机源码:
从DEBUG可以看到,是500MS变化一次,说明我们的设置是对的,但是在这里还是说明一点,我们用的而是STC15W系列的芯片,但是定时器我配置成了12T模式,和STC89C52是一样使用的。既然我们实现了这个500MS高电平,500MS低电平的效果,我们再来实现下200MS亮,800MS灭的效果吧,程序代码如下:
仿真效果如下图所示:
我们可以看到高电平占到了80,低电平占到了20,但是把代码下载进单片机,怎么不是我们想要的那种状态呢?这里普及一个知识点,前面帖子说过的,人类的眼睛不能分辨这种刷新速度低于10MS的物体,如果物体的刷新速度高于10MS,我们的眼睛就会感觉到明显的闪烁了,所以我们看到了下载进开发板的现象就是亮200MS,灭800MS的效果,但是我们想实现我们想要的那种不是太亮的效果怎么办呢?其实只要把刷新频率高于100HZ就OK了,也就是周期要控制在10MS之内,改变高低电平所占的比例即可实现这样的效果,我们写一个让小灯2MS亮8MS灭的程序,看看啥效果,程序如下:
将程序下载进板子上,可以很明显的看到小灯变的不是那么亮了,用逻辑分析仪看下,我们的周期是10MS,实现了我们想要的变的不是那么亮的效果,可是距离我们想要的呼吸灯还是没有实现啊?怎么办呢?答案很简单,只要在在定时器中装入不同的初值即可实现这样的效果,不过要实现呼吸灯的效果,一个定时器是不够的,还要再用一个定时器1才可以,写好的程序如下:
- /********************************************************
- *描述:工程模板 PWM呼吸灯代码
- ********************************************************/
- #include "stc15w.h"
- /*******************************************************************************
- * 文件名:位定义
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本号:1.0.1(2017.05.23)
- *******************************************************************************/
- bit flag200ms = 0;
- bit flag800ms = 0;
- sbit LED0 = P1^0;//
- sbit DATA0 = P2^0;//
- void Bsp_Power_Init(void);
- void TIM0_Init(void);
- void TIM1_Init(void);
- /*******************************************************************************
- * 文件名:数据类型定义
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本号:1.0.1(2017.05.23)
- *******************************************************************************/
- typedef unsigned char uint8;
- typedef unsigned int uint16;
- typedef unsigned long uint32;
- uint8 i = 0;
- code uint16 PWM_H[] = {
- 100,300,500,700,1000,1300,1500,1700,2000,2300,2500,2700,
- 3000,3300,3500,3700,4000,4300,4500,4700,5000,5300,5500,
- 5700,6000,6300,6500,6700,7000,7300,7500,7700,8000,8300,
- 8500,8700,9000,9300,9500,9700,9900 //高电平重装值
- };
- code uint16 PWM_L[] = {
- 9900,9700,9500,9300,9000,8700,8500,8300,8000,7700,7500,
- 7300,7000,6700,6500,6300,6000,5700,5500,5300,5000,4700,
- 4500,4300,4000,3700,3500,3300,3000,2700,2500,2300,2000,
- 1700,1500,1300,1000,700,500,300,100//低电平重装值
- };
- /*******************************************************************************
- * 文件名:主循环入口
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本号:1.0.1(2017.05.23)
- *******************************************************************************/
- void main(void)
- {
- Bsp_Power_Init();//LED端口初始化
- TIM0_Init();
- TIM1_Init();
- LED0 = 1;
- while(1)
- {
- if(flag200ms)
- {
- flag200ms = 0;
- DATA0 = 0;
- }
- if(flag800ms)
- {
- flag800ms = 0;
- DATA0 = 1;
- }
- }
- }
- /*******************************************************************************
- * 文件名:void Bsp_Power_Init()
- * 描 述: 数码管上电显示
- * 功 能:编程模块化
- * 作 者:大核桃
- * 版本号:1.0.1(2017.05.23)
- *******************************************************************************/
- void Bsp_Power_Init(void)
- {
- P0M1 = 0xFC;
- P0M0 = 0X03;
- P0 = 0X00;
- P1M1 = 0xE0;
- P1M0 = 0X1F;
- P1 = 0X00;
- //P2口开漏输出
- P2M1 = 0XFF;
- P2M0 = 0XFF;
- P2 = 0Xff;
- // //P54,P55口为推挽输出
- P5M1 = 0X00;
- P5M0 = 0X00;
- P5 = 0xFF;
- //P37,P36,3.2,P3.3 P3.4口为推挽输出
- P3M1 = 0X00;
- P3M0 = 0XFC;
- P3 = 0X23;
- }
- /*******************************************************************************
- * 文件名:void Timer0Init(void)
- * 描 述: 数定时器0初始化
- * 功 能:编程模块化
- * 作 者:大核桃
- * 版本号:1.0.1(2017.05.23)
- *******************************************************************************/
- void TIM0_Init(void)
- {
- AUXR &= 0x7F;//定时器时钟12T模式
- TMOD &= 0XF0;//配置定时器0为工作模式1
- TMOD |= 0X01;
- TH0 = (65535 - 1000) / 256;//高八位重载值溢出1000次定时1ms
- TL0 = (65535 - 1000) % 256;//低八位重载值
- ET0 = 1;//打开定时器0中断使能位
- TR0 = 1;//打开定时器,使之工作
- EA = 1;//打开总中断
- }
- /*******************************************************************************
- * 文件名:void TIM1_Init(void)
- * 描 述:定时器1初始化配置
- * 功 能:初始化
- * 作 者:大核桃
- * 版本号:1.0.1(2016.07.23)
- *******************************************************************************/
- void TIM1_Init(void)
- {
- AUXR &= 0xBF;//定时器时钟12T模式
- TMOD &= 0X0F;//配置定时器1为工作模式1
- TMOD |= 0X10;
- TH1 = (65535 - 10000) / 256;//高八位重载值溢出1000次定时1ms
- TL1 = (65535 - 10000) % 256;//低八位重载值
- ET1 = 1;//打开定时器1中断使能位
- TR1 = 1;//打开定时器,使之工作
- EA = 1;//打开总中断
- }
- /*******************************************************************************
- * 文件名:中断服务函数
- * 描 述:定时器1中断服务函数
- * 功 能: 中断标号对应 参考数据手册560页
- * 中断名称
- * 作 者:大核桃
- * 版本号:1.0.1(2016.11.15)
- *******************************************************************************/
- void TIM1_IRQ_Handler(void) interrupt 3
- {
- static uint16 tmr50ms = 0;
- static bit a = 0;
- TH1 = (65535 - 10000) / 256;
- TL1 = (65535 - 10000) % 256;//10ms溢出一次
- tmr50ms++;
- if(tmr50ms >= 5)//50ms改变一次PWM重装值
- {
- tmr50ms = 0;
- if(a)
- {
- i--;
- if(i == 0)
- {
- a = 0;
- }
-
- }
- else
- {
- i++;
- if(i >= 40)
- {
- a = 1;
- }
-
- }
- }
-
- }
- /*******************************************************************************
- * 文件名:void TIMER0_INTER(void) interrupt 1
- * 描 述: 中断处理程序
- * 功 能:
- * 作 者:大核桃
- * 版本号:1.0.1(2017.05.23)
- *******************************************************************************/
- void TIM0_IRQ_Handler(void) interrupt 1
- {
- static bit index = 0;
- if(index)
- {
- TH0 = (65536 - PWM_H[i]) / 256;
- TL0 = (65536 - PWM_H[i]) % 256;//12MHZ下溢出1000次定时1ms
- flag200ms = 1;
- index = 0;
- }
- else
- {
- TH0 = (65536 - PWM_L[i]) / 256;
- TL0 = (65536 - PWM_L[i]) % 256;//12MHZ下溢出1000次定时1ms
- flag800ms = 1;
- index = 1;
- }
-
- }
复制代码
我们用了定时器1每隔50MS改变1次定时器的初值,做了2个数组,分别存放PWM的高电平计数初值和低电平计数初值,在12MHZ下计数10000个,恰好是10MS,这样我们就实现了呼吸灯的效果,如果你想让呼吸灯变的更平滑更均匀,可以将定时器的初值更加细化就可以了,如果你对这个程序有啥疑问,可以留言,好了,就介绍到这里吧,代码奉献上。
全部资料51hei下载地址:
009 实用PWM的使用.rar
(35.08 KB, 下载次数: 203)
|