普通的家用時鐘一般只能設(shè)置一個鬧鐘時間,但很多人均需為工作日、周末、早晨、午休等不同時段設(shè)置不同的鬧鐘時間。如果是使用普通的鬧鐘,只好每次休息前重新設(shè)置,很不方便,有時甚至?xí)霈F(xiàn)忘記更改鬧鐘設(shè)置而睡過頭的情況。
針對這種情況,本人利用89C51單片機(jī)設(shè)計了一款有8種鬧鐘設(shè)置的時鐘,通過一段時間的使用,情況良好。
1、 元件清單 共陰極數(shù)碼管 8個 4511七段譯碼芯片 1片 ATMEL89C51單片機(jī) 1片 24C08EEPROM 1片 24M晶振 1個 9V變壓器(3VA) 1個 LM317輸出可調(diào)穩(wěn)壓IC 1個 整流橋堆 1個 470uF電容 1個 100uF電容 1個 10uF電容 1個 0.1uF電容 1個 33pF電容 2個 蜂鳴器 1個 9014(或其它NPN管) 8個 ksp92(或其它PNP管) 1個 二極管 2個 1K歐電阻 16個 470歐電阻 1個 10K歐可調(diào)電阻 1個 10K歐電阻 4個 按鈕開關(guān) 4個 可裝四節(jié)電池的電池盒 1個 萬能板(約12CM*17CM) 1塊 所有元件按以下的電路圖焊接在一塊萬能板上。注意LM317的輸出應(yīng)由低調(diào)高,以免燒IC。
1、 電路圖
image002.jpg (26.82 KB, 下載次數(shù): 78)
下載附件
2016-2-2 04:43 上傳
1、 功能簡介 該時鐘以24小時制顯示時間,并可顯示2000年至2049年之間的任何日期及星期,日期與時間經(jīng)按鍵可相互切換,可輸入8個鬧鐘時間設(shè)置,每個鬧鐘設(shè)置包括響鈴的時間(小時與分鐘)、對工作日有效還是對周末有效的標(biāo)志,以及本項設(shè)置是否啟用的標(biāo)志等三部分。這8個鬧鐘設(shè)置均保存在EEPROM中,即使掉電也不用重新輸入。當(dāng)然使用者可通過按鈕對任何一個設(shè)置作修改。數(shù)碼管可經(jīng)按鈕關(guān)閉顯示,避免夜間刺眼、影響睡眠。調(diào)節(jié)LM317輸出電壓,可改變數(shù)碼管亮度,但電壓不能低于后備電池的電壓,否則后備電池供電。用四節(jié)1.5V電池串聯(lián)作后備電源,保證市電停電時時鐘繼續(xù)走時。時鐘的精度取決于晶振頻率的精度。
2、 程序清單 本程序用C語言編寫,經(jīng)Keil C51編譯成二進(jìn)制碼后寫入89C51內(nèi)的EPROM內(nèi)即可。 - #include "atmelat89x51.h"
- #include "intrins.h"
-
- unsigned char hour,min,sec,year,month,day,weekday; //當(dāng)前時間、日期、星期
- unsigned int count_down; //1秒鐘計時用
-
- bit led_on; //數(shù)碼管是否點亮的標(biāo)志
- unsigned char display[8]; //8位數(shù)碼管要顯示的數(shù)據(jù)
- unsigned char attr; //八個數(shù)碼管的閃爍控制字節(jié),當(dāng)為0時,對應(yīng)數(shù)碼管閃
- bit flash; //LED 閃爍開關(guān),與attr共同決定數(shù)碼管是否閃爍
- unsigned char show_status; //LED 顯示狀態(tài)標(biāo)志
- // 0:設(shè)置鬧鐘數(shù)據(jù)
- // 1:顯示當(dāng)前日期及星期
- // 2:顯示當(dāng)前時間
- // 3:設(shè)置當(dāng)前日期
- // 4:設(shè)置當(dāng)前時間
-
- bit km; //按鍵已去抖動標(biāo)志
- bit kp; //按鍵已處理標(biāo)志
-
- bit sound; //蜂鳴器響標(biāo)志
- bit alarm_stop; //蜂鳴器響后用戶手工按停標(biāo)志
- struct { unsigned char h; //小時
- unsigned char m; //分鐘
- } alarm[8]; //8個鬧鐘項
- unsigned char alarm_en; //鬧鐘項啟用標(biāo)志
- unsigned char alarm_wk; //鬧鐘項周末啟用標(biāo)志
- unsigned char cur_alarm_set; //當(dāng)前設(shè)置的鬧鐘項
- unsigned char cur_alarm_active; //當(dāng)前到點的鬧鐘項
- bit new_alarm_info; //鬧鐘項內(nèi)容已修改標(biāo)志
-
- sbit sound_output = P1^5; //蜂鳴器驅(qū)動端口,輸出0時蜂鳴器響
- sbit SDA_PIN = P1^6; //EEPROM數(shù)據(jù)線端口
- sbit SCL_PIN = P1^7; //EEPROM時鐘線端口
-
- void I2cDelay() //EEPROM操作時需要的延時函數(shù)
- { _nop_(); _nop_(); _nop_();
- _nop_(); _nop_(); _nop_();
- }
- void DelayX1ms(unsigned char count) //延遲函數(shù),參數(shù)為毫秒數(shù)
- {unsigned char i,j;
- for(i=0;i<count;i++)
- for(j=0;j<240;j++) ;
- }
- void Start() //I2C啟動,24C08使用I2C方式
- { SDA_PIN=1; I2cDelay();
- SCL_PIN=1; I2cDelay();
- SDA_PIN=0; I2cDelay();
- SCL_PIN=0;
- }
- void Stop() //I2C停止
- { I2cDelay(); SDA_PIN=0;
- I2cDelay(); SCL_PIN=1;
- I2cDelay(); SDA_PIN=1;
- I2cDelay();
- }
- bit SendByte(unsigned char value) //發(fā)送1字節(jié)數(shù)據(jù)給EEPROM
- {unsigned char i;
- bit no_ack=0;
- for(i=0;i<8;i++) //發(fā)送8位數(shù)據(jù)
- { I2cDelay();
- if(value&0x80) SDA_PIN=1;
- else SDA_PIN=0;
- value=value<<1;
- I2cDelay(); SCL_PIN=1;
- I2cDelay();
- I2cDelay(); SCL_PIN=0;
- }
- I2cDelay(); SDA_PIN=1; //確認(rèn)脈沖周期,等待EEPROM的確認(rèn)
- I2cDelay(); SCL_PIN=1;
- I2cDelay();
- if(SDA_PIN==1) no_ack=1;
- I2cDelay(); SCL_PIN=0;
- return no_ack;
- }
- void mywrite(unsigned char address,unsigned char value) //向EEPROM寫1字節(jié)
- { Start(); SendByte(0xa0); SendByte(address);
- SendByte(value); Stop(); DelayX1ms(10);
- }
- unsigned char ReadByte() //從EEPROM接收1字節(jié)
- {unsigned char i,bval;
- bval=0;
- for(i=0;i<8;i++) //接收8位數(shù)據(jù)
- { I2cDelay();
- SDA_PIN=1; //從P1輸入數(shù)據(jù)時,先往P1輸入“1”
- I2cDelay(); SCL_PIN=1;
-
- I2cDelay(); bval=bval<<1; if(SDA_PIN) bval=bval|0x01;
- I2cDelay(); SCL_PIN=0;
- }
- I2cDelay(); SDA_PIN=1; //確認(rèn)脈沖周期,不送出確認(rèn)
- I2cDelay(); SCL_PIN=1;
- I2cDelay();
- I2cDelay();
-
- return(bval);
- }
- unsigned char myread(unsigned char address) //從EEPROM讀入1字節(jié)數(shù)據(jù)
- {unsigned char tmp;
- Start(); SendByte(0xa0); SendByte(address);
- Start(); SendByte(0xa1); tmp=ReadByte();
- Stop(); DelayX1ms(2);
- 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; //數(shù)碼管閃爍的開關(guān)量
- if(sound && flash) sound_output=0; //驅(qū)動蜂鳴器
- else sound_output=1; //關(guān)閉蜂鳴器
- return;
- }
- /*** 計算當(dāng)前日期為星期幾***/
- 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; //計算出當(dāng)前年的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;
- }
- /*** 查詢是否有鬧鐘時間項符合觸發(fā)條件 ***/
- if(count_down==5000)
- { if((alarm_stop || sound) && alarm[cur_alarm_active].m!=min) //觸發(fā)后1分鐘
- { alarm_stop=0; sound=0; } //自動關(guān)蜂鳴器
-
- if(sound==0 && alarm_stop==0) //沒有已觸發(fā)的鬧鐘項
- for(tmp=0;tmp<8;tmp++) //則查詢8個鬧鐘項內(nèi)是否有符合條件的
- { if(((alarm_en>>tmp)&1)==0) continue; //該鬧鐘項不啟用
- if(((alarm_wk>>tmp)&1)==1) //該鬧鐘項周末有效
- { if(weekday!=6 && weekday!=7) continue; } //當(dāng)前不是星期六或星期天
- else
- { if(weekday==6 || weekday==7) continue; }
-
- if(alarm[tmp].h==hour && alarm[tmp].m==min) //比較當(dāng)前時間與該
- { 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++; }
- }
- }
- }
- }
- }
- }
-
- void Timer1ISR(void) interrupt 3 using 2 //定時器2中斷,用于按鍵掃描
- {unsigned char keytmp;
- char tmp;
-
- TH1=0x15; TL1=0xa0; // 每30ms中斷一次
-
- /*** 當(dāng)前顯示的內(nèi)容 ***/
- if(show_status==0) //當(dāng)前正在設(shè)置鬧鐘項
- { 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) //當(dāng)前顯示或設(shè)置日期
- { 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) //當(dāng)前顯示或設(shè)置時間
- { 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; } //進(jìn)入鬧鐘設(shè)置
- else if(show_status==0) //如正在設(shè)置鬧鐘時間項
- { show_status=2; new_alarm_info=1; attr=0xff; } //返回當(dāng)前時間顯示
- return;
- }
-
- if(keytmp==2 && led_on) //第二個按鈕,僅當(dāng)數(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; //當(dāng)前日期的“年”加1
- else if(show_status==4)
- hour=(hour+1)%24; //當(dāng)前時間的“時”加1
- break;
- case 0xcf: if(show_status==0) //鬧鐘設(shè)置的“時”加1
- alarm[cur_alarm_set].h=(alarm[cur_alarm_set].h+1)%24;
- else if(show_status==3)
- { month++; //當(dāng)前日期的“月”加1
- if(month==13) month=1; }
- else if(show_status==4)
- min=(min+1)%60; //當(dāng)前時間的“分”加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++; //當(dāng)前日期的“日”加1
- if(day==32) day=1; }
- else if(show_status==4)
- {count_down=8000;
- sec=(sec+1)%60; } //當(dāng)前時間的“秒”加1
- break;
- case 0xfd: if(show_status==0)
- alarm_wk^=0x1<<cur_alarm_set; 周末標(biāo)志位切換
- break;
- case 0xfe: if(show_status==0)
- alarm_en^=0x1<<cur_alarm_set; 啟用標(biāo)志位切換
- } //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; //打開或關(guān)閉數(shù)碼管顯示
- break;
- case 0x3f: if(show_status==0) //如果正在設(shè)置鬧鐘
- { if(cur_alarm_set==0) cur_alarm_set=7;
- else cur_alarm_set--;}
- else if(show_status==3) //當(dāng)前日期的“年”減1
- { if(year==0) year=49; else year--; }
- else if(show_status==4) //當(dāng)前時間的“時”減1
- { tmp=hour-1; if(tmp<0) hour=23; else hour=tmp; }
- break;
- case 0xcf: if(show_status==0) //鬧鐘設(shè)置的“時”減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--; //當(dāng)前日期的“月”減1
- if(month==0) month=12; }
- else if(show_status==4)
- { tmp=min-1; //當(dāng)前時間的“分”減1
- if(tmp<0) min=59; else min=tmp; }
- break;
- case 0xf3: if(show_status==0) //鬧鐘設(shè)置的“分鐘”減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--; //當(dāng)前日期的“日”減1
- if(day==0) day=31; }
- else if(show_status==4)
- { tmp=sec-1; //當(dāng)前時間的“秒”減1
- count_down=8000;
- if(tmp<0) sec=59; else sec=tmp; }
- break;
- case 0xfd: if(show_status==0) //切換周末標(biāo)志
- alarm_wk^=0x1<<cur_alarm_set;
- break;
- case 0xfe: if(show_status==0) //切換啟用標(biāo)志
- alarm_en^=0x1<<cur_alarm_set;
- } //end of switch(attr)
- return;
- } //end of if(keytmp==2)
-
- if(keytmp==8 & led_on) //第四個按鈕,僅當(dāng)數(shù)碼管打開時有效
- { switch(attr)
- { case 0xff: if(show_status==1) //如果當(dāng)前顯示日期
- show_status=3; //切換到調(diào)準(zhǔn)日期狀態(tài)
- else if(show_status==2) //如果當(dāng)前顯示時間
- show_status=4; //切換到調(diào)準(zhǔn)時間狀態(tài)
- attr=0x3f; break; //第一、二個數(shù)碼管閃爍
- case 0x3f: attr=0xcf; break; //第三、四個數(shù)碼管閃爍
- case 0xcf: attr=0xf3; break; //第五、六個數(shù)碼管閃爍
- case 0xf3: if(show_status==0) attr=0xfd; //第七個數(shù)碼管閃爍
- else if(show_status==3)
- { show_status=1; attr=0xff; } //恢復(fù)正常顯示日期
- else if(show_status==4)
- { show_status=2; attr=0xff; } //恢復(fù)正常顯示時間
- break;
- case 0xfd: if(show_status==0) attr=0xfe; //第八個數(shù)碼管閃爍
- break;
- case 0xfe: if(show_status==0) attr=0x3f; //第一、二個數(shù)碼管閃爍
- }
- } // 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; //加電后顯示當(dāng)前時間
-
- new_alarm_info=0; sound=0; alarm_stop=0;
-
- for(i=0;i<8;i++) //從EEPROM中讀入8個鬧鐘設(shè)置
- { 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; //disable all interrupt and lower priority
- TMOD=0x12; //timer 0 is set to mode 2,auto_reloading,timer1,mode 1
- TH0=6; TL0=6; //timer0 parameters for 0.125ms
- TH1=0x15; TL1=0xa0; //timer1 parameters for 30ms
- TR0=1; TR1=1; //timer0 interrupt most important
- ET0=1; ET1=1; EA=1; //enable interrupt function
-
- while(1)
- { if(led_on)
- for(i=0;i<8;i++)
- { P2=0;
- if(flash || attr&(0x80>>i))
- { P0=display[i]; P2=0x80>>i; DelayX1ms(1); }
- }
- else P2=0;
-
- if(new_alarm_info)
- { P2=0; //暫時關(guān)閉數(shù)碼管
- 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);
- }
- }
- }
-
復(fù)制代碼
</cur_alarm_set;
</cur_alarm_set;
</month;tmp++)
</count;i++)
|