⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 clock.c

📁 时钟程序
💻 C
字号:
#include <AT89x51.h>
#include "intrins.h"
unsigned char hour,min,sec,year,month,day,weekday;   //当前时间?日期?星期
unsigned int  count_down;      //1秒钟计时用
bit led_on;                    //数码管是否点亮的标志
unsigned char display[8];       //8位数码管要显示的数据
unsigned char attr;             //八个数码管的闪烁控制字节,当为0时,对应数码管闪
bit flash;                      //LED 闪烁开关,与attr共同决定数码管是否闪烁
unsigned char show_status;      //LED 显示状态标志
 // 0:设置闹钟数据
// 1:显示当前日期及星期
// 2:显示当前时间
// 3:设置当前日期
// 4:设置当前时间
bit km;                       //按键已去抖动标志
bit kp;                       //按键已处理标志
bit sound;                    //蜂鸣器响标志
bit alarm_stop;                //蜂鸣器响后用户手工按停标志
struct  { unsigned char h;  //小时
       unsigned char m;  //分钟
       } alarm[8];    //定义结构数组,8个闹钟项
unsigned char alarm_en;         //闹钟项启用标志
unsigned char alarm_wk;        //闹钟项周末启用标志
unsigned char cur_alarm_set;    //当前设置的闹钟项
unsigned char cur_alarm_active; //当前到点的闹钟项
bit new_alarm_info;            //闹钟项内容已修改标志
sbit sound_output = P1^5;       //蜂鸣器驱动端口,输出0时蜂鸣器响
sbit SDA   = P1^6;     //EEPROM数据线端口
sbit SCL   = P1^7;     //EEPROM时钟线端口
//以下是EEPROM操作时需要的延时函数
void I2cDelay() 
{
_nop_();  _nop_();  _nop_();
   _nop_();  _nop_();  _nop_(); 
}
  //以下是延迟函数,参数为毫秒数
void DelayX1ms(unsigned char count) 
{
unsigned char i,j;
   for(i=0;i<count;i++)
    for(j=0;j<240;j++)   ;
}
//以下是I2C启动函数,24C08使用I2C方式
void Start() 
{
SDA=1; 
I2cDelay(); 
SCL=1; 
I2cDelay();
SDA=0; 
I2cDelay();  
SCL=0;  
} 
//以下是I2C停止函数
void Stop()
{ 
I2cDelay();
SDA=0;
I2cDelay();
SCL=1; 
I2cDelay(); 
SDA=1; 
I2cDelay();
}
//以下是发送1字节数据给EEPROM函数
bit SendByte(unsigned char value)
{
unsigned char i;
bit no_ack=0;
for(i=0;i<8;i++)                     //发送8位数据
{
I2cDelay(); 
if(value&0x80)   SDA=1;
else            SDA=0;
value=value<<1;
I2cDelay();
SCL=1; 
I2cDelay();
I2cDelay(); 
SCL=0; 
}
I2cDelay(); 
SDA=1;     //确认脉冲周期,等待EEPROM的确认
I2cDelay();
SCL=1; 
I2cDelay();
if(SDA==1)   no_ack=1;
I2cDelay(); 
SCL=0;
return no_ack;
}
//以下是向EEPROM写1字节函数
void mywrite(unsigned char address,unsigned char value) 
{
Start();
SendByte(0xa0);      //发送从地址
SendByte(address);    //发送子地址
SendByte(value);     //发送数值
Stop();
DelayX1ms(10);
}
//以下是从EEPROM接收1字节函数
unsigned char ReadByte()
{
unsigned char i,bval;
bval=0;
for(i=0;i<8;i++)     //接收8位数据
{I2cDelay();
SDA=1;              //从P1输入数据时,先往P1输入"1"
I2cDelay();
SCL=1;
I2cDelay();
bval=bval<<1; 
if(SDA)    bval=bval|0x01;
I2cDelay();
SCL=0;}
I2cDelay(); 
SDA=1;  //确认脉冲周期,不送出确认
I2cDelay();
SCL=1; 
I2cDelay(); 
I2cDelay();
return(bval);
}
//以下是从EEPROM读入1字节数据函数
unsigned char myread(unsigned char address) 
{
unsigned char tmp;
Start();
SendByte(0xa0); 
SendByte(address); 
Start();
SendByte(0xa1);
tmp=ReadByte();
Stop();
DelayX1ms(2);
return(tmp);
}
//以下是定时器0中断程序,用于走时,1/8000秒一次
void Timer0ISR(void) interrupt 1 using 3 
{
unsigned char tmp; unsigned int tmp_days;
count_down--;
if(count_down==1 || count_down==2001 || count_down==4001 || count_down==6001)
{flash=~flash;                //数码管闪烁的开关量
if(sound && flash) sound_output=0;  //驱动蜂鸣器
else   sound_output=1;  //关闭蜂鸣器
return;}
/*** 计算当前日期为星期几***/ 
 if(count_down==3000)
{if(year==0)       weekday=5;    //2000年1月1日为星期六
else { tmp=(year-1)/4+1;
  tmp=(year-tmp)+tmp*2;
weekday=(tmp+5)%7;}      //计算出当前年的1月1日为星期几
tmp_days=0;
for(tmp=1;tmp<month;tmp++)
if(tmp==1 || tmp==3 || tmp==5 || tmp==7 || tmp==8 || tmp==10)
tmp_days=tmp_days+31;
else if(tmp==4 || tmp==6 || tmp==9 || tmp==11)
tmp_days=tmp_days+30;
else if(tmp==2)
{if(year%4==0) tmp_days=tmp_days+29;
else   tmp_days=tmp_days+28;}
tmp_days=tmp_days+day-1; 
weekday=(weekday+tmp_days%7)%7+1;
return;}
 /***  查询是否有闹钟时间项符合触发条件 ***/
if(count_down==5000)
{
if((alarm_stop || sound) && alarm[cur_alarm_active].m!=min)  //触发后1分钟
{alarm_stop=0; sound=0; }       //自动关蜂鸣器
 if(sound==0 && alarm_stop==0)           //没有已触发的闹钟项
for(tmp=0;tmp<8;tmp++)      //则查询8个闹钟项内是否有符合条件的
{
if(((alarm_en>>tmp)&1)==0) continue;    //该闹钟项不启用
if(((alarm_wk>>tmp)&1)==1)           //该闹钟项周末有效
 {if(weekday!=6 && weekday!=7) continue;}//不是星期六或星期天
else
{ if(weekday==6 || weekday==7) continue; }
 if(alarm[tmp].h==hour && alarm[tmp].m==min) //比较当前时间
{ sound=1;  cur_alarm_active=tmp; break; }  //与该闹钟项的时间
 }
 return;
}
if(count_down==0)      //过了一秒钟
{ 
count_down=8000;
sec++;
if(sec==60)
{
sec=0;
 min++;
 if(min==60)
{
min=0; hour++;
 if(hour==24)
{
hour=0;   day++;
switch(day)
 {case 29: if(month==2 && year%4)    { day=1; month=3; } break; 
case 30: if(month==2 && year%4==0) { day=1; month=3; } break; 
case 31:if(month==4 || month==6 || month==9 || month==11)  
{ day=1; month++; } break; 
 case 32: day=1;   month++;
if(month==13) { month=1; year++; }
}
}
}
}
}
}
//以下是定时器2中断,用于按键扫描
void Timer1ISR(void) interrupt 3 using 2 
{
unsigned char keytmp;
char tmp;
TH1=0x15; TL1=0xa0;      // 每30ms中断一次
/***  当前显示的内容 ***/
 if(show_status==0)   //当前正在设置闹钟项
{display[0]=cur_alarm_set;
display[1]=0xf;
display[2]=alarm[cur_alarm_set].h/10;
display[3]=alarm[cur_alarm_set].h%10;
display[4]=alarm[cur_alarm_set].m/10;
display[5]=alarm[cur_alarm_set].m%10;
display[6]=(alarm_wk>>cur_alarm_set)&1; 
display[7]=(alarm_en>>cur_alarm_set)&1;}
if(show_status==1 || show_status==3)         //当前显示或设置日期
{display[0]=year/10;
display[1]=year%10; 
display[2]=month/10;   
display[3]=month%10; 
display[4]=day/10;
display[5]=day%10;
display[6]=0xf; 
display[7]=weekday;}
 if(show_status==2 || show_status==4)         //当前显示或设置时间
{display[0]=hour/10;
display[1]=hour%10;
display[2]=min/10;
display[3]=min%10;
display[4]=sec/10;
display[5]=sec%10;
display[6]=0xf;
display[7]=0xf;}   //最后两后无显示
 /***  按键扫描及处理  ***/
  keytmp=~(P1) & 0x0f;
if(keytmp==0)  { km=0;  kp=0; } 
else { if(km==0)   km=1;
else{ if(kp==0)
{kp=1;if(keytmp==1)   //第一个按钮
{ if(sound) { alarm_stop=1; sound=0; }  //如果闹钟正响,按此键停止
else if((show_status==1 || show_status==2) && led_on)  //正显示日期或时间
{ show_status=0;  cur_alarm_set=0;  attr=0x3f; }  //进入闹钟设置
 else if(show_status==0)                //如正在设置闹钟时间项
 { show_status=2;  new_alarm_info=1; attr=0xff; }  //返回当前时间显示
 return;
}
 if(keytmp==2 && led_on)    //第二个按钮,仅当数码管打开时有效
{switch(attr)
{ case 0xff: if(show_status==1) show_status=2;   //在显示时间与日期间切换
 else if(show_status==2) show_status=1;   break;  
case 0x3f: if(show_status==0) cur_alarm_set=(cur_alarm_set+1)%8;
 else if(show_status==3) 
year=(year+1)%50;       //当前日期的"年"加1
else if(show_status==4) 
hour=(hour+1)%24;       //当前时间的"时"加1
 break;  
case 0xcf: if(show_status==0)          //闹钟设置的"时"加1
alarm[cur_alarm_set].h=(alarm[cur_alarm_set].h+1)%24;
else if(show_status==3) 
{ month++;                //当前日期的"月"加1
if(month==13) month=1; } 
else if(show_status==4) 
min=(min+1)%60;         //当前时间的"分"加1
break;  
case 0xf3: if(show_status==0) 
alarm[cur_alarm_set].m=(alarm[cur_alarm_set].m+1)%60;
else if(show_status==3) 
{day++;                  //当前日期的"日"加1
 if(day==32) day=1; }
else if(show_status==4) 
{count_down=8000; 
 sec=(sec+1)%60; }        //当前时间的"秒"加1
break; 
case 0xfd: if(show_status==0)
alarm_wk^=0x1<<cur_alarm_set;   //周末标志位切换
break;
case 0xfe: if(show_status==0)
alarm_en^=0x1<<cur_alarm_set;   //启用标志位切换
}
return;
}
 if(keytmp==4)  //第三个按钮
{switch(attr)
{ case 0xff: if(show_status==1 || show_status==2) 
led_on=~led_on;        //打开或关闭数码管显示
break; 
case 0x3f: if(show_status==0)         //如果正在设置闹钟
{ if(cur_alarm_set==0)  cur_alarm_set=7; 
else cur_alarm_set--;}
 else if(show_status==3)     //当前日期的"年"减1
 { if(year==0)  year=49;  else year--; }
else if(show_status==4)     //当前时间的"时"减1
{ tmp=hour-1;  if(tmp<0) hour=23;  else  hour=tmp;  }
break;
case 0xcf: if(show_status==0)          //闹钟设置的"时"减1
{ tmp=alarm[cur_alarm_set].h-1;
if(tmp<0)    alarm[cur_alarm_set].h=23;
else         alarm[cur_alarm_set].h=tmp;
}
 else if(show_status==3)  
{ month--;             //当前日期的"月"减1
if(month==0)  month=12; }
else if(show_status==4) 
{ tmp=min-1;           //当前时间的"分"减1
 if(tmp<0) min=59;   else  min=tmp;   }
break;
case 0xf3: if(show_status==0)          //闹钟设置的"分钟"减1
{ tmp=alarm[cur_alarm_set].m-1;
if(tmp<0)    alarm[cur_alarm_set].m=59;
else         alarm[cur_alarm_set].m=tmp;
}
else if(show_status==3) 
{ day--;                 //当前日期的"日"减1
if(day==0)  day=31; }
 else if(show_status==4) 
{ tmp=sec-1;             //当前时间的"秒"减1
 count_down=8000;
 if(tmp<0) sec=59;   else  sec=tmp; }
break;
case 0xfd: if(show_status==0)     //切换周末标志
alarm_wk^=0x1<<cur_alarm_set;
break;
case 0xfe: if(show_status==0)     //切换启用标志
alarm_en^=0x1<<cur_alarm_set;
}
return;
}
if(keytmp==8 & led_on)    //第四个按钮,仅当数码管打开时有效
{switch(attr)
{case 0xff: if(show_status==1)   //如果当前显示日期
show_status=3;       //切换到调准日期状态
else if(show_status==2)  //如果当前显示时间
 show_status=4;      //切换到调准时间状态
attr=0x3f; break;   //第一?二个数码管闪烁
case 0x3f: attr=0xcf; break;  //第三?四个数码管闪烁
case 0xcf: attr=0xf3; break;  //第五?六个数码管闪烁case 0xf3: if(show_status==0) 
case 0xf3: if(show_status==0) attr=0xfd;   //第七个数码管闪烁
    else if(show_status==3) 
   { show_status=1; attr=0xff; }  //恢复正常显示日期
else if(show_status==4) 
{ show_status=2; attr=0xff; }  //恢复正常显示时间
 break;
 case 0xfd: if(show_status==0) attr=0xfe;     //第八个数码管闪烁
break;
case 0xfe: if(show_status==0) attr=0x3f;     //第一?二个数码管闪烁
}
}    // end of if(keytmp==4)
}       // end of if(kp==0)
}         // end of if(km==0)
}             // end of if(keytmp!=0)
}
//以下是主函数
main()
{
unsigned char i;
   hour=23;  min=58;  sec=30;  year=2;  month=4;  day=25;
count_down=8000;
flash=0;    attr=0xff;      led_on=1;
km=0;     kp=0;          show_status=2;     //加电后显示当前时间
new_alarm_info=0;  sound=0;   alarm_stop=0;   
for(i=0;i<8;i++)           //从EEPROM中读入8个闹钟设置
 { alarm[i].h=myread(i*2);  alarm[i].m=myread(i*2+1); }  
 alarm_en=myread(i*2); 
alarm_wk=myread(i*2+1);
IE=0;  IP=0;             //禁止所有中断
TMOD=0x12;             //定时器0为模式2,定时器1为模式1
TH0=6;    TL0=6;        //定时器0为0.125ms
TH1=0x15; TL1=0xa0; //定时器1为30ms
TR0=1;    TR1=1;        //启动定时器0和定时器1
ET0=1;  ET1=1;    EA=1;  //开定时器0中断?定时器1中断和总中断
while(1)
{
if(led_on)
 for(i=0;i<8;i++)
{ P2=0; if(flash || attr&(0x80>>i))  
{ P0=display;  P2=0x80>>i; DelayX1ms(1); } }
else  P2=0; 
if(new_alarm_info)
{ P2=0;                      //暂时关闭数码管
 new_alarm_info=0;      //写入EEPROM
for(i=0;i<8;i++)
{ mywrite(i*2,alarm[i].h);  mywrite(i*2+1,alarm[i].m); }  
 mywrite(i*2,alarm_en);   mywrite(i*2+1,alarm_wk);}
} 
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -