详细分析单片机IO口利用PWM脉宽调制实现LED灯的渐亮渐灭

 /*****************************************************************************
硬件说明:
AT89S52,晶振11.0592MHz。P3.6为一个LED背光板,等于0时发光。
P0.0为一个按键,按下时为0。
*****************************************************************************/
/******************************************************************************
程序思路说明:
LED背光板的渐亮渐灭实际上就是LED的亮度等级由低到高(渐亮)再由高到低(渐灭)的
过程,那么首先要通过PWM脉宽调制实现LED显示不同的亮度等级。(脉宽调制的基本原理
可以参考http://hi.baidu.com/gdmgb520/blog/item/077f0601810d1a047bec2cb7.html)
怎么实现不同的亮度等级呢?假设在某个长度的周期(一个适当的时间长度)内如果LED
的管脚一直输出0(我这里输出0时LED背光板点亮),那么很显然LED肯定是最亮的;相
反如果一直输出1,那LED肯定是最暗的(也就是不亮)。如果在这个周期内管脚输出的是
脉冲波(10101010……)那么LED的亮度就只有最亮时的一半,或者说如果前一半时间是
高电平后一半时间是低电平,那LED的亮度也是最亮的一半,这是由于LED的余晖效应,但
这个时间必须恰当,不然LED就不是亮而是闪烁。下面这段代码即可实现LED显示在某个亮度
等级:
void timer0(void) interrupt 1 using 2
{
static uchar counter=0; //中断次数计数器变量
TH0=V_TH0; //恢复定时器初始值
TL0=V_TL0;
counter++;
if (counter<=(ZKB1)) //当小于占空比值时输出低电平,高于时是高电平,从而实现占空比的调整
{LED3=0;}
else
{LED3=1;}
if (counter==100)
{counter=0;}
}
那么现在我们就可以依次变换LED的亮度等级,从而实现LED的渐亮渐灭了。若上面的定时器
中断是0.1ms触发一次、ZKB1=20,那么整个周期为0.1ms*100=10ms,LED3为低脉冲的时间
长度为20*0.1ms=2ms,显示亮度为最亮的20%。如果我们把100个不同的亮度等级挨个显示
一遍就出现了由暗到亮的渐亮过程或渐灭过程。那么我们就在每当counter==100的时候,
ZKB1++就行了(由暗到亮)。上面的程序第24行{}内加入ZKB1++;,并在第25行加入
if (ZKB1==100) ZKB1=0; 语句即可。这样看到的效果是LED由暗变亮然后又有暗变亮。
现在我们再加入由亮变暗。新增一个变量ZKB2,counter==100时ZKB2++,当ZKB2<=100
时ZKB1=ZKB2,实现有暗变亮;当100<ZKB2<=200时ZKB1=200-ZKB2,实现由亮变暗;当
200<ZKB2<=400时ZKB1=0,LED保持熄灭。从时间上来说,每一个亮度等级耗时10ms,那
么渐亮耗时1s,渐灭耗时1s,熄灭保持2s,然后开始下一个周期。
这里我还加了一个flag0变量,作用是当K0按下时使LED停止发光。

2010.4.30,对上面的这段话再做一次解释:
1、ZKB1决定了LED3应该显示多亮(ZKB1的值为0—99,即把亮度分为100个等级)
2、ZKB2的值决定了现在应该选择哪个亮度等级(ZKB2的值为0—399),实际上ZKB2相当于
X轴(时间),而ZKB1相当于Y轴(亮度)。ZKB2从0到99时LED3渐亮(ZKB1从0到99),当
ZKB2从100到199时LED3渐灭(ZKB1从99回到0),当ZKB2从200到399时LED3熄灭(ZKB1=0),
这样整个过程就是LED3渐亮—渐灭—熄灭,然后重复。
y
|      * *                 * *
|    *     *            *      *
|  *          *       *           *
|*               * *                 *
|____________________ x
******************************************************************************/
#include <REG52.H>
#define uchar unsigned char

#define V_TH0 0xff //定时器0初值
#define V_TL0 0xa3

sbit LED3=P3^6; ///背光片接口
sbit K0=P0^0;

unsigned char ZKB1,ZKB2;
bit flag0;

/*————————-定时器初始化———————————–*/
void init_sys(void)
{
TMOD=0x01; //定时器0工作在方式1
TH0=V_TH0; //定时周期为0.1ms
TL0=V_TL0;
TR0=1;
ET0=1;
EA=1;
}

/*———————–定时器0中断函数———————————*/
void timer0(void) interrupt 1 using 2
{
static uchar counter=0; //中断次数计数器变量
TH0=V_TH0; //恢复定时器初始值
TL0=V_TL0;
if (flag0==1)
{
counter++;
if (counter>=100)
{
counter=0;
ZKB2++;
if (ZKB2<=100) //占空比变化部分
{ ZKB1=ZKB2;}
else if (ZKB2<=200)
{ ZKB1=200-ZKB2;}
else
{ ZKB1=0;}
}
if (counter<=(ZKB1)) //当小于占空比值时输出低电平,高于时是高电平,从而实现占空比的调整
LED3=0;
else
LED3=1;
if (ZKB2>399) ZKB2=1;
}
else
{
LED3=1;
}
}

/*——————————主函数————————————-*/
void main (void)
{
init_sys();
while(1)
{
P0=0xff;
flag0=K0;
}
}
/***************************************************************************
小结:这里定时器的定时长度需要根据具体情况作出适当调节
****************************************************************************/

下在KeilC源代码
下载地址1 

下载地址2

分享到: 更多
版权申明:

本站保留所有原创文章的版权,本站地址:奔跑的博客[http://www.elecbench.com]

原创文章转载时请注明出处,并添加文章所在页面的链接:http://www.elecbench.com/142/

本站所有 2010年3月4日 以后发表、未标明为“转载”的文章均是本站原创。

发表评论


(设置自己的个性头像)

*

申请属于你的免费顶级域名