可設置8個鬧鐘時間的智能時鐘 1.功能簡介 該時鐘以24小時制顯示時間,并可顯示2000年至2049年之間的任何日期及星期,日期與時間經(jīng)按鍵可相互切換,可輸入8個鬧鐘時間設置,每個鬧鐘設置包括響鈴的時間(小時與分鐘)、對工作日有效還是對周末有效的標志,以及本項設置是否啟用的標志等三部分。這8個鬧鐘設置均保存在EEPROM中,即使掉電也不用重新輸入。當然使用者可通過按鈕對任何一個設置作修改。數(shù)碼管可經(jīng)按鈕關閉顯示,避免夜間刺眼、影響睡眠。調節(jié)LM317輸出電壓,可改變數(shù)碼管亮度,但電壓不能低于后備電池的電壓,否則后備電池供電。用四節(jié)1.5V電池串聯(lián)作后備電源,保證市電停電時時鐘繼續(xù)走時。時鐘的精度取決于晶振頻率的精度。原理圖見下: 程序清單 本程序用C語言編寫,經(jīng)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:設置鬧鐘數(shù)據(jù) // 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操作時需要的延時函數(shù) { _nop_() _nop_() } void DelayX1ms(unsigned char count) //延遲函數(shù),參數(shù)為毫秒數(shù) {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) //發(fā)送1字節(jié)數(shù)據(jù)給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字節(jié) { Start() SendByte(value) } unsigned char ReadByte() //從EEPROM接收1字節(jié) {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字節(jié)數(shù)據(jù) {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) //觸發(fā)后1分鐘 { alarm_stop=0 if(sound==0 && alarm_stop==0) //沒有已觸發(fā)的鬧鐘項 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) //第二個按鈕,僅當數(shù)碼管打開時有效 { 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) //第四個按鈕,僅當數(shù)碼管打開時有效 { 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) } } }
|