/*60S倒计时*/
/*个位每1S变一次,从0~9*/
/*十位,个位为0的下一秒十位发生变化*/
#include<reg52.h>
sbit ADDR0=P1^0;
sbit ADDR1=P1^1;
sbit ADDR2=P1^2;
sbit ADDR3=P1^3;
sbit ENLED=P1^4;
unsigned char code LedChar[]=
{
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
0x80,0x90
};
void main()
{
unsigned int cnt=0; //记录中断次数
unsigned int sec=0; //记录秒数
unsigned char g=0; //个位数码管
unsigned char s=6; //十位数码管
ENLED=0; //使能U3 选中数码管DS1
TMOD=0x01; //T0为模式1
TH0=0xB8; //T0赋初值0xB800
TL0=0x00;
TR0=1; //启动T0
while(1)
{
ADDR3=1;
P0=0xff;
ADDR2=0;
ADDR1=0;
ADDR0=0;
P0=LedChar[g]; //选中个位数码管
if(TF0==1) //判断T0是否溢出
{
TF0=0; //T0溢出后清零中断标志
TH0=0xB8; //并重新赋值
TL0=0x00;
cnt++;
}
if(cnt>=50) //判断溢出是否达到50次
{
cnt=0; //达到五十次(1s)清零
sec--; //秒数累加
}
P0=0xff;
ADDR0=1;
P0=LedChar[s]; //选中十位数码管,并显示当前秒数下的数字
if(s==0&&g==0) //避免00状态的出现,状态60~60时间为60s
s=6;
if(g==0&&sec%10==1)
s--;
if(sec%10!=0)
{
g=10-sec%10;
}
if(sec%10==0)
{
g=0;
}
}
}
(1)实际上数码管状态变化为01~00~60,只不过00存在的时间过短,无法被人眼识别。
(2)在切换数码管前先让 P0=0xff 避免了前一个数码管留下的余辉。
上面程序较为繁琐,以下为更新程序:
/*60倒计时*/
#include<reg52.h>
sbit ADDR0=P1^0;
sbit ADDR1=P1^1;
sbit ADDR2=P1^2;
sbit ADDR3=P1^3;
sbit ENLED=P1^4;
unsigned char code LedChar[]=
{
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
0x80,0x90
};
unsigned int cnt=0; //记录中断次数
unsigned char flag=0; //中断标志位
unsigned char i=0; //遍历
unsigned char g=0xff; //初始化个位数码管
unsigned char s=0xff; //初始化十位数码管
unsigned int sec=61; //秒数
void main()
{
EA=1; //定时器总中断
ENLED=0; //?使能U3
ADDR3=1; //T0模式为1
TMOD=0x01; //定时1ms
TH0=0xfc;
TL0=0x67;
ET0=1;
TR0=1; //启动T0
while(1)
{
if(flag==1) //中断标志
{
g=sec%10;
s=sec/10;
flag=0; //标志清0
if(sec==0) //重新倒计时
sec=61;
}
}
}
void InterruptTimer0() interrupt 1
{
TH0=0xfc; //重新赋值
TL0=0x67;
cnt++;
if(cnt>=1000) //中断累计1000 1s
{
cnt=0; //清0
flag=1;
sec--; //秒数自减
}
P0=0xff;
switch(i)
{
case 0:ADDR0=0;ADDR1=0;ADDR2=0;P0=LedChar[g];i++;break; //选中个位数码管
case 1:ADDR0=1;ADDR1=0;ADDR2=0;P0=LedChar[s];i=0;break; //选中十位数码管
default:break;
}
}
改进后,程序更为简洁直观。采用中断+switch遍历优化程序。
注意:遍历中,为了使几个数码管看起来像是同时亮,故大大缩短了定时时间,由20ms变为1ms。