/********************************************************************
* 描述* * 电子时钟,LED数码管显示,晶振使用12MHz *
* K1---时调整 *
K2---分调整 * K3---秒调整 K4---时间暂停 K5---12小时制和24小时制切换
* 上电时初始化显示:12-00-00 *
*********************************************************************#include<reg51.h>
#include<intrins.h>
unsignedchar data dis_digit;
unsignedchar key_s, key_v;
unsignedchar code dis_code[11]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00
};//0,1,2,3 4, 5, 6, 7, 8, 9, off //共阴数码管
unsignedchar data dis_buf[8];
unsignedchar data dis_index;
unsignedchar hour,min,sec;
unsignedchar sec100;
unsignedchar flag,flag1,flag2; //falg用于时间停止和启动标志,falg1为12小时和24小时的切换标志,falg2为12小时的上午和下午的切换标志
sbitK1 = P1^0; //用于时的调整
sbitK2 = P1^1; //用于分的调整
sbitK3 = P1^2; //用于秒的调整
sbitK4 = P1^3; //用于时间调整的开关,按下一次暂停时间,再按一次时则开始计时
sbitK5 = P1^4; //用于24小时制和12小时制的切换
bitscan_key();
voidproc_key();
voidinc_sec();
voidinc_min();
voidinc_hour();
voidinc_hour1();
voiddelayms(unsigned char ms);
voidmain(void)
{
P0 = 0xff;
P2 = 0xff;
TMOD = 0x11; // 定时器0, 1工作模式1, 16位定时方式
TH1 = 0xd8; //使用12MHz的晶振,定时时间为:10ms
TL1 = 0xf0;
TH0 = 0xf8; //定时时间为2ms,用于数码管的动态扫描
TL0 = 0x30;
hour = 12;
min = 00;
sec = 00;
sec100 = 0;
flag = 0;
flag1 = 0;
dis_buf[0] = dis_code[hour / 10]; // 时十位
dis_buf[1] = dis_code[hour % 10]; // 时个位
dis_buf[2] = dis_code[min / 10]; // 分十位
dis_buf[3] = dis_code[min % 10]; // 分个位
dis_buf[4] = dis_code[sec / 10]; // 秒十位
dis_buf[5] = dis_code[sec % 10]; // 秒个位
dis_buf[6] = 0x00; // 关闭该位数码管
dis_buf[7] = 0x7f; // 显示 B 表示下午
dis_digit = 0xfe;
dis_index = 0;
TCON = 0x01; //T1:TF1TR1 T0:TF0 TR0 定时中断:IE1 IT1 IE0 IT0 //外部中断0的触发方式为下降沿触发
IE = 0x8a; // 使能timer0,1 中断,控制IE寄存器的格式:EA - -ES ET1 EX1 ET0 EX0
// 1 0 0 0 1 0 1 0
TR0 = 1; //开定时器0
TR1 = 1; //开定时器1
key_v = 0x1f; //定义为K1,K2,K3,K4,K5相应的按键,当没有按下时则为,K1,K2,K3,K4,K5,都为高电平,0x1f
while(1)
{
if(scan_key()) //先扫描一次是否有按键按下
{
delayms(10); //延时
if(scan_key()) //再次扫描一次是否有按键 按下
{
key_v = key_s; //将key_s的值传给key_v
proc_key();
}
}
}
}
bitscan_key()
{
key_s = 0x00; //设置key_s先为0
key_s |=K5;
key_s <<= 1;
key_s |= K4;
key_s <<= 1;
key_s |= K3;
key_s <<= 1;
key_s |= K2; //K2 -> P1.1,若K2按下则为0,当K2按下时key_s与K2或的结果会使得key_s为0x00,未按下时K2则为1,key_s与K2或的结果会使得key_s为0x01
key_s <<= 1; //将key_s左移一位,
key_s |= K1; //K1-> P1.0,若K1按下则为0,当K1按下时key_s与K1或的结果会使得key_s为0x00或是0x02,没有按下时则得到的结果为0x01或是0x03
return(key_s ^ key_v); //进行逻辑异或运算,相同为0,不同为1. 已经定义了变量:key_v = 0x1f; 如果两个按键都没有按下则是得到0x03,异或的结果是为0
}
voidproc_key()
{
if(K4==0) //K4是否按下
{
flag++; //关闭定时器
TR1=0; //关闭定时器1
dis_buf[6] = 0x76; // 显示 H,用于表示设定时间的标志
}
if(K5==0)
{
flag1++; //flag1的初始值是0,falg1为12小时和24小时的切换标志
if(flag1==1) //24小时制
{
if(dis_buf[7]==0x7f) //判断是不是下午
{
hour=hour+12;
dis_buf[7]=0x00;
}
dis_buf[7] = 0x00; // 关闭显示
if(hour >23)
{
hour = 0;
}
if(hour > 9)
dis_buf[0] = dis_code[hour / 10]; // 时十位
else
dis_buf[0] = 0x00; // 当小时的十位为0时不显示
dis_buf[1] = dis_code[hour % 10]; // 时个位
}
if(flag1==2) //12小时制
{
flag1=0;
if(hour>12||hour==0) //判断是不是下午,并用于处理24小时制的大于13和等于0的处理
{
if(hour>12)
hour=hour-12;
if(hour==0)
hour=12;
dis_buf[7]=0x7f; //显示下午 B
}
else
dis_buf[7] = 0x77; // 显示 A 表示上午
if(hour > 9)
dis_buf[0] = dis_code[hour / 10]; // 时十位
else
dis_buf[0] = 0x00; // 当小时的十位为0时不显示
dis_buf[1] = dis_code[hour % 10]; // 时个位
}
}
if((key_v & 0x01) == 0) // K1,当只有K1按下时,则key_s为0x02,没有按下时key_s则为0x03
{
if(flag1==0)
inc_hour();
if(flag1==1)
inc_hour1();
}
elseif((key_v & 0x02) == 0) // K2, 当只有K2按下时,则key_s为0x01,没有按下时key_s则为0x03
{
min++;
if(min > 59)
{
min= 0;
}
dis_buf[2] = dis_code[min / 10]; // 分十位
dis_buf[3] = dis_code[min % 10]; // 分个位
}
else if((key_v & 0x04) == 0) // K3是否按下
{
sec++;
if(sec > 59)
{
sec= 0;
}
dis_buf[4] = dis_code[sec / 10]; // 秒十位
dis_buf[5] = dis_code[sec % 10]; // 秒个位
}
if(flag==2)
{
flag=0;
TR1=1;
dis_buf[6] = 0x00;
if(flag1==0)
{
if(flag2)
dis_buf[7] = 0x77; // 显示 A 表示上午
else
dis_buf[7] = 0x7f; // 显示 B 表示下午
}
}
}
voidtimer0() interrupt 1
// 定时器0中断服务程序, 用于数码管的动态扫描
//dis_index --- 显示索引, 用于标识当前显示的数码管和缓冲区的偏移量
//dis_digit --- 位选通值, 传送到P0口用于选通当前数码管的数值,如等于0xfe时,
// 选通P2.0口数码管
//dis_buf --- 显于缓冲区基地址
{
TH0 = 0xf8; //定时时间为2ms
TL0 = 0x30;
P2 = 0xff; // 先关闭所有数码管
P0 = dis_buf[dis_index]; // 显示代码传送到P0口
P2 = dis_digit; //
dis_digit = _crol_(dis_digit,1); // 位选通值左移, 下次中断时选通下一位数码管
dis_index++; //
dis_index &= 0x07; // 8个数码管全部扫描完一遍之后,再回到第一个开始下一次扫描 ,限定了只扫描8位,当为超过8位时则开始清零.
} //0x07=0000 0111 dis_index在一个个加一时,则为加到8次。
//定时器1的定时时间为10ms
voidtimer1() interrupt 3
{
TH1 = 0xd8;
TL1 = 0xf0;
sec100++;
if(sec100 >= 100) //定时1秒的时间
{
sec100 = 0;
inc_sec();
}
}
voidinc_sec()
{
sec++;
if(sec > 59)
{
sec = 0;
inc_min();
}
dis_buf[4] = dis_code[sec / 10]; // 秒十位
dis_buf[5] = dis_code[sec % 10]; // 秒个位
}
voidinc_min()
{
min++;
if(min > 59)
{
min = 0;
if(flag1==0)
inc_hour();
if(flag1==1)
inc_hour1();
}
dis_buf[2] = dis_code[min / 10]; // 分十位
dis_buf[3] = dis_code[min % 10]; // 分个位
}
voidinc_hour() //12小时制
{
hour++;
if(hour > 12)
{
flag2=~flag2;
hour= 1;
}
if(hour> 9)
dis_buf[0] = dis_code[hour / 10]; // 时十位
else
dis_buf[0] = 0x00; // 当小时的十位为0时不显示
dis_buf[1] = dis_code[hour % 10]; // 时个位
if(flag2)
dis_buf[7] = 0x77; //显示 A 表示上午
else
dis_buf[7] = 0x7f; // 显示 B 表示下午
}
voidinc_hour1() //24小时制时间
{
hour++;
if(hour >23)
{
hour = 0;
}
if(hour > 9)
dis_buf[0] = dis_code[hour / 10]; // 时十位
else
dis_buf[0] = 0x00; // 当小时的十位为0时不显示
dis_buf[1] = dis_code[hour % 10]; // 时个位
}
voiddelayms(unsigned char ms)
// 延时子程序
{
unsigned char i;
while(ms--)
{
for(i = 0; i < 120; i++);
}
}