|
可设置8个闹钟时间的智能时钟 1.功能简介 该时钟以24小时制显示时间,并可显示2000年至2049年之间的任何日期及星期,日期与时间经按键可相互切换,可输入8个闹钟时间设置,每个闹钟设置包括响铃的时间(小时与分钟)、对工作日有效还是对周末有效的标志,以及本项设置是否启用的标志等三部分。这8个闹钟设置均保存在EEPROM中,即使掉电也不用重新输入。当然使用者可通过按钮对任何一个设置作修改。数码管可经按钮关闭显示,避免夜间刺眼、影响睡眠。调节LM317输出电压,可改变数码管亮度,但电压不能低于后备电池的电压,否则后备电池供电。用四节1.5V电池串联作后备电源,保证市电停电时时钟继续走时。时钟的精度取决于晶振频率的精度。原理图见下:
程序清单 本程序用C语言编写,经Keil C51编译成二进制码后写入89C51内的EPROM内即可。 #i nclude "atmel\at89x51.h" #i nclude "intrins.h" unsigned char hour,min,sec,year,month,day,weekday unsigned int count_down bit led_on unsigned char display[8] unsigned char attr bit flash unsigned char show_status // 0:设置闹钟数据 // 1:显示当前日期及星期 // 2:显示当前时间 // 3:设置当前日期 // 4:设置当前时间 bit km bit kp bit sound bit alarm_stop struct { unsigned char h unsigned char m } alarm[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 sbit SDA_PIN = P1^6 sbit SCL_PIN = P1^7 void I2cDelay() //EEPROM操作时需要的延时函数 { _nop_() _nop_() } void DelayX1ms(unsigned char count) //延迟函数,参数为毫秒数 {unsigned char i,j for(i=0 for(j=0 } void Start() //I2C启动,24C08使用I2C方式 { SDA_PIN=1 SCL_PIN=1 SDA_PIN=0 SCL_PIN=0 } void Stop() //I2C停止 { I2cDelay() I2cDelay() I2cDelay() I2cDelay() } bit SendByte(unsigned char value) //发送1字节数据给EEPROM {unsigned char i bit no_ack=0 for(i=0 { I2cDelay() if(value&0x80) SDA_PIN=1 else SDA_PIN=0 value=value<<1 I2cDelay() I2cDelay() I2cDelay() } I2cDelay() I2cDelay() I2cDelay() if(SDA_PIN==1) no_ack=1 I2cDelay() return no_ack } void mywrite(unsigned char address,unsigned char value) //向EEPROM写1字节 { Start() SendByte(value) } unsigned char ReadByte() //从EEPROM接收1字节 {unsigned char i,bval bval=0 for(i=0 { I2cDelay() SDA_PIN=1 I2cDelay() I2cDelay() I2cDelay() } I2cDelay() I2cDelay() I2cDelay() I2cDelay() return(bval) } unsigned char myread(unsigned char address) //从EEPROM读入1字节数据 {unsigned char tmp Start() Start() Stop() return(tmp) } void Timer0ISR(void) interrupt 1 using 3 //定时器0中断程序,用于走时,1/8000秒一次 {unsigned char tmp,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 else { tmp=(year-1)/4+1 weekday=(tmp+5)%7 } tmp_days=0 for(tmp=1 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 return } if(count_down==5000) { if((alarm_stop || sound) && alarm[cur_alarm_active].m!=min) //触发后1分钟 { alarm_stop=0 if(sound==0 && alarm_stop==0) //没有已触发的闹钟项 for(tmp=0 { 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 } 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 switch(day) { case 29: if(month==2 && year%4) { day=1 break case 30: if(month==2 && year%4==0) { day=1 break case 31: if(month==4 || month==6 || month==9 || month==11) { day=1 break case 32: day=1 if(month==13) { month=1 } } } } } } void Timer1ISR(void) interrupt 3 using 2 //定时器2中断,用于按键扫描 {unsigned char keytmp char tmp TH1=0x15 if(show_status==0) //当前正在设置闹钟项 { display[0]=cur_alarm_set display[2]=alarm[cur_alarm_set].h/10 display[4]=alarm[cur_alarm_set].m/10 display[6]=(alarm_wk>>cur_alarm_set)&1 } if(show_status==1 || show_status==3) //当前显示或设置日期 { display[0]=year/10 display[3]=month%10 display[6]=0xf } if(show_status==2 || show_status==4) //当前显示或设置时间 { display[0]=hour/10 display[3]=min%10 display[6]=0xf } keytmp=~(P1) & 0x0f if(keytmp==0) { km=0 else { if(km==0) km=1 else { if(kp==0) { kp=1 if(keytmp==1) //第一个按钮 { if(sound) { alarm_stop=1 else if((show_status==1 || show_status==2) && led_on) //正显示日期或时间 { show_status=0 else if(show_status==0) //如正在设置闹钟时间项 { show_status=2 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 else if(show_status==4) hour=(hour+1)%24 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++ if(month==13) month=1 else if(show_status==4) min=(min+1)%60 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++ if(day==32) day=1 else if(show_status==4) {count_down=8000 sec=(sec+1)%60 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 } //end of switch(attr) return } //end of if(keytmp==1) 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 if(show_status==4) //当前时间的“时”减1 { tmp=hour-1 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-- if(month==0) month=12 else if(show_status==4) { tmp=min-1 if(tmp<0) min=59 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-- if(day==0) day=31 else if(show_status==4) { tmp=sec-1 count_down=8000 if(tmp<0) sec=59 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 } //end of switch(attr) return } //end of if(keytmp==2) 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 case 0x3f: attr=0xcf case 0xcf: attr=0xf3 case 0xf3: if(show_status==0) attr=0xfd else if(show_status==3) { show_status=1 else if(show_status==4) { show_status=2 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 count_down=8000 flash=0 km=0 new_alarm_info=0 for(i=0 { alarm[i].h=myread(i*2) alarm_en=myread(i*2) IE=0 TMOD=0x12 TH0=6 TH1=0x15 TR0=1 ET0=1 while(1) { if(led_on) for(i=0 { P2=0 if(flash || attr&(0x80>>i)) { P0=display[i] } else P2=0 if(new_alarm_info) { P2=0 new_alarm_info=0 for(i=0 mywrite(i*2,alarm_en) } } }
|