单片机万年历实训.doc
单片机万年历实训仿真图:原理图:程序:/* 使用LCD1602与单片机实现的时钟与日期 显示年、月、日、时、分、秒(24小时制) 时间可调, 年份20009999 2012年10月9日*/#include<reg51.h>/*全局变量的定义与设置*/sbit key_mean=P10;/按键的定义sbit key_ok =P11;sbit key_set =P12;sbit RS =P15; /LCD控制端口 sbit RW =P16; sbit E =P17; sfr PORT =0xa0;char flag_sec=0; /值为20代表1schar flag_en_ok=0;char flag_en_key=0; /值为1表示键盘有效,否则无效char flag_ok=0; /key_up与key_down的功能选择char flag_mean=0;char flag_set=0; /key_up与key_down的功能标识char flag_year_leap=0;/短语 /code char string_time= "Setting time ? 0" code char string_year= "Setting year ? 0" code char string_month= "Setting month ? 0" code char string_mday= "Setting day ? 0" code char string_week= "Setting week ? 0" code char string_hour= "Setting hour ? 0" code char string_minute="Setting minute? 0" code char string_second="Setting second? 0"/年月日时分秒的定义struct DATA_TIMEshort year;/待选char month;char mday ;char week ;char hour ;char minute;char second;time;struct time_char /time的字符型 char ch_year4; char ch_month2; char ch_mday2; char ch_week1; char ch_hour2; char ch_minute2; char ch_second2;time_ch;/*子函数*/LCD的延时程序void LCD_delay(void) char i,j; for(i=100;i>=0;i-)for(j=0;j<20;j+);/向LCD写指令函数void wcmd(char cmd ) RS=0; RW=0; E=1; LCD_delay(); PORT=cmd; LCD_delay(); E=0;/向LCD写数据函数void wdata(char DATA) RS=1; RW=0; E=1; LCD_delay(); PORT=DATA; LCD_delay(); E=0;/写字符串void wstring(char *pt,char i)/ i为字符的个数,不带0 char j; for(j=0;j<i;j+) wdata(ptj); /时间数据的格式转换void tran_2_ch(char *pt,char DATA) pt0=DATA/10+0x30; pt1=DATA-pt0*10+0x10;void tran_4_ch(char *pt,int DATA) pt0=DATA/1000; pt1=DATA/100-pt0*10; pt2=DATA/10-pt0*100-pt1*10; pt3=DATA-pt0*1000-pt1*100-pt2*10; pt0+=0x30; pt1+=0x30; pt2+=0x30; pt3+=0x30;/格式转换的实现void tran_ch(void) tran_4_ch(time_ch.ch_year ,time.year ); tran_2_ch(time_ch.ch_month,time.month); tran_2_ch(time_ch.ch_mday ,time.mday ); /tran_2_ch(time_ch.ch_week ,time.week*10+1); time_ch.ch_week0=time.week+0x30; tran_2_ch(time_ch.ch_hour ,time.hour ); tran_2_ch(time_ch.ch_minute,time.minute); tran_2_ch(time_ch.ch_second,time.second);/是否是闰年char leep_year(int year)/如果是闰年则flag_year_leep=1,否则=0 if(year%400=0)|year%4=0&&year%100!=0)return 1; else return 0;/星期的判断void de_week(void) int i,j,day=0; char mweek; for(i=2000;i<time.year;i+) if(leep_year(i)day+=366; else day+=365; for(j=1;j<time.month;j+) if(j=1|j=3|j=5|j=7|j=8|j=10|j=12) day+=31; if(j=4|j=6|j=9|j=11) day+=30; if(flag_year_leap&&j=2)day+=29; if(!flag_year_leap)&&j=2)day+=28; day+=time.mday; mweek=day%7; switch(mweek) case 0:time.week=5;break; case 1:time.week=6;break; case 2:time.week=7;break; case 3:time.week=1;break; case 4:time.week=2;break; case 5:time.week=3;break; case 6:time.week=4;break; time_ch.ch_week0=time.week+0x30; /月份的进位处理void time_carry_mon_year(void) flag_year_leap=leep_year(time.year);/判断闰年 if(time.mday>=30) if(time.month=1|time.month=3|time.month=5|time.month=7 |time.month=8|time.month=10|time.month=12) /天数为31天的月,1、3、5、7、8、10、12 if(time.mday=32) time.mday=1;time.month+; if(time.month=4|time.month=6|time.month=9|time.month=11) if(time.month=31) time.mday=1;time.month+; tran_ch(); if(time.month=2&&time.mday>=28) if(flag_year_leap&&(time.mday=30) time.mday=1;time.month+; if(!flag_year_leap)&&(time.mday=29) time.mday=1;time.month+; tran_ch(); if(time.month=13) time.month=1;time.year+;tran_ch();/年的进位/时间的进位void time_carry(void) if(time.second=60) time.minute+;time.second=0; if(time.minute=60) time.hour+ ;time.minute=0; if(time.hour =24) time.mday+ ;time.hour=0 ;time_carry_mon_year();de_week();/屏幕的显示函数void dsp(void) if(flag_en_key=0)/非设置模式下的显示 tran_ch(); wcmd(0x02);/return cursor wstring(time_ch.ch_hour,2); wdata(':'); wstring(time_ch.ch_minute,2); wdata(':'); wstring(time_ch.ch_second,2); wstring(" ",3); wcmd(0x80+0x40); wstring(" ",3); wstring(time_ch.ch_year,4); wdata('-'); wstring(time_ch.ch_month,2); wdata('-'); wstring(time_ch.ch_mday,2); wstring(" W",2); wdata(time_ch.ch_week0); if(flag_en_key=1)/设置模式下的显示 if(flag_set<=2) tran_ch(); wcmd(0x02);/return cursor wstring(time_ch.ch_year,4); wdata('-'); wstring(time_ch.ch_month,2); wdata('-'); wstring(time_ch.ch_mday,2); if(flag_set>2) tran_ch(); wcmd(0x02);/return cursor wstring(time_ch.ch_hour,2); wdata(':'); wstring(time_ch.ch_minute,2); wdata(':'); wstring(time_ch.ch_second,2); wstring(" ",3); /显示字符串string_ void dsp_string(char *pt) char i=0; wcmd(0x80+0x40);/将光标调整至第二行 while(pti) wdata(pti); i+; /退出按键模式void exit_key(void) char i; flag_en_key=0; flag_en_ok=0; flag_ok=0; flag_mean=0; flag_set=0; /清除所有标记 wcmd(0x80+0x40); for(i=0;i<=16;i+) wdata('0');/按键的处理子函数(由外中断调用)int solve_key(void) /if(key_mean=0); if(key_ok=0)/确定或退出 if(flag_ok) exit_key(); time_carry_mon_year(); de_week(); return 0; else flag_ok=1; if(key_set =0) if(flag_ok=0)/时分秒的选择 flag_set+; if(flag_set=6)flag_set=0; switch(flag_set) case 0:dsp_string(string_year);break; case 1:dsp_string(string_month);break; case 2:dsp_string(string_mday);break; case 3:dsp_string(string_hour);break; case 4:dsp_string(string_minute);break; case 5:dsp_string(string_second);break; if(flag_ok=1)/时分秒的设置 switch(flag_set) case 0:time.year+ ;if(time.year=10000)time.year=2000;break; case 1:time.month+ ;time_carry_mon_year();break; case 2:time.mday+ ;time_carry_mon_year();break; case 3:time.hour+ ;if(time.hour=24)time.hour=0;break; case 4:time.minute+;if(time.minute=60)time.minute=0;break; case 5:time.second+;if(time.second=60)time.second=0;break; if(flag_ok=1)/是否更改数据 是显示 OK 否显示 ? wcmd(0x80+0x4e); wdata('O'); wdata('K'); else wcmd(0x80+0x4e); wdata('0'); wdata('?'); return 0;/MCU的秒中断函数与显示函数的执行void INT_sec() interrupt 1 using 0 if(flag_sec=20)/flag_sec=20代表1s flag_sec=0; TH0=0x3c; TL0=0xb0+10;/定时器再次进入0.05s的中断 /加10是为了抵消中断与前两个指令消耗的时间(不精确) time.second+; flag_en_ok=0;/去掉key_ok的特殊优先权 time_carry(); dsp(); else flag_sec+; TH0=0X3C; TL0=0XB0+10;/定时器再次进入0.05s的中断 /外中断0,键盘中断 INT_X0(void) interrupt 0 using 0 LCD_delay();/去抖 TR0=0;/关闭定时计数器0 if(key_mean=0) flag_en_ok=1; flag_ok=0; dsp_string(string_year); if(flag_en_ok&&key_ok=0) flag_en_key=1;flag_en_ok=0; if(flag_en_key=1) solve_key(); TR0=1;/启动定时计数器0/LCD 的初始化void init(void) E=0;/确定E在初始化时为0 wcmd(0x01); wcmd(0x06); wcmd(0x0c); wcmd(0x38);/*main函数*/int main() /单片机的初始化 EA=1; ET0=1;/定时计数器0的使能 EX0=1;/外中断0的使能 IT0=1;/下降沿触发中断 IP=0x02;/定时计数器0的中断优先级最高 TMOD=0x01;/定时计数器0为工作模式1 TH0=0x3c; TL0=0xb0;/定时计数器参数为0x3cb0,0xffff-0x3cb0=50000,一个中断中期为0.05s TR0=1;/MCU初始化后直接启动定时计数器0 time.year=2000;/数据的初始化 time.month=1; time.mday=1; time.week=6; time.hour=0; time.minute=0; time.second=0; init(); /LCD 初始化 while(1);/单片机等待中断12 / 12