#include <reg51.h> #include <intrins.h> unsigned char code dis_week[]={"SUN,MON,TUE,WED,THU,FRI,SAT"}; unsigned char code para_month[13]={0,0,3,3,6,1,4,6,2,5,0,3,5}; //星期月參變數 unsigned char data dis_buf1[16]; //lcd上排顯示緩沖區 unsigned char data dis_buf2[16]; //lcd下排顯示緩沖區 unsigned char data year,month,date,week;//年、月、日、星期 unsigned char data armhour,armmin,armsec;//鬧鐘時、分、秒 unsigned char data hour,min,sec,sec100; //時、分、秒、百分之一秒 unsigned char data flag,vkey,skey;//設置狀態計數標志、按鍵先前值、按鍵當前值 bit alarm; //標識是否啟用鬧鐘,1--啟用,0--關閉 sbit rs = P2^0; //LCD數據/命令選擇端(H/L) sbit rw = P2^1; //LCD讀/寫選擇端(H/L) sbit ep = P2^2; //LCD使能控制 sbit PRE = P3^3; //調整鍵(AN3) sbit SET = P3^4; //調整鍵(AN4) sbit SPK = P3^6; void delayms(unsigned char ms); //延時程序 bit lcd_busy(); //測試LCD忙碌狀態程序 void lcd_wcmd(char cmd); //寫入指令到LCD程序 void lcd_wdat(char dat); //寫入數據到LCD程序 void lcd_pos(char pos); //LCD數據指針位置程序 void lcd_init(); //LCD初始化設定程序 void pro_timedate(); //時間日期處理程序 void pro_display(); //顯示處理程序 void pro_key(); //按鍵處理程序 void time_alarm(); //定時報警功能(鬧鐘) unsigned char scan_key(); //按鍵掃描程序 unsigned char week_proc(); //星期自動計算與顯示函數 bit leap_year(); //判斷是否為閏年 void lcd_sef_chr(); //LCD自定義字符程序 void update_disbuf(unsigned char t1,unsigned char t2[],unsigned char dis_h,unsigned char dis_m,unsigned char dis_s); //更新顯示緩沖區函數 // 延時程序 void delay(unsigned char ms) { while(ms--) { unsigned char i; for(i = 0; i< 250; i++) { _nop_(); //執行一條_nop_()指令為一個機器周期 _nop_(); _nop_(); _nop_(); } } } //測試LCD忙碌狀態 bit lcd_busy() { bit result; rs = 0; rw = 1; ep = 1; _nop_(); _nop_(); _nop_(); _nop_(); result =(bit)(P0&0x80); //LCD的D0--D7中,D7=1為忙碌,D7=0為空閑 ep = 0; return result; } //寫入指令到LCD void lcd_wcmd(char cmd) { while(lcd_busy()); //當lcd_busy為1時,再次檢測LCD忙碌狀態,lcd-busy為0時,開始寫指令 rs = 0; rw = 0; ep = 0; _nop_(); _nop_(); P0 = cmd; _nop_(); _nop_(); _nop_(); _nop_(); ep = 1; _nop_(); _nop_(); _nop_(); _nop_(); ep = 0; } //寫入數據到LCD void lcd_wdat(char dat) { while(lcd_busy()); //當lcd_busy為1時,再次檢測LCD忙碌狀態,lcd-busy為0時,開始寫數據 rs = 1; rw = 0; ep = 0; P0 = dat; _nop_(); _nop_(); _nop_(); _nop_(); ep = 1; _nop_(); _nop_(); _nop_(); _nop_(); ep = 0; } //LCD數據指針位置程序 void lcd_pos(char pos) { lcd_wcmd(pos|0x80); //數據指針=80+地址碼(00H~27H,40H~67H) } //設定二個自定義字符,(注意:LCD1602中自定義字符的地址為0x00--0x07,即可定義8個字符) //這里我們設定把一個自定義字符放在0x00位置(000),另一個放在0x01位子(001) void lcd_sef_chr() { //第一個自定義字符 lcd_wcmd(0x40); //"01 000 000" 第1行地址 (D7D6為地址設定命令形式D5D4D3為字符存放位置(0--7),D2D1D0為字符行地址(0--7)) lcd_wdat(0x1f); //"XXX 11111" 第1行數據(D7D6D5為XXX,表示為任意數(一般用000),D4D3D2D1D0為字符行數據(1-點亮,0-熄滅) lcd_wcmd(0x41); //"01 000 001" 第2行地址 lcd_wdat(0x11); //"XXX 10001" 第2行數據 lcd_wcmd(0x42); //"01 000 010" 第3行地址 lcd_wdat(0x15); //"XXX 10101" 第3行數據 lcd_wcmd(0x43); //"01 000 011" 第4行地址 lcd_wdat(0x11); //"XXX 10001" 第4行數據 lcd_wcmd(0x44); //"01 000 100" 第5行地址 lcd_wdat(0x1f); //"XXX 11111" 第5行數據 lcd_wcmd(0x45); //"01 000 101" 第6行地址 lcd_wdat(0x0a); //"XXX 01010" 第6行數據 lcd_wcmd(0x46); //"01 000 110" 第7行地址 lcd_wdat(0x1f); //"XXX 11111" 第7行數據 lcd_wcmd(0x47); //"01 000 111" 第8行地址 lcd_wdat(0x00); //"XXX 00000" 第8行數據 //第二個自定義字符 lcd_wcmd(0x48); //"01 001 000" 第1行地址 lcd_wdat(0x01); //"XXX 00001" 第1行數據 lcd_wcmd(0x49); //"01 001 001" 第2行地址 lcd_wdat(0x1b); //"XXX 11011" 第2行數據 lcd_wcmd(0x4a); //"01 001 010" 第3行地址 lcd_wdat(0x1d); //"XXX 11101" 第3行數據 lcd_wcmd(0x4b); //"01 001 011" 第4行地址 lcd_wdat(0x19); //"XXX 11001" 第4行數據 lcd_wcmd(0x4c); //"01 001 100" 第5行地址 lcd_wdat(0x1d); //"XXX 11101" 第5行數據 lcd_wcmd(0x4d); //"01 001 101" 第6行地址 lcd_wdat(0x1b); //"XXX 11011" 第6行數據 lcd_wcmd(0x4e); //"01 001 110" 第7行地址 lcd_wdat(0x01); //"XXX 00001" 第7行數據 lcd_wcmd(0x4f); //"01 001 111" 第8行地址 lcd_wdat(0x00); //"XXX 00000" 第8行數據 } //LCD初始化設定 void lcd_init() { lcd_wcmd(0x38); //設置LCD為16X2顯示,5X7點陣,八位數據借口 delay(1); lcd_wcmd(0x0c); //LCD開顯示及光標設置(光標不閃爍,不顯示"-") delay(1); lcd_wcmd(0x06); //LCD顯示光標移動設置(光標地址指針加1,整屏顯示不移動) delay(1); lcd_wcmd(0x01); //清除LCD的顯示內容 delay(1); } //閏年的計算 bit leap_year() { bit leap; if((year%4==0&&year%100!=0)||year%400==0)//閏年的條件 leap=1; else leap=0; return leap; } //星期的自動運算和處理 unsigned char week_proc() { unsigned char num_leap; unsigned char c; num_leap=year/4-year/100+year/400;//自00年起到year所經歷的閏年數 if( leap_year()&& month<=2 ) //既是閏年且是1月和2月 c=5; else c=6; week=(year+para_month[month]+date+num_leap+c)%7;//計算對應的星期 return week; } //更新顯示緩沖區 void update_disbuf(unsigned char t1,unsigned char t2[],unsigned char dis_h,unsigned char dis_m,unsigned char dis_s) { dis_buf1[0]=t1; // dis_buf1[1]=0x20; //空格 dis_buf1[2]=50; //'2' dis_buf1[3]=48; //'0' dis_buf1[4]=year/10+48; dis_buf1[5]=year%10+48; dis_buf1[6]=0x2d; dis_buf1[7]=month/10+48; dis_buf1[8]=month%10+48; dis_buf1[9]=0x2d; //'-' dis_buf1[10]=date/10+48; dis_buf1[11]=date%10+48; dis_buf1[12]=0x20; dis_buf1[13]=dis_week[4*week]; dis_buf1[14]=dis_week[4*week+1]; dis_buf1[15]=dis_week[4*week+2]; dis_buf2[0]=t2[0]; dis_buf2[1]=t2[1]; dis_buf2[2]=t2[2]; dis_buf2[3]=t2[3]; dis_buf2[4]=t2[4]; dis_buf2[5]=t2[5]; dis_buf2[6]=t2[6]; //空格 if (alarm) dis_buf2[7]=0x01; //alarm=1,顯示鬧鐘啟用標致(第二個自定義字符) else dis_buf2[7]=0x20; //alarm=0,不顯示鬧鐘啟用標致 dis_buf2[8]=dis_h/10+48; dis_buf2[9]=dis_h%10+48; dis_buf2[10]=0x3a; //':' dis_buf2[11]=dis_m/10+48; dis_buf2[12]=dis_m%10+48; dis_buf2[13]=0x3a; dis_buf2[14]=dis_s/10+48; dis_buf2[15]=dis_s%10+48; } //時間和日期處理程序 void pro_timedate() { sec++; if(sec > 59) {sec = 0; min++; if(min>59) {min=0; hour++; if(hour>23) {hour=0; date++; if (month==1||month==3||month==5||month==7||month==8||month==10||month==12) if (date>31) {date=1;month++;} //大月31天 if (month==4||month==6||month==9||month==11) if (date>30) {date=1;month++;} //小月30天 if (month==2) {if( leap_year()) //閏年的條件 {if (date>29) {date=1;month++;}} //閏年2月為29天 else {if (date>28) {date=1;month++;}} //平年2月為28天 } if (month>12) {month=1;year++;} if (year>99) year=0; } } } week_proc(); if (sec==armsec && min==armmin && hour==armhour) {if (alarm) TR1=1; //鬧鐘啟用時,報警時間到,啟動Timer1 } } //顯示處理程序 void pro_display() { unsigned char i; lcd_pos(0x00); for (i=0;i<=15;i++) {lcd_wdat(dis_buf1);} lcd_pos(0x40); for (i=0;i<=15;i++) {lcd_wdat(dis_buf2);} } //Timer0中斷處理程序,秒的產生 void timer0() interrupt 1 { TH0=0xD8; TL0=0xF0; sec100++; if(sec100 >= 100) //1秒時間 (100*10ms=1000ms=1s) {sec100 = 0; pro_timedate();//調用時間和日期處理程序 } if (sec&0x01) //"__run__"閃一秒,停一秒 update_disbuf(0x00," ",hour,min,sec); //0x00表示顯示00位置的自定義字符 else update_disbuf(0x00,"__run__",hour,min,sec); pro_display(); //調用顯示處理函數 } //按鍵掃描程序 unsigned char scan_key() { skey=0x00; //給變量vkey置初值 skey|=PRE; //讀取PRE鍵的狀態 skey=skey<<1; //將PRE鍵的狀態存于skey的B1位 skey|=SET; //讀取SET鍵的狀態,并存于skey的B0位 return skey; //返回skey的鍵值(即PRE,SET的狀態) } //外部中斷INT0中斷處理程序 void int0() interrupt 0 { TR0=0; //禁止Timer0 IE=0; //禁止中斷 lcd_wcmd(0x0e); //顯示光標"_",整個光標不閃爍 alarm=1; update_disbuf(0x50,"alarm_",armhour,armmin,armsec); //更新顯示數據,0x50表示要顯示"P" pro_display(); //調用顯示處理程序 lcd_pos(0x47); //使光標位于第一個調整項下 flag=0; vkey=0x03; while(flag^0x0a) {skey = scan_key(); //掃描按鍵狀態 if (skey^vkey) //若skey與vkey相同,跳出循環,相異執行循環體 { delay(10); //去按鍵抖動 skey = scan_key(); //轉回掃描按鍵狀態 if (skey^vkey) //若skey與vkey相同,跳出循環,相異執行循環體 { vkey=skey; //將skey的值付給vkey if (skey==0x01) //PRE鍵按下 { flag++; //調整標志位加1 switch (flag) //將光標置于相應調整位置 { case 1: lcd_pos(0x49);break; //光標置小時報警設置位置 case 2: lcd_pos(0x4c);break; //光標置分鐘報警設置位置 case 3: lcd_pos(0x4f);break; //光標置秒時報警設置位置 case 4: update_disbuf(0x50,"time_ ",hour,min,sec); pro_display(); lcd_pos(0x05);break; //光標置年調整位置 case 5: lcd_pos(0x08);break; //光標置月調整位置 case 6: lcd_pos(0x0b);break; //光標置日調整位置 case 7: lcd_pos(0x49);break; //光標置時調整位置 case 8: lcd_pos(0x4c);break; //光標置分調整位置 case 9: lcd_pos(0x4f);break; //光標置秒調整位置 default:break; } } if (skey==0x02) //SET鍵按下 { pro_key(); //轉設置按鍵處理程序 } } } } lcd_wcmd(0x0c); //設置LCD開顯示及光標不閃爍,不顯示"-" lcd_wcmd(0x01); //清除LCD的顯示內容 IE=0x8f; //CPU開中斷,INT0,INT1,開中斷 TR0=1; //Timer0啟動 } //主程序,初始化及初值設定 void main() { lcd_init(); //初始化LCD lcd_sef_chr(); //寫入自定義字符號 hour=0;min=0;sec=0; //開機時的時,分,秒顯示 armhour=0;armmin=0;armsec=0; //開機時的時,分,秒報警初值 year= 5; month=1;date=1; //開機時的年,月,日,星期顯示 week_proc(); alarm=1; //初始開機,啟用鬧鐘 IE = 0x8f; //CPU開中斷,INT0,INT1,Timer0,Timer1開中斷 IP = 0x04; //設置INT0為中斷最高優先級 IT0=0;IT1=0; //外部INT0,INT1設置為電平觸發方式(注意,觸發不要選邊沿方式,易誤動) TMOD = 0x11; //Timer0,Timer1工作于模式1, 16位定時方式 TH0 = 0xdc;TL0 = 0x00; //Timer0置10ms定時初值 TH1 = 0xff;TL1 = 0x00; //Timer1置初值 TR0 = 1; //Timer0啟動 TR1 = 0; while(1); } //設置按鍵處理程序 void pro_key() { switch (flag) { case 0:alarm=!alarm; //啟用或關閉鬧鐘(alarm=1:啟用,alarm=0:關閉) update_disbuf(0x50,"alarm_",armhour,armmin,armsec); //更新顯示數據 pro_display(); //調用顯示處理 lcd_pos(0x47);break; //光標回到原調整位置 case 1:armhour++; if (armhour>23) armhour=0; update_disbuf(0x50,"alarm_",armhour,armmin,armsec); //更新顯示數據 pro_display(); //調用顯示處理 lcd_pos(0x49);break; //光標回到原調整位置 case 2:armmin++; if (armmin>59) armmin=0; update_disbuf(0x50,"alarm_",armhour,armmin,armsec); pro_display(); lcd_pos(0x4c);break; case 3:armsec++; if (armsec>59) armsec=0; update_disbuf(0x50,"alarm_",armhour,armmin,armsec); pro_display(); lcd_pos(0x4f);break; case 4:year++; if (year> 99) year= 0; week_proc(); //星期自動運算 update_disbuf(0x50,"time_ ",hour,min,sec); pro_display(); lcd_pos(0x05);break; case 5:month++; if (month>12) month=1; week_proc(); //星期自動運算 update_disbuf(0x50,"time_ ",hour,min,sec); pro_display(); lcd_pos(0x08);break; case 6:date++; if (month==1||month==3||month==5||month==7||month==8||month==10||month==12) if (date>31) date=1; //大月31天 if (month==4||month==6||month==9||month==11) if (date>30) date=1; //小月30天 if (month==2) {if(leap_year()) //閏年的條件 {if (date>29) date=1;} //閏年2月為29天 else {if (date>28) date=1;}} //平年2月為28天 week_proc(); //星期自動運算 update_disbuf(0x50,"time_ ",hour,min,sec); pro_display(); lcd_pos(0x0b);break; case 7:hour++; if (hour>23) hour=0; update_disbuf(0x50,"time_ ",hour,min,sec); pro_display(); lcd_pos(0x49);break; case 8:min++; if (min>59) min=0; update_disbuf(0x50,"time_ ",hour,min,sec); pro_display(); lcd_pos(0x4c);break; case 9:sec++; if (sec>59) sec=0; update_disbuf(0x50,"time_ ",hour,min,sec); pro_display(); lcd_pos(0x4f);break; default: break ; } }
//Timer1中斷處理程序,產生報警的聲音 void timer1() interrupt 3 { TH1=0x80; TL1=0x00; SPK=~SPK; t++; } //外部中斷INT1中斷處理程序,停止報警聲音 void int1() interrupt 2 { if(TR1) TR1=0; }
這個程序代碼怎么改啊,求大神指教
|