51单片机电子日历(电子时钟)
51基于单片机的电子万年历毕业设计[管理资料]
单片机课程设计报告电子万年历设计姓名:周义学号:0520010058 专业班级:05电气(6)班指导老师:张宏伟所在学院:万方科技学院摘要随着科技的快速发展,时间的流逝,至从观太阳、摆钟到现在电子钟,人类不断研究,不断创新纪录。
美国DALLAS公司推出的具有涓细电流充电能的低功耗实时时钟电路DS1302。
它可以对年、月、日、周日、时、分、秒进行计时,还具有闰年补偿等多种功能,而且DS1302的使用寿命长,误差小。
对于数字电子万年历采用直观的数字显示,可以同时显示年、月、日、周日、时、分、秒和温度等信息,还具有时间校准等功能。
该电路采用AT89S52单片机作为核心,功耗小,能在3V的低压工作,电压可选用3~5V电压供电。
综上所述此万年历具有读取方便、显示直观、功能多样、电路简洁、成本低廉等诸多优点,符合电子仪器仪表的发展趋势,具有广阔的市场前景。
本设计是基于51系列的单片机进行的电子万年历设计,可以显示年月日时分秒及周信息,具有可调整日期和时间功能。
在设计的同时对单片机的理论基础和外围扩展知识进行了比较全面准备。
在硬件与软件设计时,没有良好的基础知识和实践经验会受到很大限制,每项功能实现时需要那种硬件,程序该如何编写,算法如何实现等,没有一定的基础就不可能很好的实现。
具体实现功能:(1)显示年月日时分秒及星期信息(2)具有可调整日期和时间功能(3)与即时时间同步目录1方案论证 (3) (3) (3) (4) (4)2系统的硬件设计与实现 (5) (5) (5) (5) (5) (6) (7) (8)3系统的软件设计 (9) (9)4测试与结果分析 (11) (12) (12) (12)测试结果分析 (12)测试结论 (12)5prodeus软件仿真........................................ ..........错误!未定义书签。
ISIS简介 (12) (13) (13)6课程设计总结与体会.......................................... .....错误!未定义书签。
基于stc51单片机的LCD1602显示时间的电子万年历(显示
1 课设所需软件简介1.1 Keil uVision4的简要介绍2009年2月发布Keil μVision4,Keil μVision4引入灵活的窗口管理系统,使开发人员能够使用多台监视器,并提供了视觉上的表面对窗口位置的完全控制的任何地方。
新的用户界面可以更好地利用屏幕空间和更有效地组织多个窗口,提供一个整洁,高效的环境来开发应用程序。
新版本支持更多最新的ARM芯片,还添加了一些其他新功能。
2011年3月ARM公司发布最新集成开发环境RealView MDK开发工具中集成了最新版本的Keil uVision4,其编译器、调试工具实现与ARM器件的最完美匹配。
Keil C51开发系统基本知识Keil C51开发系统基本知识1. 系统概述Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。
另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。
在开发大型软件时更能体现高级语言的优势。
下面详细介绍Keil C51开发系统各部分功能和使用。
2. Keil C51单片机软件开发系统的整体结构C51工具包的整体结构,uVision与Ishell分别是C51 for Windows和for Dos 的集成开发环境(IDE),可以完成编辑、编译、连接、调试、仿真等整个开发流程。
开发人员可用IDE本身或其它编辑器编辑C或汇编源文件。
然后分别由C51及C51编译器编译生成目标文件(.OBJ)。
目标文件可由LIB51创建生成库文件,也可以与库文件一起经L51连接定位生成绝对目标文件(.ABS)。
ABS文件由OH51转换成标准的Hex文件,以供调试器dScope51或tScope51使用进行源代码级调试,也可由仿真器使用直接对目标板进行调试,也可以直接写入程序存贮器如EPROM中。
使用独立的Keil仿真器时,注意事项:* 仿真器标配11.0592MHz的晶振,但用户可以在仿真器上的晶振插孔中换插其他频率的晶振。
基于51单片机的电子日历
#include<reg52.h>#define uchar unsigned char#define uint unsigned intsbit LCD_RS = P2^6;sbit LCD_RW = P2^5;sbit LCD_EP = P2^7;sbit SDA=P2^1;sbit SCL=P2^0;sbit s1=P3^0;sbit s2=P3^1;sbit s3=P3^2;sbit beep=P1^5;uchar count=0,s1num=0,week=5;int second,minute,hour,year1=20,year2=10,mouth=12,day=25;unsigned char code mouth_0[]={31,28,31,30,31,30,31,31,30,31,30,31};//设置闰年和非闰年每个月的天数unsigned char code mouth_1[]={31,29,31,30,31,30,31,31,30,31,30,31};void delay0(){;;}void delay(uint z)//延时函数{uint x,y;for(x=z;x>0;x--)for(y=110;y>0;y--);}void di(){beep=0;delay(100); //扬声器beep=1;}void start()//24c02开始信号{SDA=1;delay0();SCL=1;delay0();SDA=0;delay0();}void stop()//24c02停止信号{SDA=0;SCL=1;delay0();SDA=1;delay0();}void respons()//应答{uchar i;SCL=1;delay0();while((SDA==1)&&(i<250))i++;SCL=0;delay0();}void init()//I2c初始化{SDA=1;delay0();SCL=1;delay0();}void write_byte(uchar date)//写一个字节{uchar i,temp;temp=date;for(i=0;i<8;i++){temp=temp<<1;SCL=0;delay0();SDA=CY;delay0();SCL=1;delay0();}SCL=0;delay0();SDA=1;delay0();}uchar read_byte()//读一个字节{SCL=0;delay0();SDA=1;delay0();for(i=0;i<8;i++){SCL=1;delay0();k=(k<<1)|SDA;SCL=0;delay0();}return k;}void write_add(uchar address,uchar date)//指定地址写一个字节{start();write_byte(0xa0);respons();write_byte(address);respons();write_byte(date);respons();stop();}uchar read_add(uchar address)//指定地址读一个字节{uchar date;start();write_byte(0xa0);respons();write_byte(address);respons();start();write_byte(0xa1);respons();date=read_byte();stop();return date;}void write_com(uchar com)//液晶写命令函数{LCD_RS = 0;LCD_RW = 0;LCD_EP = 0;P0=com;delay0();LCD_EP=1;delay(5);LCD_EP=0;}void write_date(uchar date)//液晶写数据函数{LCD_RS = 1;LCD_RW = 0;LCD_EP = 0;P0=date;delay(5);LCD_EP=1;delay(5);LCD_EP=0;}void write_sfm(uchar add,uchar date)//写时分秒函数{uchar shi,ge;shi=date/10;ge=date%10;write_com(0x80+0x40+add);write_date(0x30+shi);write_date(0x30+ge);}void write_nyr(uchar add,uchar date)//写年、月、日函数{char shi,ge;shi=date/10;ge=date%10;write_com(0x80+add);write_date(0x30+shi);write_date(0x30+ge);}void write_week(char we)//写星期函数{write_com(0x80+12);switch(we){case 1: write_date('M');delay0();write_date('O');delay0();write_date('N');break;case 2: write_date('T');delay0();write_date('U');delay0();write_date('E');break;case 3: write_date('W');delay0();write_date('E');delay0();write_date('D');break;case 4: write_date('T');delay0();write_date('H');delay0();write_date('U');break;case 5: write_date('F');delay0();write_date('R');delay0();write_date('I');break;case 6: write_date('S');delay0();write_date('A');delay0();write_date('T');break;case 7: write_date('S');delay0();write_date('U');delay0();write_date('N');break;}}void init1()//初始化函数{second=0;minute=0;hour=0;init();write_com(0x38);//初始化1602液晶write_com(0x0c);write_com(0x06);write_com(0x01);write_com(0x80);//设置显示初始坐标write_com(0x80+0x40+2);//写出时间显示部分的两个冒号write_date(':');write_com(0x80+0x40+5);write_date(':');write_com(0x80+4);//写出日期显示部分的两个-号write_date('-');write_com(0x80+7);write_date('-');second=read_add(1);//首次上电从24C02读取出存储的数据minute=read_add(2);hour=read_add(3);day=read_add(5);week=read_add(4);mouth=read_add(6);year2=read_add(7);write_sfm(6,second);//分别送去液晶显示write_sfm(3,minute);write_sfm(0,hour);write_nyr(0,year1);write_nyr(2,year2);write_nyr(8,day);write_nyr(5,mouth);write_week(week);}void keyscan(){int year=year1*100+year2;if(s1==0){delay(5);if(s1==0){s1num++;while(!s1);di();switch(s1num){//光标显示位置case 1: TR0=0;write_com(0x80+0x40+6);write_com(0x0f);break;case 2: write_com(0x80+0x40+3);break;case 3: write_com(0x80+0x40+0);break;case 4: write_com(0x80+12);break;case 5: write_com(0x80+8);break;case 6: write_com(0x80+5);break;case 7: write_com(0x80+2);break;case 8: s1num=0;TR0=1;write_com(0x0c);break;}}}if(s1num!=0){if(s2==0){delay(5);if(s2==0){while(!s2);di();switch (s1num){case 1: second++;if(second==60)second=0;write_sfm(6,second);write_com(0x80+0x40+6);write_add(1,second);break;case 2: minute++;if(minute==60)minute=0;write_sfm(3,minute);write_com(0x80+0x40+3);write_add(2,minute);break;case 3: hour++;if(hour==24)hour=0;write_sfm(0,hour);write_com(0x80+0x40+0);write_add(3,hour);break;case 4: week++;if(week>7)week=1;write_week(week);write_add(4,week);break;case 5: day++;if(year%4==0||(year%100==0&&year%400==0))//判断是否为闰年{if(day>mouth_1[mouth-1])//判断是否大于当前月的天数,是则将天数置1day=1;}else if(day>mouth_0[mouth-1])//不是闰年,判断是否大于当前月的天数,是则置1day=1;write_nyr(8,day);write_add(5,day);break;case 6: mouth++;if(mouth>12)mouth=1;write_nyr(5,mouth);break;case 7: year2++;if(year2>99){year2=0;year1++;write_nyr(0,year1);write_add(8,year1);}write_nyr(2,year2);write_add(7,year2);break;}}}if(s3==0){delay(5);if(s3==0){while(!s3);di();switch (s1num){case 1: second--;if(second<0)second=59;write_sfm(6,second);write_com(0x80+0x40+6);write_add(1,second);break;case 2: minute--;if(minute<0)minute=59;write_sfm(3,minute);write_com(0x80+0x40+3);write_add(2,minute);break;case 3: hour--;if(hour<0)hour=23;write_sfm(0,hour);write_com(0x80+0x40+0);break;case 4: week--;if(week==0)week=7;write_week(week);write_add(4,week);break;case 5: day--;if(year%4==0||(year%100==0&&year%400==0))//判断是否为闰年{if(day==0)day=mouth_1[mouth-1];}else if(day==0)day=mouth_0[mouth-1];write_nyr(8,day);write_add(5,day);break;case 6: mouth--;if(mouth==0)mouth=12;write_nyr(5,mouth);write_add(6,mouth);break;case 7: year2--;if(year2<0){year2=99;year1--;write_nyr(0,year1);write_add(8,year1);}write_nyr(2,year2);write_add(7,year1);break;}}}}}void main(){init1();TMOD=0x01;TH0=(65536-50000)/256;TL0=(65536-50000)%256;IE=0x82;TR0=1;while(1)keyscan();}void time0()interrupt 1{int year=year1*100+year2;TH0=(65536-50000)/256;TL0=(65536-50000)%256;if(++count==20){count=0;second++;if(second==60){second=0;minute++;if(minute==60){minute=0;hour++;if(hour==24){hour=0;day++;week++;if(week>7)week=1;write_week(week);write_add(4,week);if(year%4==0||(year%100==0&&year%400==0))//判断是否为闰年{if(day>mouth_1[mouth-1])//判断天数是否大于当前这个月的天数,是则将天数置1{day=1;mouth++;if(mouth>12){mouth=1;year2++;if(year2>100){year1++;write_nyr(0,year1);}write_nyr(2,year2);}write_nyr(5,mouth);}write_nyr(8,day);}else if(day>mouth_0[mouth-1]) {day=1;mouth++;if(mouth>12){ mouth=1;year2++;if(year2==100){year2=0;year1++;write_nyr(0,year1);}}write_nyr(2,year2);write_add(7,year2);write_nyr(5,mouth);write_add(6,mouth);}write_nyr(8,day);write_add(5,day);}write_sfm(0,hour);write_add(3,hour);}write_sfm(3,minute);write_add(2,minute);}write_sfm(6,second);write_add(1,second);} }。
基于51单片机,电子显示时钟带闹钟、整点报时、日期、星期
{ StrTab[1]=second/10; //秒十位
StrTab[0]=second%10; //秒个位
StrTab[2]=10; //间隔符-
StrTab[4]=minute/10; //分十位
StrTab[3]=minute%10; //分个位
StrTab[5]=10; //间隔符-
void display(uchar w[32])
{ unsigned int i,j,c=0;
if(a==0)//正常时间显示
{ for(i=0;i<8;i++) //依次将数组w中八个数取出,并显示
{ P2=weikong_code[i]; //位选
j=w[i]; //取出要显示的数码
P0=tab[j]; //取出段选编码
if(month==13)
{month=1; year++;
if(year==10000)
year=0;}}
week++;//星期走
if(week==8)
week=1;
data1();
week1();
while(second==err);
}
}
/**********************键盘扫描子程序*************************/
{if(dБайду номын сангаасy==30); //闰年29天
{day=1; month++;
if(month==13)
{month=1; year++;
if(year==10000)
year=0;}}}
基于51单片机的日历时钟显示
SCLK BIT P1.0 ;DS1302时钟口P1.
IO BIT P1.1 ;DS1302数据口P1.1
RST BIT P1.2 ;DS1302片选口P1.2
NOP ;延时等待
NOP
SETB RST ;使能片选
NOP ;延时等待
NOP
CALL DS1302_WriteByte ;写地址
BUSY:
PUSH ACC
CLR RS
SETB RW
TT0: SETB E
MOV A,P0
CLR E
ANL A,#80H
JNZ TT0
DJNZ R4,$
DJNZ R3,D2
RET
EE:
MOV A,@R0
ANL A,#0FH ;先转换低位
MOV @R1,A
INC R1 ;转换高位
MOV A,@R0
ANL A,#0F0H
SWAP A
MOV @R1,A
INC R0
RET
DS1302_WriteData:
CLR RST ;拉低片选
NOP ;延时等待
NOP
CLR SCLK ;时钟线拉低
LCALL BUSY
LCALL WRTC
MOV R5,#0BH
MOV R1,#6AH
LCALL DELAY
LCALL WRTD
MOV A,#0C0H ;显示开及光标设置
MOV A,#01H ;清屏
LCALL BUSY
LCALL WRTC
MOV A,#06H ;显示光标移动设置
LCALL BUSY
51单片机c语言电子万年历完整程序
该程序为51单片机c语言电子万年历#include"reg52.h"//#include<stdio.h>#define uchar unsigned char#define uint unsigned intsbit lcden=P3^4;sbit lcdrs=P3^5;sbit DATA=P0^7;sbit RST=P0^5;sbit SCLK=P0^6;sbit menu=P3^0; //菜单sbit add=P3^1; //加一sbit dec=P3^7; //减一sbit led0=P1^0;sbit led1=P1^1;sbit led2=P1^2;sbit led3=P1^3;sbit ds=P3^2;//sbit beep=P3^3;uint temp;float f_temp;uint warn_l1=270;uint warn_l2=250;uint warn_h1=300;uint warn_h2=320;uint get_temp();void delayms(uint x);void write_com(uchar com);void write_data(uchar date);void init();void dis_temp(uint t);void Write1302(uchar dat);void WriteSet1302(uchar Cmd,uchar dat);uchar Read1302(void);uchar ReadSet1302(uchar Cmd);void Init_DS1302(void);void DisplaySecond(uchar x);void DisplayMinute(uchar x);void DisplayHour(uchar x);void DisplayDay(uchar x);void DisplayMonth(uchar x);void DisplayYear(uchar x);void DisplayWeek(uchar x);void dis_temp(uint t);void read_date(void);void turn_val(char newval,uchar flag,uchar newaddr,uchar s1num);void key_scan(void);char code table[]="0123456789" ;uchar code table2[]= "TUEWESTHUFRISATSUNMON"; uchar second,minute,hour,day,month,year,week,count=0; uchar ReadValue,num,time;void delayms(uint x){uint i,j;for(i=x;i>0;i--)for(j=110;j>0;j--);}////////////////////////////////////////////////////////////void write_com(uchar com){lcdrs=0;P2=com;delayms(5);lcden=1;delayms(5);lcden=0;}void write_data(uchar date){lcdrs=1;P2=date;delayms(5);lcden=1;delayms(5);lcden=0;}void init(){lcden=0;write_com(0x38);write_com(0x0c);write_com(0x06);write_com(0x01);}/////////////////////////////////////////////////////////////////void Write1302(uchar dat){uchar i;SCLK=0; //拉低SCLK,为脉冲上升沿写入数据做好准备 delayms(2); //稍微等待,使硬件做好准备for(i=0;i<8;i++) //连续写8个二进制位数据DATA=dat&0x01; //取出dat的第0位数据写入1302delayms(2); //稍微等待,使硬件做好准备SCLK=1; //上升沿写入数据delayms(2); //稍微等待,使硬件做好准备SCLK=0; //重新拉低SCLK,形成脉冲dat>>=1; //将dat的各数据位右移1位,准备写入下一个数据位 }}void WriteSet1302(uchar Cmd,uchar dat){RST=0; //禁止数据传递SCLK=0; //确保写数居前SCLK被拉低RST=1; //启动数据传输delayms(2); //稍微等待,使硬件做好准备Write1302(Cmd); //写入命令字Write1302(dat); //写数据SCLK=1; //将时钟电平置于已知状态RST=0; //禁止数据传递}uchar Read1302(void){uchar i,dat;delayms(2); //稍微等待,使硬件做好准备for(i=0;i<8;i++) //连续读8个二进制位数据dat>>=1; //将dat的各数据位右移1位,因为先读出的是字节的最低位if(DATA==1) //如果读出的数据是1dat|=0x80; //将1取出,写在dat的最高位SCLK=1; //将SCLK置于高电平,为下降沿读出delayms(2); //稍微等待SCLK=0; //拉低SCLK,形成脉冲下降沿delayms(2); //稍微等待}return dat; //将读出的数据返回}uchar ReadSet1302(uchar Cmd){uchar dat;RST=0; //拉低RSTSCLK=0; //确保写数居前SCLK被拉低RST=1; //启动数据传输Write1302(Cmd); //写入命令字dat=Read1302(); //读出数据SCLK=1; //将时钟电平置于已知状态RST=0; //禁止数据传递return dat; //将读出的数据返回}void Init_DS1302(void){WriteSet1302(0x8E,0x00); //根据写状态寄存器命令字,写入不保护指令WriteSet1302(0x80,((0/10)<<4|(0%10))); //根据写秒寄存器命令字,写入秒的初始值WriteSet1302(0x82,((59/10)<<4|(59%10))); //根据写分寄存器命令字,写入分的初始值WriteSet1302(0x84,((23/10)<<4|(23%10))); //根据写小时寄存器命令字,写入小时的初始值WriteSet1302(0x86,((28/10)<<4|(28%10))); //根据写日寄存器命令字,写入日的初始值 WriteSet1302(0x88,((2/10)<<4|(2%10))); //根据写月寄存器命令字,写入月的初始值WriteSet1302(0x8c,((14/10)<<4|(14%10))); //nian//WriteSet1302(0x8a,((4/10)<<4|(4%10)));}/////////////////////////////////////////////////////////////////void DisplaySecond(uchar x){uchar i,j;i=x/10;j=x%10;write_com(0x80+0x46);write_data(i+0x30);write_com(0x80+0x47);write_data(j+0x30);write_com(0x80+0x48);write_data(' ');dis_temp(get_temp());}void DisSecond(uchar x){uchar i,j;ReadValue = ReadSet1302(0x81);second=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);i=x/10;j=x%10;write_com(0x80+0x46);write_data(i+0x30);write_com(0x80+0x47);write_data(j+0x30);}void DisplayMinute(uchar x) {uchar i,j;i=x/10;j=x%10;write_com(0x80+0x43);write_data(i+0x30);write_com(0x80+0x44);write_data(j+0x30);write_com(0x80+0x45);write_data(':');}void DisplayHour(uchar x){uchar i,j;i=x/10;j=x%10;write_com(0x80+0x40);write_data(i+0x30);write_com(0x80+0x41);write_data(j+0x30);write_com(0x80+0x42);write_data(':');}void DisplayDay(uchar x) {uchar i,j;i=x/10;j=x%10;write_com(0x89);write_data(i+0x30);write_com(0x8a);write_data(j+0x30); }void DisplayMonth(uchar x) {uchar i,j;i=x/10;j=x%10;write_com(0x86);write_data(i+0x30); write_com(0x87);write_data(j+0x30);write_com(0x88);write_data('/');}void DisplayYear(uchar x) {uchar i,j;i=x/10;j=x%10;write_com(0x81);write_data(2+0x30);write_com(0x82);write_data(0+0x30);write_com(0x83);write_data(i+0x30);write_com(0x84);write_data(j+0x30);write_com(0x85);write_data('/');}void DisplayWeek(uchar x){ uchar i;x=x*3;// write_com(0x8c);write_data(table2[x]);// write_com(0x8d);write_data(table2[x+1]);// write_com(0x8e);write_data(table2[x+2]);write_com(0x8c);for(i=0;i<3;i++){write_data(table2[x]);x++;}}void read_date(void){ReadValue = ReadSet1302(0x81);second=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = ReadSet1302(0x83);minute=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = ReadSet1302(0x85);hour=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = ReadSet1302(0x87);day=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = ReadSet1302(0x89);month=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = ReadSet1302(0x8d);year=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue=ReadSet1302(0x8b); //读星期week=ReadValue&0x07;DisplayYear(year);DisplayMonth(month);DisplayDay(day);DisplayWeek(week);DisplayHour(hour);DisplayMinute(minute);DisplaySecond(second);dis_temp(get_temp()); ///温度显示key_scan(); ///按键检测}void turn_val(char newval,uchar flag,uchar newaddr,uchar s1num){newval=ReadSet1302(newaddr); //读取当前时间newval=((newval&0x70)>>4)*10+(newval&0x0f); //将bcd码转换成十进制 if(flag) //判断是加一还是减一{newval++;switch(s1num){ case 1: if(newval>99) newval=0;DisplayYear(newval);break;case 2: if(newval>12) newval=1;DisplayMonth(newval);break;case 3: if(newval>31) newval=1;DisplayDay(newval);break;case 4: if(newval>6) newval=0;DisplayWeek(newval);break;case 5: if(newval>23) newval=0;DisplayHour(newval);break;case 6: if(newval>59) newval=0;DisplayMinute(newval);break;case 7: if(newval>59) newval=0;DisplaySecond(newval);break;default:break;}}else{newval--;switch(s1num){ case 1: if(newval==0) newval=99;DisplayYear(newval);break;case 2: if(newval==0) newval=12;DisplayMonth(newval);break;case 3: if(newval==0) newval=31;DisplayDay(newval);break;case 4: if(newval<0) newval=6;DisplayWeek(newval);break;case 5: if(newval<0) newval=23;DisplayHour(newval);break;case 6: if(newval<0) newval=59;DisplayMinute(newval);break;case 7: if(newval<0) newval=59;DisplaySecond(newval);break;default:break;}}WriteSet1302((newaddr-1),((newval/10)<<4)|(newval%10)); //将新数据写入寄存器}//////////////////////////////////////void dsreset(void){uint i;ds=0;i=103;while(i>0)i=4;while(i>0)i--;}bit tempreadbit(void){uint i;bit dat;ds=0;i++;ds=1;i++;i++;dat=ds;i=8;while(i>0)i--;return(dat);}uchar tempread(void){uchar i,j,dat;dat=0;for(i=1;i<=8;i++){j=tempreadbit();dat=(j<<7)|(dat>>1); }return(dat);}void tempwritebyte(uchar dat) {bit testb;for(j=1;j<=8;j++){testb=dat&0x01;dat=dat>>1;if(testb){ds=0;i++;i++;ds=1;i=8;while(i>0)i--;}else{ds=0;i=8;while(i>0)i--;ds=1;i++;i++;}}}void tempchange(void) {dsreset();delayms(1);tempwritebyte(0xcc);tempwritebyte(0x44);key_scan(); //////按键函数}uint get_temp(){uchar a,b;tempchange(); //////温度函数dsreset();delayms(1);tempwritebyte(0xcc);tempwritebyte(0xbe);a=tempread();b=tempread();temp=b;temp<<=8;temp=temp|a;f_temp=temp*0.0625;temp=f_temp*10+0.5;f_temp=f_temp+0.05;return temp;}//////void dis_temp(uint t){uchar n1,n2,n3;n1=t/100;n2=t%100/10;n3=t%100%10;DisSecond(second); ///秒显示 write_com(0x80+0x49);write_data(table[n1]);//delayms(5);write_com(0x80+0x4a);write_data(table[n2]);//delayms(5);write_com(0x80+0x4b);write_data('.');// delayms(5);write_com(0x80+0x4c);write_data(table[n3]);//delayms(5);write_com(0x80+0x4d);write_data('^');//delayms(5);write_com(0x80+0x4e);write_data('C');//delayms(5);DisSecond(second); ////秒显示}/*********************液晶显示*****************/ void warn(uint s,uchar led){uchar i;i=s;// beep=0;P1=~(led);while(i--){dis_temp(get_temp());}// beep=1;P1=0xff;i=s;while(i--){dis_temp(get_temp());}}void deal(uint t){uchar i;if((t>warn_l2)&&(t<=warn_l1)){warn(40,0x01);}else if(t<=warn_l2){warn(10,0x03);}else if((t<warn_h2)&&(t>=warn_h1)) {warn(40,0x04);}else if(t>=warn_h2){warn(10,0x0c);}else{i=40;while(i--){dis_temp(get_temp());DisSecond(second);}}}///////////////////////////////////////void main(){init();Init_DS1302();while(1){tempchange();read_date();deal(temp);key_scan();}}////******************************************* void key_scan(void){// uchar miao,s1num=0;uchar s1num=0;if(menu==0){delayms(5);if(menu==0){while(!menu);s1num++;while(1){if(menu==0){delayms(5);if(menu==0){while(!menu);s1num++;}}// miao=ReadSet1302(0x81);// second=miao;// WriteSet1302(0x80,miao|0x80);write_com(0x0f);//光标闪射if(s1num==1){ //year=ReadSet1302(0x8d);write_com(0x80+4); //年光标if(add==0){delayms(3);if(add==0){ while(!add);turn_val(year,1,0x8d,1);}}if(dec==0){delayms(3);if(dec==0){ while(!dec);turn_val(year,0,0x8d,1);}}}if(s1num==2){//month=ReadSet1302(0x89);write_com(0x80+7); //月光标if(add==0){delayms(3);if(add==0){ while(!add);turn_val(month,1,0x89,2);}}if(dec==0){delayms(3);if(dec==0){ while(!dec);turn_val(month,0,0x89,2);}}}if(s1num==3){ //day=ReadSet1302(0x87);write_com(0x80+10);//日光标{delayms(3);if(add==0){ while(!add);turn_val(day,1,0x87,3);}}if(dec==0){delayms(3);if(dec==0){ while(!dec);turn_val(day,0,0x87,3); //写入日寄存器 }}}if(s1num==4){ //week=ReadSet1302(0x8b);write_com(0x80+14); //星期光标if(add==0){delayms(3);if(add==0){ while(!add);turn_val(week,1,0x8b,4);}}if(dec==0){delayms(3);{ while(!dec);turn_val(week,0,0x8b,4);}}}if(s1num==5){// hour=ReadSet1302(0x85)write_com(0x80+0x40+1); //时光标if(add==0){delayms(3);if(add==0){ while(!add);turn_val(hour,1,0x85,5);}}if(dec==0){delayms(3);if(dec==0){ while(!dec);turn_val(hour,0,0x85,5);}}}if(s1num==6)//调时间分{ // minute=ReadSet1302(0x83);write_com(0x80+0x40+4);if(add==0){delayms(5);if(add==0){ while(!add);turn_val(minute,1,0x83,6); //写入分寄存器}}if(dec==0){delayms(3);if(dec==0){ while(!dec);turn_val(minute,0,0x83,6); //写入分寄存器}}}if(s1num==7)//调时间秒{// second=ReadSet1302(0x81);write_com(0x80+0x40+7);//秒光标if(add==0){delayms(3);if(add==0){ while(!add);if(second==0x60)second=0x00;turn_val(second,1,0x81,7);}}if(dec==0){delayms(3);if(dec==0){ while(!dec);turn_val(second,0,0x81,7);}}}if(s1num==8){// miao=ReadSet1302(0x81);// second=miao;// WriteSet1302(0x80,second&0x7f);s1num=0;//s1num清零//write_com(0x0c);//光标不闪烁//break;}}}}}。
51单片机电子日历
51单片机电子日历(电子时钟)硬件实验箱是伟福LAB2000实验箱。
代码:/* 电子日历,有时间显示、闹铃、日期、秒表及键盘设置功能 */能键A: 设置位数字+1 闹钟模式下为闹钟开关秒表模式下为记时开关 */能键B: 设置位数字-1 闹钟模式下为闹钟开关 */能键C:设置模式及设置位选择秒表模式下为清零键 */ 能键D:在四种工作模式下切换设置闹钟开关 */dede/***************这里设置程序初始化时显示的时间****************/#define SET_HOUR 12 /*设置初始化小时*/#define SET_MINUTE 00 /*设置初始化分钟*/#define SET_SECOND 00 /*设置初始化秒数*/************************系统地址****************************/efine BASE_PORT 0x8000 /*选通基地址*/efine KEY_LINE BASE_PORT+1 /*键盘行线地址#define KEY_COLUMN BASE_PORT+2 /*键盘列线地址*/efine LED_SEG BASE_PORT+4 /*数码管段选地址*/efine LED_BIT BASE_PORT+2 /*数码管位选地址*/efine LED_ON(x) XBYTE[LED_BIT]=(0x01<<X)&NBSP; *6位led的位选通,带参数宏,参数为0~5*=""efine LED_OFF XBYTE[LED_SEG]=0x00 /*LED显示空*//**************在设置模式下对秒分时的宏定义*****************/#define SECOND 0 /*对应数码管右边两位*/#define MINUTE 1 /*对应数码管中间两位*/#define HOUR 2 /*对应数码管左边两位*//********************定义四种工作模式***********************/#define CLOCK clockstr /*时钟模式*/#define ALART alartstr /*闹钟模式*/#define DATE datestr /*日期模式*/#define TIMER timerstr /*秒表模式*//****************以下是所有子函数的声明*********************/void sys_init(void); /*系统的初始化程序*/void display(void); /*动态刷新一次数码管子程序*/void clockplus(void); /*时间加1S的子程序*/void update_clockstr(void); /*更新时间显示编码*/void update_alartstr(void); /*更新闹钟时间的显示编码*/void update_datestr(void); /*更新日期显示编码*/void update_timerstr(void); /*更新秒表时间的显示编码*/void deley(int); /*延时子程序*/void update_dispbuf(unsigned char *); /*更新显示缓冲区*/unsigned char getkeycode(void); /*获取键值子程序*/void keyprocess(unsigned char); /*键值处理子程序*/unsigned char getmonthdays(unsigned int,unsigned char);/*计算某月的天数子程序*//*功能键功能子函数*/void Akey(void); /*当前设置位+1 开关闹钟开关秒表*/void Ckey(void); /*设置位选择秒表清零*/void Dkey(void); /*切换四种工作模式*//**********************全局变量声明部分unsigned char led[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};/*从0~9的LED编码*/unsigned char ledchar[3]={0x5c,0x54,0x71};/*o n f*///unsigned char key[24]={ /* 键值代码数组对应键位:*/// 0x70,0x71,0x72,0x73,0x74,0x75, /* 7 8 9 A TRACE RESET*/// 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5, /* 4 5 6 B STEP MON */// 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5, /* 1 2 3 C HERE LAST */// 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5}; /* 0 F E D EXEC NEXT */struct{ /*时间结构体变量*/unsigned char s;unsigned char m;unsigned char h;}clock={SET_SECOND,SET_MINUTE,SET_HOUR};struct{ /*闹铃时间结构体变量*/unsigned char m;unsigned char h;}alart={SET_MINUTE,SET_HOUR};struct{ /*日期结构体变量*/unsigned int year;unsigned char month;unsigned char day;}date={6,1,1};struct{ /*秒表时间结构体变量*/unsigned char ms;unsigned char s;unsigned char m;}timer={0,0,0};unsigned char dispbuf[6]; /*显示缓冲区数组*/unsigned char clockstr[6]; /*时间显示的数码管编码数组*/unsigned char alartstr[6]; /*闹钟显示的数码管编码数组*/unsigned char datestr[6]; /*日期显示的数码管编码数组*/unsigned char timerstr[6]; /*秒表显示的数码管编码数组*/unsigned int itime=0,idot; /*定时器0中断计数*/unsigned char itime1=0; /*定时器1中断计数*/sbit P3_1=P3^1; /*外接蜂鸣器的管脚*/bdata bit IsSet=0; /*设置模式标志位0:正常走时1:设置模式*/bdata bit Alart_EN=0; /*闹铃功能允许位0:禁止闹铃1:允许闹铃*/bdata bit IsBeep=0; /*响铃标志位0:未响铃1:正在响铃*/unsigned char SetSelect=0; /*在设置模式IsSet=1时,正在被设置的位,对应上面的宏*/unsigned char *CurrentMode; /*标志当前正设置的功能,如CurrentMode=CLOCK或CurrentMode=ALART等*/void timerplus(void);/**************************函数部分*************************/void main(void){sys_init();{XBYTE[KEY_COLUMN,0x00]; /*给键盘列线赋全零扫描码,判断是否有键按下 */while((XBYTE[KEY_LINE]&0x0f)==0x0f) /*检测是否有键按下,无则一直进行LED的刷新显示*/{if(Alart_EN&&(clock.h==alart.h)&&(clock.m==alart.m)) {IsBeep=1;}else{ IsBeep=0;P3_1=0;}display();}keyprocess(getkeycode()); /*有键按下时得到键值,并送入键值处理程序*/display(); /*可要可不要*/}}void sys_init(void){TMOD=0x22; /*定时器0和1都设置为工作方式2,基准定时250×2=500us=0.5ms*/TH0=6; /*定时器0中断服务用来产生1秒时钟定时及闹钟蜂鸣器蜂鸣脉冲*/TL0=6; /*定时器1中断服务留给秒表使用,产生1/100秒定时*/TH1=6;TL1=6;ET0=1;ET1=1;EA=1;TR0=1;update_clockstr(); /*初始化时钟显示编码数组*/update_alartstr(); /*初始化闹钟显示编码数组*/update_datestr(); /*初始化日期显示编码数组*/update_timerstr(); /*初始化秒表显示编码数组*/update_dispbuf(clockstr);/*初始化显示缓冲数组*/CurrentMode=CLOCK; /*默认的显示摸式为时钟*/P3_1=0; /*蜂鸣器接线引脚复位*/}void timer0(void) interrupt 1 using 1 /*定时器0中断服务器,用来产生1秒定时*/{itime++;if(itime==1000){if(IsSet) /*在设置模式下,对正在设置的位闪烁显示*/{dispbuf[SetSelect*2]=0; /*对正在设置的位所对应的显示缓冲区元素赋0,使LED灭*/ dispbuf[SetSelect*2+1]=0;}if(IsBeep) P3_1=!P3_1; /*闹钟模式时,产生峰鸣器响脉冲*/if(CurrentMode==CLOCK){dispbuf[2]=dispbuf[2]&0x7f;}}if(itime==2000) /*两千次计数为1S 2000×0.5ms=1s*/{itime=0; /*定时1s时间到,软计数清零*/clockplus(); /*时间结构体变量秒数加1 */update_clockstr(); /* 更新时间显示编码数组 */if(CurrentMode!=TIMER) update_dispbuf(CurrentMode); /* 用时间编码数组更新显示缓冲区 */ }}void timer1(void) interrupt 3 using 2 /*定时器1中断服务器,用来产生1/100秒定时*/{idot++;if(++itime1==20) /*20*0.5ms=10ms*/{itime1=0;timerplus();update_timerstr();if(CurrentMode==TIMER){update_dispbuf(timerstr);dispbuf[2]=dispbuf[2]&0x7f; /*关闭小数点的显示*/dispbuf[4]=dispbuf[4]&0x7f;if(idot<1000) /*闪烁显示小数点*/{dispbuf[2]=dispbuf[2]|0x80;dispbuf[4]=dispbuf[4]|0x80;}else{dispbuf[2]=dispbuf[2]&0x7f;dispbuf[4]=dispbuf[4]&0x7f;}}}if(idot==2000) idot=0;}/*功能模块子函数*/void clockplus(void) /*时间加1s判断分,时子函数*/{if(++clock.s==60) /*秒位判断*/{clock.s=0;if(++clock.m==60) /*分位判断*/{clock.m=0;if(++clock.h==24) /*时位判断*/{if(++date.day==(getmonthdays(date.year,date.month)+1)){date.day=1;if(++date.month==13) date.month=1;}}}}}void timerplus() /*秒表1/100秒位加1,判断秒、分子程序*/{if(++timer.ms==100){timer.ms=0;if(++timer.s==60){timer.s=0;if(++timer.m==60){timer.m=0;}}}}void update_clockstr(void) /*更新时钟显示代码数组clockstr*/{clockstr[0]=led[clock.s%10]; /*给元素0赋相应数码管显示编码,编码序号是秒数的个位*/clockstr[1]=led[(int)(clock.s/10)]; /*给元素1赋相应数码管显示编码,编码序号是秒数的十位*/ clockstr[2]=led[clock.m%10]; /*以下类推*/clockstr[3]=led[(int)(clock.m/10)];clockstr[4]=led[clock.h%10];clockstr[5]=led[(int)(clock.h/10)];}void update_alartstr(void) /*更新闹钟显示代码数组alartstr*/{ /*右边两位显示on:闹钟开启 of:闹钟关闭*/if(Alart_EN) alartstr[0]=ledchar[1];/*显示字母n*/else alartstr[0]=ledchar[2]; /*显示字母f*/alartstr[1]=ledchar[0]; /*显示字母o*/alartstr[2]=led[alart.m%10];alartstr[3]=led[(int)(alart.m/10)];alartstr[4]=led[alart.h%10];alartstr[5]=led[(int)(alart.h/10)];}void update_datestr(void) /*更新日期显示代码数组datestr*/{datestr[0]=led[date.day%10];datestr[1]=led[(int)(date.day/10)];datestr[3]=led[(int)(date.month/10)];datestr[4]=led[date.year%10];datestr[5]=led[(int)(date.year/10)];}void update_timerstr(void) /*更新秒表显示代码数组timerstr*/{timerstr[0]=led[timer.ms%10];timerstr[1]=led[(int)(timer.ms/10)];timerstr[2]=led[timer.s%10];timerstr[3]=led[(int)(timer.s/10)];timerstr[4]=led[timer.m%10];timerstr[5]=led[(int)(timer.m/10)];}void display(void) /*刷新显示六位LED一次*/{unsigned char i;for(i=0;i<6;i++){LED_ON(i); /*选通相应位*/XBYTE[LED_SEG]=dispbuf[i]; /*写显示段码*/deley(50); /*延时显示*/LED_OFF; /*写LED全灭段码*/}}void update_dispbuf(unsigned char *str) /*更新显示缓冲区子函数,参数为要用来更新缓冲区的源字符数组的首地址*/ {dispbuf[0]=str[0]; /*将要更新的源字符数组内容COPY至dispbuf数组,用作显示缓冲区*/dispbuf[1]=str[1];dispbuf[2]=str[2]|0x80; /*默认把时位和分位后面的小数点显示出来,根据需要再取舍*/dispbuf[3]=str[3];dispbuf[4]=str[4]|0x80;dispbuf[5]=str[5];}void deley(int i) /*延时子函数*/{while(i--);}unsigned char getkeycode(void) /*键盘扫描子程序,返回获得的键码*/{unsigned char keycode; /*键码变量,一开始存行码*/unsigned char scancode=0x20; /*列扫描码*/unsigned char icolumn=0; /*键的列号*/display(); /*用刷新数码管显示的时间去抖*/XBYTE[KEY_COLUMN]=0x00;keycode=XBYTE[KEY_LINE]&0x0f; /*从行端口读入四位行码*/while((scancode&0x3f)!=0) /*取scancode的低六位,只要没变为全0,则执行循环*/{if((XBYTE[KEY_LINE]&0x0f)==keycode) break; /*检测按键所在的列跳出循环*/scancode=scancode>>1; /*列扫描码右移一位*/icolumn++; /*列号加1*/}keycode=keycode<<4; /*把行码移到高四位*/keycode=keycode|icolumn; /*由行码和列码组成键码*///等待按键放开XBYTE[KEY_COLUMN]=0x00;while((XBYTE[KEY_LINE]&0x0f)!=0x0f) display();return keycode;}void keyprocess(unsigned char keycode) /*键值处理函数*/{switch (keycode){case 0x73: Akey();break;case 0xB3: Bkey();break;case 0xD3: Ckey();break;case 0xE3: Dkey();break;default: break;}update_dispbuf(CurrentMode);}unsigned char getmonthdays(unsigned int year,unsigned char month)/*得到某月的天数*/ {unsigned char days;switch (month){case 4:case 6:case 9:case 11:days=30;break;case 2: if(year%4==0) days=29;else days=28;break;default:days=31;break;}return days;}/*功能键子函数部分*/void Akey(void) /*对当前设置位进行加一操作,如果设置秒位,则给秒位清零*/if(CurrentMode==TIMER) /*秒表模式下启闭走时*/{ TR1=!TR1;return;}if(!IsSet) return; /*如果不是设置模式退出*/switch (SetSelect){case SECOND:if(CurrentMode==CLOCK){clock.s=0; /*如果当前被设置位是秒位,则清零秒位*/update_clockstr();}if(CurrentMode==ALART){Alart_EN=!Alart_EN;update_alartstr();}if(CurrentMode==DATE){if(++date.day==(getmonthdays(date.year,date.month)+1)) date.day=1; update_datestr();}if(CurrentMode==TIMER){TR1=!TR1;}break;case MINUTE:if(CurrentMode==CLOCK){if(++clock.m==60) clock.m=0; /*如果当前被设置分位,则分位加1*/update_clockstr();}if(CurrentMode==ALART){if(++alart.m==60) alart.m=0;update_alartstr();}if(CurrentMode==DATE){if(++date.month==13) date.month=1;update_datestr();}break;case HOUR: if(CurrentMode==CLOCK){if(++clock.h==24) clock.h=0; /*如果当前被设置时位,则时位加1*/update_clockstr();if(CurrentMode==ALART){if(++alart.h==24) alart.h=0;update_alartstr();}if(CurrentMode==DATE){if(++date.year==100) date.year=0;update_datestr();}break;default: break;}update_dispbuf(CurrentMode);}void Bkey(void) /*对当前设置位进行减一操作,如果设置秒分,则给秒位清零,类比Akey()函数*/{if(!IsSet) return;switch (SetSelect){case SECOND:if(CurrentMode==CLOCK){clock.s=0;update_clockstr();}if(CurrentMode==ALART){Alart_EN=!Alart_EN;update_alartstr();}if(CurrentMode==DATE){if(--date.day==0x00) date.day=getmonthdays(date.year,date.month); update_datestr();}break;case MINUTE:if(CurrentMode==CLOCK){if(--clock.m==0xff) clock.m=59;update_clockstr();}if(CurrentMode==ALART){if(--alart.m==0xff) alart.m=59;update_alartstr();}if(CurrentMode==DATE)if(--date.month==0x00) date.month=12;update_datestr();}break;case HOUR: if(CurrentMode==CLOCK){if(--clock.h==0xff) clock.h=23;update_clockstr();}if(CurrentMode==ALART){if(--alart.h==0xff) alart.h=23;update_alartstr();}if(CurrentMode==DATE){if(--date.year==0xffff) date.year=99;update_datestr();}break;default: break;}update_dispbuf(CurrentMode);}void Ckey(void) /*正常走时模式和设置模式的切换*/{if(CurrentMode==TIMER){TR1=0; /*初始化定时器1设置,停止秒表记时*/TH1=6;TL1=6;timer.ms=0; /*初始化秒表数组*/timer.s=0;timer.m=0;update_timerstr();}else{if(IsSet==0) /*在非秒表模式下,第一次按下C键进入设置模式,设置时位*/{IsSet=1; /*置位标志位,进入设置模式 */SetSelect=HOUR;return;} /*第二次按C键设置分位,第三次按键设置秒位,第四次按键完成退出设置*/if(SetSelect==0) /*按到第四次,即设置完秒位后,将标志位IsSet置0,完成设置*/ {IsSet=0; /*复位标志位,进入正常走时模式*/return;if(SetSelect>0) SetSelect--; /*设置位的标志变量SetSelect=0:时位 1:分位 2:秒位*/}}void Dkey(void) /*工作状态切换:时钟、闹钟、日期、秒表*/{if(CurrentMode==CLOCK) /*切换至闹钟,同时开关闹钟*/{ CurrentMode=ALART;Alart_EN=!Alart_EN;update_alartstr();return;}if(CurrentMode==ALART) /*切换至日期*/{ CurrentMode=DATE;return;}if(CurrentMode==DATE)/*切换至秒表,同时关闭设置模式*/{CurrentMode=TIMER;IsSet=0;return;}if(CurrentMode==TIMER) /*切换至时钟*/{CurrentMode=CLOCK;return;}}11。
51单片机电子时钟设计
51单片机电子时钟设计电子时钟是一种非常实用的电子设备,它可以准确地显示时间,并拥有一系列的功能,如闹钟、日历等。
使用51单片机设计电子时钟,可以实现这些功能,同时还能够进行功能扩展,更好地满足用户需求。
首先,我们需要硬件上的准备工作。
51单片机需要与时钟(晶振)和显示器(LCD模块)进行连接。
晶振是提供单片机时钟脉冲的源头,LCD模块用于显示时间和各种功能。
同时,在电路中还需要进行一些扩展,如实时时钟模块(RTC模块)、按键模块等。
在软件设计方面,主要需要考虑以下几个方面:1.时钟脉冲:通过配置晶振的频率,可以生成单片机所需的时钟脉冲。
这个脉冲控制了单片机的运行速度,从而影响到时钟的准确性。
需要根据晶振频率进行相关配置。
2.时间的获取和计算:通过RTC模块可以获取当前的时钟数据(包括年、月、日、时、分、秒)。
在程序中,需要通过相应的接口获取这些数据,并进行计算。
比如,在显示时钟的时候,可以通过获取秒数、分钟数和小时数,并将其转换为相应的字符串进行显示。
3.菜单和按键功能:为了实现更多的功能,我们可以通过按键来实现菜单切换和功能选择。
在程序中,需要对按键进行扫描,判断按键的状态,然后进行相应的操作。
比如,按下菜单键可以进入菜单界面,通过上下键选择不同的功能,再通过确定键进行确认。
4.闹钟功能:闹钟功能是电子时钟中常见的功能之一、通过设置闹钟时间,并进行闹钟的开启或关闭,可以在指定的时间点触发相应的报警动作。
在程序中,需要编写逻辑判断闹钟是否到达指定的时间,然后触发报警。
5.日历功能:除了显示时间,电子时钟还可以显示当前的日期,包括年、月、日。
在程序中,需要编写相关的逻辑来获取日期数据,并进行显示。
通过以上的步骤,我们可以基本实现一个简单的电子时钟功能。
当然,根据用户的需求,还可以进行更多的功能扩展,比如添加温湿度监测、自动调光等功能。
总结起来,51单片机电子时钟的设计主要包括硬件和软件两个方面。
51单片机课程设计——电子万年历
电子万年历的设计学院计算机与控制工程学院专业班级自动化学生姓名指导教师2010年6月25日引言随着社会、科技的发展,人类得知时间,从观太阳、摆钟到现在电子钟,不断研究、创新。
为了在观测时间的同时,能够了解其它与人类密切相关的信息,比如温度、星期、日期等,电子万年历诞生了,它集时间、日期、星期和温度功能于一身,具有读取方便、显示直观、功能多样、电路简洁等诸多优点,符合电子仪器仪表的发展趋势,具有广阔的市场前景。
二十一世纪的今天,最具代表性的计时产品就是电子万年历,它是近代世界钟表业界的第三次革命。
第一次是摆和摆轮游丝的发明,相对稳定的机械振荡频率源使钟表的走时差从分级缩小到秒级,代表性的产品就是带有摆或摆轮游丝的机械钟或表。
第二次革命是石英晶体振荡器的应用,发明了走时精度更高的石英电子钟表,使钟表的走时月差从分级缩小到秒级。
第三次革命就是单片机数码计时技术的应用(电子万年历),使计时产品的走时日差从分级缩小到1/600万秒,从原有传统指针计时的方式发展为人们日常更为熟悉的夜光数字显示方式,直观明了,并增加了全自动日期、星期、温度以及其他日常附属信息的显示功能,它更符合消费者的生活需求!因此,电子万年历的出现带来了钟表计时业界跨跃性的进步……我国生产的电子万年历有很多种,总体上来说以研究多功能电子万年历为主,使万年历除了原有的显示时间,日期等基本功能外,还具有闹铃,报警等功能。
商家生产的电子万年历更从质量,价格,实用上考虑,不断的改进电子万年历的设计,使其更加的具有市场。
本设计主要采用AT89C51单片机作为主控核心,由DS1302时钟芯片提供时钟、LED 动态扫描显示屏显示。
AT89C51单片机是由Atmel公司推出的,功耗小,电压可选用4~6V电压供电;DS1302时钟芯片是美国DALLAS公司推出的具有涓细电流充电功能的低功耗实时时钟芯片,它可以对年、月、日、星期、时、分、秒进行计时,还具有闰年补偿等多种功能,而且DS1302的使用寿命长,误差小;数字显示是采用的LED液晶显示屏来显示,可以同时显示年、月、日、星期、时、分、秒和温度等信息。
基于51单片机电子闹钟或万年历的设计课程设计
课程设计基于51单片机电子闹钟或万年历的设计目录目录 (1)1.项目背景 (4)1.1 项目研究的目的和意义 (4)1.2课题研究的内容 (4)2.方案的选择和和论证 (5)2.1 单片机型号的选择 (5)2.2 按键的选择 (5)2.3 显示器的选择 (5)2.4 计时部分的选择 (6)2.5 发音部分的设计 (6)2.6电路设计最终方案 (6)3. AT89C52单片机简介 (7)3.1单片机基本特性 (7)3.2单片机内部结构图 (7)3.3 单片机I/O引脚结构 (7)3.3.1 P0口 (7)3.3.2 P1口 (8)3.3.3 P2口 (8)3.3.4 P3口 (8)3.4单片机最小系统板 (9)4. 数字电子钟的设计原理和方法 (10)4.1 设计原理 (10)4.2 硬件电路的设计 (10)4.2.1 DS1302时钟芯片 (10)4.2.2 1602 液晶简介 (12)4.2.3 蜂鸣器驱动电路 (13)4.2.4 独立键盘电路 (14)5.软件部分的设计 (15)5.1程序流程图 (15)5.1.1 系统总流程图 (15)5.1.2 DS1302时钟程序流程图 (16)5.1.3 LCD显示程序流程图 (17)5.2程序的设计 (18)5.2.1 DS1302读写程序 (18)5.2.2液晶显示程序 (18)7.心得体会 (21)参考文献 (22)附录一系统原理图 (23)附录二系统程序 (24)1.项目背景1.1 项目研究的目的和意义20世纪末,电子技术获得了飞速的发展,在其推动下,现代电子产品几乎渗透了社会的各个领域,有力地推动了社会生产力的发展和社会信息化程度的提高,同时也使现代电子产品性能进一步提高,产品更新换代的节奏也越来越快。
时间对人们来说总是那么宝贵,工作的忙碌性和繁杂性容易使人忘记当前的时间。
忘记了要做的事情,当事情不是很重要的时候,这种遗忘无伤大雅。
但是,一旦重要事情,一时的耽误可能酿成大祸。
基于51单片机的电子万年历
#include<reg52.h> typedef unsigned char u8; typedef unsigned int u16; sbit A=P2^0;sbit BA=P2^1;sbit C=P2^2;sbit led = P3^0;sbit s7=P3^7;/*k1设置时间,k2定时。
k1,k2使用不自动弹起按键。
k3时分秒选择,k4加数字,k5减数字。
*/sbit k1=P1^0;sbit k2=P1^1;sbit k3=P1^2;sbit k4=P1^3;sbit k5=P1^4;u8 count=0,sec,min=44,hour=8,mon=9,day=28,ss=0;s=60,m=60,h=24,month=12,d=31; u16 year=2015,y=9999;#define DATA P0u16 code table[]={ 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//共阴数码管0-F编码void delay(u16 z){u16 x,y;for(x=z;x>0;x--)for(y=110;y>0;y--);}/*通过count获得hour,min,sec变量值*/ void timecontrol(){sec++;if(sec==60){sec=0;min++;if(min==60){min=0;hour++;if(hour>=24)//1d{hour=0;day++;switch(mon) {case 1:case 3:case 5:case 7:case 8:case 10:case 12:if(day==32){//一个月day=0;mon++;}break;case 4:case 6:case 9:case 11:if(day==31){//一个月day=0;mon++;}break;default: //2月if((year%4==0 && year%100!=0 ) || (year%100==0 && year%400==0))//闰年{if(day==30){day=0;mon++;}}else{if(day==29){day=0;mon++;}}break;}}if(mon>=12) //1年{mon=0;year++;if(year>=9999)year=0;}}}}void display(u16 year1,u8 mon1,u8 day1,u8 zs,u8 zm, u8 zh) {led=1;DATA=0x00;A=0;BA=0;C=0;DATA=table[zh/10];delay(3);DATA=0x00;A=1;BA=0;C=0;led=1;DATA=table[zh%10]|0x80;delay(3);DATA=0x00;A=0;BA=1;C=0;led=1;DATA=table[zm/10];delay(3);DATA=0x00;A=1;BA=1;C=0;led=1;DATA=table[zm%10]|0x80;delay(3);DATA=0x00;A=0;BA=0;C=1;led=1;DATA=table[zs/10];delay(3);DATA=0x00;A=1;BA=0;C=1;led=1;DATA=table[zs%10]|0x80;delay(3);DATA=0x00;led=0;A=0;BA=0;C=0;DATA=table[year1/1000]; delay(3);DATA=0x00;A=1;BA=0;C=0;led=0;DATA=table[year1%1000/100]; delay(3);DATA=0x00;A=0;BA=1;C=0;led=0;DATA=table[year1%1000%100/10]; delay(3);DATA=0x00;A=1;BA=1;C=0;led=0;DATA=table[year1%10];delay(3);DATA=0x00;A=0;BA=0;C=1;led=0;DATA=table[mon1/10]; delay(3);DATA=0x00;A=1;BA=0;C=1;led=0;DATA=table[mon1%10]; delay(3);DATA=0x00;A=0;BA=1;C=1;led=0;DATA=table[day1/10]; delay(3);DATA=0x00;A=1;BA=1;C=1;led=0;DATA=table[day1%10]; delay(3);void keyscan_settime() //时间设置模块{u16 ye;u8 mo,da, st,mt,ht;if(k1==0){delay(10);if(k1==0){st=sec,mt=min,ht=hour,ye=year,mo=mon,da=day;TR0=0; //关闭定时器while(k1==0) //等待用户按键{display(ye,mo,da,st,mt,ht);if(k3==0){delay(10);if(k3==0){while(!k3);ss++;if(ss==6) ss=0;}}/*通过ss选择时分秒设置*/if(k4==0) //加数{delay(10);if(k4==0){while(!k4);switch(ss){case 0:st++;if(st==60)st=0;break;case 1:mt++;if(mt==60)mt=0;break;case 2:ht++;if(ht==24)ht=0;break;case 3:ye++;if(ye==9999) ye=0;break;case 4:mo++;if(mo==13) mo=0;break;case 5:da++;if(da==32) da=0;break;default :;}}}if(k5==0) //减数{delay(10);if(k5==0){while(!k5);switch(ss){case 0:if(st>0)st--;else st=0;break;case 1:if(mt>0)mt--;else mt=0;break;case 2:if(ht>0)ht--;else ht=0;break;case 3:if(ye>0) ye--;else ye=0;break;case 4:if(mo>0) mo--;elsemo=0;break;case 5:if(da>0) da--;elseda=0;break;default : ;}}}}TR0=1;sec=st,min=mt,hour=ht,day=da,mon=mo,year=ye;}}display(year,mon,day,sec,min,hour);}void keyscan_time() //用户定时模块{if(k2==0){delay(10);if(k2==0){s=sec;m=min;h=hour,y=year,month=mon,d=day;while(k2==0) //开始定时设置{display(y,month,d,s,m,h);if(k3==0){delay(10);if(k3==0){while(!k3);ss++;if(ss==6) ss=0;}}/*通过ss选择时分秒设置*/if(k4==0) //加数{delay(10);if(k4==0){while(!k4);switch(ss){case 0:s++;if(s==60)s=0;break;case 1:m++;if(m==60)m=0;break;case 2:h++;if(h==24)h=0;break;case 3:y++;if(y==9999) y=0;break;case 4:month++;if(month==13) month=0;break;case 5:d++;if(d==32) d=0;break;default :;}}}if(k5==0) //减数{delay(10);if(k5==0){while(!k5);switch(ss){case 0:if(s>0)s--;else s=0;break;case 1:if(m>0)m--;else m=0;break;case 2:if(h>0)h--;else h=0;break;case 3:if(y>0) y--;else y=0;break;case 4:if(month>0) month--;elsemonth=0;break;case 5:if(d>0) d--;else d=0;break;default : ;}}}}}}}void buz(){if((year==y)&&(mon==month)&&(day==d)&&(hour==h)&&(min==m)&&(sec==s)) {s7=0;delay(1000);s7=1;}}void time0_init(){TMOD=0x01;TH0=0x4b;TL0=0xfe;EA=1;ET0=1;TR0=1;}void main(void){time0_init();while(1){keyscan_settime();. . .. . .keyscan_time();buz();}}void Time0() interrupt 1{TH0=0x4b;TL0=0xfe;count++;if(count==20){count=0;timecontrol();}}. 专业.专注.。
51单片机万年历实验原理
51单片机万年历实验原理1. 概述51单片机万年历实验是一项基于51单片机的实验项目,用于模拟和显示日期和时间信息,使其具备一定的时钟和日历功能。
本文将详细介绍该实验的原理和实现方法。
2. 实验所需材料完成51单片机万年历实验需要以下材料: - 51单片机开发板 - LCD显示屏 - 时钟芯片(如DS1302) - 电容 - 电阻 - 键盘模块 - 连接线等3. 实验原理本实验的原理主要包括三个方面:51单片机的控制逻辑、时钟芯片的数据存储和显示屏的信息展示。
3.1 51单片机的控制逻辑在51单片机中,首先需要定义和初始化各个引脚和功能模块。
通过引脚的输入输出控制、时钟和定时器的设置,实现对时钟芯片和LCD显示屏的控制和数据传输。
3.2 时钟芯片的数据存储时钟芯片一般具有独立的电源供应和存储空间,用于储存日期和时间等信息。
通过与51单片机的通讯接口,读取和写入时钟芯片中的数据,实现对日期和时间信息的读取和更新。
3.3 显示屏的信息展示LCD显示屏作为用户界面,用于展示日期和时间等信息。
通过51单片机的输出控制,将读取到的日期和时间信息通过LCD显示屏进行展示。
具体的显示方式可以根据需求设计,如以年、月、日的格式显示,或者以星期和时间的格式显示等。
4. 实验步骤基于以上原理,可以按照以下步骤进行51单片机万年历实验:4.1 硬件连接按照实验所需材料,将51单片机开发板、LCD显示屏和时钟芯片等进行正确的连接。
根据引脚功能和电平要求,通过连接线将它们连接在一起。
4.2 编写程序使用合适的集成开发环境(如Keil)编写51单片机的程序。
程序主要包括引脚和功能模块的初始化设置、时钟芯片数据的读写和LCD显示屏信息的输出等。
4.3 载入程序将编写好的程序通过USB下载线或其他方式,将程序载入到开发板中。
确保程序可以正确地运行在51单片机上。
4.4 测试实验接通电源,观察LCD显示屏是否正常显示日期和时间信息。
基于51单片机的电子日历
南昌工程学院Nanchang Institute of Technology课程设计题目电子日历的设计制作学生姓名班级学号指导教师日期2014 年 1 月 2 日技术交流:群:99133698QQ:1294976338南昌工程学院课程设计摘要本文介绍了基于AT89C51单片机的多功能电子万年历的硬件结构和软硬件设计方法。
系统以AT89C51单片机为控制器,以串行时钟日历芯片DS1302记录日历和时间,它可以对年、月、日、时、分、秒进行计时,还具有闰年补偿等多种功能。
万年历采用直观的数字显示,可以在LCD上同时显示年、月、日、星期、时、分、秒,还具有时间校准等功能;并能按月显示当月的日历表。
此万年历具有读取方便、显示直观、功能多样、电路简洁、成本低廉等诸多优点,具有广阔的市场前景。
关键字AT89C51;电子万年历; DS1302目录摘要 (I)第一章绪论 (2)1.1 课题研究的背景 (2)1.2课题的研究目的与意义 (2)1.3课题解决的主要内容 (2)第二章系统的总体设计 (4)2.1系统方案的构想与确定 (4)2.2 器件的选用 (4)2.3 系统框图设计 (5)第三章系统硬件的设计 (6)3.1 单片机最小系统 (6)3.1.1 51单片机 (6)3.1.2 复位及时钟震荡电路 (7)3.2 DS1302模块 (8)3.1.1 DS1302时钟芯片 (8)3.1.2 时钟模块电路 (10)3.3 液晶显示模块 (10)3.4 闹钟提醒模块 (11)3.5 矩阵键盘电路 (11)第四章系统的软件设计 (12)4.1 主程序 (12)4.2 闹钟提醒程序 (13)第五章系统仿真及调试 (14)5.1 日历显示模式仿真调试 (14)5.2 时间调整模式仿真调试 (15)结论 (16)参考文献 (17)附录I 原理图 (18)附录II 主程序代码 (19)第一章绪论1.1 课题研究的背景随着科技的快速发展,时间的流逝,从观太阳、摆钟到现在电子钟,人类不断研究,不断创新纪录。
51单片机 电子钟万年历设计
论文设计题目电子钟万年历学院专业电子信息工程班级指导老师2010年11月目录摘要 (I)Abstract ................................................................................................................................................................ I I 1 绪论 (1)1.1 系统基本方案选择和论证 (1)1.1.1 单片机芯片的选择 (1)1.1.2 显示模块选择方案和论证 (1)1.1.3 时钟芯片的选择方案和论证 (2)1.2 电路设计最终方案决定 (2)2 系统的硬件设计与实现 (3)2.1 电路设计框图 (3)2.2 系统硬件概述 (3)2.3 主要单元电路的设计 (3)2.3.1 AT89C51单片机主控制模块的设计 (3)2.3.2 单片机中断系统 (5)2.3.3时钟电路模块的设计 (7)2.3.4 显示模块的设计 (8)2.3.5 DS1302原理及说明 (9)2.3.6 12864工作原理及说明 (9)2.3.7 系统仿真电路 (10)3系统软件设计 (11)4 系统测试 (14)4.1 硬件测试 (14)4.2 软件测试 (14)5 结束语 (15)参考文献 (16)附录:程序 (17)2摘要本文借助电路仿真软件Protues对基于AT89C51单片机的电子万年历的设计方法及仿真进行了全面的阐述。
该电子万年历在硬件方面主要采用AT89C51单片机作为主控核心,由DS1302时钟芯片提供时钟、12864点阵液晶显示屏显示。
AT89C51单片机是由Atmel公司推出的,功耗小,电压可选用4~6V电压供电;DS1302时钟芯片是美国DALLAS公司推出的具有涓细电流充电功能的低功耗实时时钟芯片,它可以对年、月、日、星期、时、分、秒进行计时,还具有闰年补偿等多种功能,而且DS1302的使用寿命长,误差小;数字显示是采用的LED液晶显示屏来显示,可以同时显示年、月、日、星期、时、分、秒等信息。
51实现电子时钟程序带万年历的哦
}
else date[2]=date[2]+1;
break;
case 9: if( date[2]==30)
{
date[1]=date[1]+1;
date[2]=1;
break;
{
date[1]=date[1]+1;
date[2]=1;
break;
}
else date[2]=date[2]+1;
break;
case 6: if( date[2]==30)
{
//////////////////////////////////////////////////////////////
void timeadd(int i) //时间加1函数
{
if(i)
{
timeadd1--;
if(time[2]==59) //秒加1
P1=siwei;
P2=dispcode[y%10];
delay(120);
}
P1=sanwei;
P2=dispcode[y/10];
delay(120);
P1=siwei;
P2=dispcode[y%10];
delay(120);
#include <REG52.H>
#include <STDIO.H>
#define unchar unsigned char
#define unint unsigned int
int code dispcode[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; //数码管字形表,0-9
51单片机课程设计之日历闹钟
(三)题目三:日历时钟设计1.利用MCS-51内部定时器定时,编程实现年、月、日、时、分、秒的显示,显示格式为:年月日: 08-06-24时分秒: 09-12-35也可自行设定显示格式,两部分可按键切换显示。
2.能按键调整日期、时间的数值。
3.能设置报警时间,报警时间可设置两个以上。
4.其它扩展功能:如12小时制/24小时制等。
CLK BIT P1.6 ;时钟信号端DISP BIT P1.7 ;串出锁存端DBUF EQU 30H ;秒的最低位地址LED BIT P1.1TW2 BIT 21H ;加12的标志位AD1 EQU 50H ;秒AD2 EQU 51H ;分AD3 EQU 52H ;时AD4 EQU 53H ;天AD5 EQU 54H ;月AD6 EQU 55H ;年; 初始化存储单元结束ORG 0000HLJMP MAINORG 001BH ;T1断入口LJMP INT_INT1ORG 0030HMAIN: MOV R0,#AD1MOV R7,#06HLOOP0: MOV A,#00HMOV @R0,AINC R0DJNZ R7,LOOP0 ;R0~R7清零MOV AD1,# 23HMOV AD2,#0CHMOV AD3,#09HMOV AD4,#18HMOV AD5,#06HMOV AD6,#08H ;初始化时间为9:12:35,日期为08年6月24日SETB EASETB ET1 ;允许T1中断MOV TMOD,#10H ;T1许工作,选择方式1MOV TH0,#3CHMOV TL0,#0B0H ;送入计数初值0.05sMOV R5,#14H ;定时1s, 初始化结束SETB TR1 ;开启T1LOOP: LCALL KEY ;扫描键盘MOV P0,ACJNE A,#0FEH,NEXT1 ;0FEH为1键LCALL DY1MSLCALL KEYDONENEXT1: SJMP LOOP ;扫描键盘看是否需要调整日历DISPLAY: LCALL KEY ;扫描键盘CJNE A,#0DFH,L1 ;0DFH为6键SETB PSW.1 ;显示十二小时LJMP ML1: CJNE A,#0EFH,L2 ;0EFH为5键CLR PSW.1 ;显示二十四小时CLR TW2LJMP ML2: CJNE A,#0CFH,L ;0CFH为5+6键SETB PSW.1SETB TW2 ;TW2=1表示把十二表示法转换成二十四小时LJMP ML: CLR TW2M:LCALL KEYCJNE A,#9FH,NEXTL ;7FH为8键SETB PSW.5LJMP ZHUAN1NEXTL: CJNE A,#0BFH,ZHUAN1 ;0BFH为7键CLR PSW.5ZHUAN1: JB PSW.5,DAY ;PSW.5=0 调整时间,为1则调整日期MOV A,AD1 ;调整时间键码分离MOV B,#0AHDIV ABMOV R0,#DBUFMOV @R0,BINC R0MOV @R0,AMOV A,AD2MOV B,#0AHDIV ABINC R0MOV @R0,BINC R0MOV @R0,ALJMP M2M1: ;判断时间是否大于12MOV A,AD3MOV B,#0CHSUBB A,BJC N1MOV AD3,ALJMP N1M2:JNB TW2,N1 ;判断是否加12MOV A,AD3ADD A,#00HMOV AD3,ACLR TW2N1: MOV A,AD3MOV B,#0AHDIV ABINC R0MOV @R0,BINC R0MOV @R0,ASJMP DDISPDAY: ;调整日期键码分离MOV A,AD4MOV B,#0AHDIV ABMOV R0,#DBUFMOV @R0,BINC R0MOV @R0,AMOV A,AD5MOV B,#0AHDIV ABINC R0MOV @R0,BINC R0MOV @R0,AMOV A,AD6MOV B,#0AHDIV ABINC R0MOV @R0,BINC R0DDISP: ;显示,串入并出的方式,上升沿写入MOV R0,#DBUFMOV R7,#06HMOV DPTR,#TABLELOOP2:MOV A,@R0MOVC A,@A+DPTRMOV R6,#08H ;显示秒;CLR CLOOP3:CLR CLKRRC AMOV DISP,CSETB CLKDJNZ R6,LOOP3INC R0DJNZ R7,LOOP2RETKEYDONE: ;人工手动调整日历AGAINB: LCALL DY250MS ;防抖250msLCALL KEYCJNE A,#9FH,NEXTL1 ;7FH为8键SETB PSW.5LJMP ZHUAN12NEXTL1:CJNE A,#0BFH,ZHUAN12CLR PSW.5 ;以上为扫描键盘并设置PSW.5ZHUAN12:JB PSW.5,NEXT6B ;判断调整日期还是时间CJNE A,#0FDH,NEXTA ;0FDH为2键,如果按键值为FDH则结束LJMP BACKNEXTA: CJNE A,#0FBH,NEXTB ;0FBH为3键INC AD1 ;秒加1MOV A,AD1CJNE A,#3CH,AGAINAMOV AD1,#00HSJMP AGAINANEXTB: CJNE A,#0F7H,NEXTC ;0F7H为4键INC AD2 ;分加1MOV A,AD2CJNE A,#3CH,AGAINAMOV AD2,#00HSJMP AGAINANEXTC: CJNE A,#0EFH,AGAINA ;0EFH为5键INC AD3 ;时加1MOV A,AD3CJNE A,#18H,AGAINAMOV AD3,#00HNEXTT: SJMP AGAINANEXT6B:CJNE A,#0FDH,NEXTAB;JIESHUJIAN ;0FDH为2键LJMP BACKNEXTAB: CJNE A,#0FBH,NEXTBB ;0FBH为3键INC AD4 ;日加1MOV A,AD4CJNE A,#20H,AGAINAMOV AD4,#01HSJMP AGAINANEXTBB: CJNE A,#0F7H,NEXTCB ;0F7H为4键INC AD5 ;月加1MOV A,AD5CJNE A,#0DH,AGAINAMOV AD5,#01HSJMP AGAINANEXTCB: CJNE A,#0EFH,AGAINA ;0EFH为5键INC AD6 ;年加1MOV A,AD6CJNE A,#64H,AGAINAMOV AD6,#00HAGAINA: LCALL DISPLAYLJMP AGAINBBACK: RETINT_INT1:DJNZ R5 ,NEXTS1 ;循环20次,每次循环为0.05秒,共0.05*20=1秒MOV R5,#14HLCALL DISPLAY ;满一秒后的显示LJMP MENDNEXTS1: LJMP NEXTS ;满一秒后的显示MEND: MOV A,AD1CJNE A,#0AH,LAREMENDSETB LED ;满十秒关蜂鸣器,否则继续LAREMEND:INC AD1CJNE A,#3CH, NEXTS2LJMP MEND1NEXTS2: LJMP NEXTS ;以上为看是否修改秒,满60,分加一否则跳转继续MEND1: MOV AD1,#00HINC AD2MOV A,AD2CJNE A,#3CH,NEXTS3LJMP MEND2NEXTS3: LJMP NEXTS ;以上为看是否修改分,满60,时加一否则跳转继续MEND2: MOV AD2,#00HINC AD3MOV A,AD3CJNE A,#08h,NEXTAJMP DIANLIANGNEXT:CJNE A,#0Bh,NORMALDIANLIANG: CLR LED ;以上为两个蜂鸣器报警NORMAL:MOV A,AD3CJNE A,#18H,NEXTS4 ;不满二十四小时跳出LJMP MEND3 ;满二十四后跳到清零小时继续加天NEXTS4: LJMP NEXTSMEND3: MOV AD3,#00HINC AD4MOV A,AD5CJNE A,#02H,JIXUTIAN ;判断是否为二月份MOV A,AD6MOV B,#4HDIV ABMOV A,BJZ RUNNIAN ;判断是否为闰年PINGNIAN:MOV A,AD4 ;平年CJNE A,#1DH,NEXTSMOV AD4,#01HAJMP JIXUYUERUNNIAN: MOV A,AD4 ; 闰年CJNE A,#1EH,NEXTSMOV AD4,#01HAJMP JIXUYUEJIXUTIAN: ; 不为二月,再查看是那个月CJNE A,#01H,PAN1LJMP DAYUEPAN1: CJNE A,#03H,PAN3LJMP DAYUEPAN3: CJNE A,#04H,PAN4LJMP XIAOYUEPAN4: CJNE A,#05H,PAN5LJMP DAYUEPAN5: CJNE A,#06H,PAN6LJMP XIAOYUEPAN6: CJNE A,#07H,PAN7LJMP DAYUEPAN7: CJNE A,#08H,PAN8LJMP DAYUEPAN8: CJNE A,#09H,PAN9LJMP XIAOYUEPAN9 : CJNE A,#10H,PAN10LJMP DAYUEPAN10: CJNE A,#11H, DAYUELJMP XIAOYUEDAYUE:MOV A,AD4 ;月份:1,3,5,7,8,10,12CJNE A,#20H,NEXTSMOV AD4,#01HAJMP JIXUYUEXIAOYUE: MOV A,AD4 ; 月份:2,4,6,8,11 CJNE A,#1FH,NEXTSMOV AD4,#01HJIXUYUE: INC AD5 ; 满月后加年CJNE A,#0DH,NEXTSMOV AD5,#01HINC AD6MOV A,AD6CJNE A,#64H,NEXTS ; 年满100后清零MOV AD6,#00HNEXTS: ;再赋初值MOV TH0,#3CHMOV TL0,#0B0HRETIDY250MS: ; 延迟防抖250msMOV R4,#0FAHLOOP8: LCALL DY1MSDJNZ R4,LOOP8RETDY1MS: ; 延迟防抖1msMOV R5,#0FAHLOOP7: MOV R7,#70HDJNZ R5,LOOP7RET;键盘扫描子程序KEY:;SETB P1.7CLR P1.7 ;低电平锁存NOPNOPSETB P1.7MOV R7,#07H ;高电平输出MOV A,#0FFHAGAIN3: CLR P2.3NOPNOPSETB P2.3MOV C, P3.5 ;把p3.5的状态存入CRLC A ;键盘状态存入ADJNZ R7,AGAIN3 ;扫描七个按键RETTABLE: DB 42H,0F6H,13H,92H,0A6H,8AH,0AH,0F2H,02H,82H ;0 1 2 3 4 5 6 7 8 9 ;子程序功能索引:;KEY--扫描键盘;DDISP--显示;NEXTS--重新赋初值;DY1MS--延迟防抖1msEND。
基于51单片机电子万年历设计
一、引言电子万年历是一种以数字形式实时显示日期、星期和时间等信息的电子设备。
在现代人日常生活中,万年历是一种常见的小型电子产品。
本文将基于51单片机设计一款简单实用的电子万年历。
二、设计原理1.时钟模块:采用DS1302实时时钟模块。
DS1302通过三线式串行接口与51单片机进行通信,可以实时获取日期、星期和时间等信息。
2.显示模块:使用数码管显示日期、星期和时间等信息。
共使用四块共阳数码管,采用数码管模块进行驱动,通过IO口进行数据传输。
3.按键模块:设计四个按键,分别为设置、上、下和确定。
通过按键来调整日期、星期和时间等信息。
4.闹钟功能:加入闹钟功能,可以设定闹钟时间,到达设定时间时,会有提示音。
5.温湿度传感器:加入温湿度传感器,可以实时监测环境温湿度,并在数码管上进行显示。
6.外部电源:由于51单片机工作电压较高,需要使用外部电源进行供电。
三、硬件设计1.电源电路:使用稳压电源芯片LM7805进行5V稳压,将稳压后的电压供给单片机和各个模块。
2.时钟模块:DS1302模块与单片机通过串行通信进行连接。
时钟模块上的时钟信号、数据信号和复位信号分别与单片机的IO口相连。
3.数码管显示模块:共有四块共阳数码管,通过595芯片进行驱动。
单片机的IO口与595芯片的串行、时钟和锁存引脚相连,595芯片的输出引脚与数码管的各段相连。
4.按键模块:通过电阻分压来实现按键功能,按下按键时,相应的IO口会被拉低。
5.闹钟功能:使用蜂鸣器来产生提示音,通过IO口与单片机相连。
6.温湿度传感器:使用DHT11温湿度传感器。
传感器的数据引脚通过IO口与单片机相连。
四、软件设计1.时钟显示:通过DS1302获取日期、星期和时间等信息,将其转化为数码管需要的编码格式,并通过595芯片进行显示。
2.按键操作:对按键进行扫描,根据按键的不同操作进行相应的处理。
例如按下设置键进行日期和时间的设置,按下上下键进行数值的变化,按下确定键进行数值的确认。
基于51单片机的万年历-闹钟-秒表设计
基于51单片机的万年历,闹钟,秒表设计有关接线图完整的程序代码#include<reg52.h>#define uchar unsigned char#define uint unsigned intuchar code table[]={"20 年月日"};uchar code table1[]={" : : "};uchar code table5[]={" QI CHUANG LA"};void LCD_WRITE_COM(uchar com);void LCD_WRITE_DAT(uchar dat);void LCD_CSH();void LCD_GD();void LCD_CLR();void DELAYUS(uchar i);void DELAY(uint t);void DELAY_A(uint n);void DISP_TIME();void DISL1();void DISL3();void DS1302_CSH();void DS1302_WRITE(uchar addr,uchar dat); void WRITE_BTY(uchar dat);void DSweek(uchar num);void DS1820RST();void DS1820WR(uchar dat);void KEYSCAN();void KEYMOVE();void TIME_UP();void TIME_DOWN();uchar DS1820RD();uchar READ_T();uchar DS1302_READ(uchar addr);uchar READ_BTY();sbit IO=P3^5;sbit RST=P1^7;sbit SCLK=P1^6;sbit FMQ=P2^4;sbit DQ=P2^3; //DS18B20输出口sbit RS=P2^5; //寄存器选择信号sbit RW=P2^6; //读写控制信号线sbit LCDEN=P2^7; //使能信号线sbit S1=P1^0;sbit S2=P1^1;sbit S3=P1^2;sbit S4=P1^3;sbit S=P1^4;char BW,SW,GW;uchar t,tflag;uchar m,f,s,x,r,y,n;uchar A,A_m,A_f,A_s,A_x;uchar num1,num2;uchar flag,flag_A,flag_j;uchar shi,ge;uchar M_a,M_b,M_c,M_d,M_e,M_f,temp,ss; long int z=0,m1,m2;//*********延时*********void DELAY(uint t) //延时1MS{int x,y;for(x=t;x>0;x--)for(y=110;y>0;y--);}void DELAY_US(uint i) //延时1US {while(i--);}void DELAY_A(uint i){ uint j;char k;for(j=0;j<i;j++){ if(S4==0){DELAY(20);if(S4==0){break;}}for(k=110;k>0;k--){FMQ=1;DELAY(10);FMQ=0;DELAY(10);if(S4==0){DELAY(20); if(S4==0) {break;} }}}}//*********LCD模块*******void LCD_CSH(){ LCD_WRITE_COM(0x38); //设置液晶工作模式 16*2行显示,5*7点阵,8位数据DELAY(1);LCD_WRITE_COM(0x0c); //开显示DELAY(1);LCD_WRITE_COM(0x06); //光标移动DELAY(1);LCD_WRITE_COM(0x01); //清屏DELAY(1);}void LCD_WRITE_COM(uchar com){RW=0; //写RS=0; //寄存器模式选择,写命令P0=com; //写命令LCDEN=0;DELAY(1);LCDEN=1; //使能,0到1DELAY(1);LCDEN=0; //数据送入有效}void LCD_WRITE_DAT(uchar dat){RW=0;RS=1; //寄存器选择,写数据P0=dat; //写数据LCDEN=0;DELAY(1);LCDEN=1; //使能0到1DELAY(1);LCDEN=0; //数据送入有效}void LCD_WORD(unsigned char *p){while(*p>0){ LCD_WRITE_DAT(*p) ;p++;}}//固定显示void LCD_GD(){char i;LCD_WRITE_COM(0x80); //"20 年月日"for(i=0;i<15;i++){LCD_WRITE_DAT(table[i]);DELAY(1);}LCD_WRITE_COM(0x90); //" : : "for(i=0;i<11;i++){LCD_WRITE_DAT(table1[i]);}LCD_WRITE_COM(0x99);LCD_WRITE_DAT(0x03);LCD_WRITE_DAT(0x03);LCD_WRITE_COM(0x9A);LCD_WORD("萍水缘");LCD_WRITE_DAT(0x03);LCD_WRITE_DAT(0x03);}//清屏void LCD_CLR(){LCD_WRITE_COM(0x01);DELAY(2);}//上电欢迎界面void DISL1(){ LCD_WRITE_COM(0x80);LCD_WORD("基于51单片机的万年历,欢迎使用!"); }void DISL3() //闹钟时间到的显示界面{char i;LCD_WRITE_COM(0x80);for(i=0;i<15;i++){LCD_WRITE_DAT(table5[i]);DELAY(1);}LCD_WRITE_COM(0x90);for(i=0;i<15;i++){LCD_WRITE_DAT(table5[i]);DELAY(1);}}//********DS1302模块**********void DS1302_CSH()//(写程序要对照DS1302的各个写地址){RST=0;SCLK=0;DS1302_WRITE(0x8e,0x00);//允许写DS1302_WRITE(0x80,0x00);//初始秒0DS1302_WRITE(0x82,0x00);//初始分0DS1302_WRITE(0x84,0x15);//初始时0DS1302_WRITE(0x8a,0x01);//初始星期6DS1302_WRITE(0x86,0x04);//初始日1DS1302_WRITE(0x88,0x06);//初始月1DS1302_WRITE(0x8c,0x12);//初始年11DS1302_WRITE(0x8e,0x80);//写保护关}uchar DS1302_READ(uchar addr){uchar dat;RST=0; //初始CE为0SCLK=0; //初始时钟线为0RST=1; //传输开始WRITE_BTY(addr); //传送读取时间的地址dat=READ_BTY(); //读取时间SCLK=1; //时钟线拉高RST=0; //传输结束return dat; //返回时间}void DS1302_WRITE(uchar addr,uchar dat) {RST=0; //初始CE为0SCLK=0; //初始时钟线为0RST=1; //传输开始DELAY(1);WRITE_BTY(addr); //传送读取时间的地址WRITE_BTY(dat); //写入修改的时间SCLK=1; //时钟线拉高RST=0; //传输结束}uchar READ_BTY(){uchar i,dat=0;SCLK=0;DELAY(1);for(i=0;i<8;i++){dat=dat>>1;DELAY(1);if(IO==1) //如果读出数据是1(当前数据线为高时,证明该位数据为1)dat|=0x80; //要传输数据的当前位置为1,不是,则为0SCLK=1; //拉高时钟线DELAY(1);SCLK=0; //制造下降沿DELAY(1);}return dat;}void WRITE_BTY(uchar dat){uchar i;SCLK=0; //当前时钟线为0DELAY(1);for(i=0;i<8;i++) //开始传输8为数据{IO=dat&0x01; //取最低位DELAY(1);SCLK=0; //拉低时钟线DELAY(1);SCLK=1; //拉高时钟线dat=dat>>1; //数据右移一位,准备传输下一个数据}}//显示时间void DISP_TIME(){LCD_WRITE_COM(0x81);//显示年,DS1302的读地址8d为年位置,LCD显示在0x81位置shi=DS1302_READ(0x8d)/16;ge=DS1302_READ(0x8d)%16;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x83); //显示月,DS1302的读地址83为年位置shi=DS1302_READ(0x89)/16;ge=DS1302_READ(0x89)%16;LCD_WRITE_DAT(shi+0x30);LCD_WRITE_DAT(ge+0x30);LCD_WRITE_COM(0x85);//显示日shi=DS1302_READ(0x87)/16;ge=DS1302_READ(0x87)%16;LCD_WRITE_DAT(shi+0x30);LCD_WRITE_DAT(ge+0x30);LCD_WRITE_COM(0x90);//显示小时shi=DS1302_READ(0x85)/16;ge=DS1302_READ(0x85)%16;LCD_WRITE_DAT(shi+0x30);LCD_WRITE_DAT(ge+0x30);LCD_WRITE_COM(0x92);//显示分钟shi=DS1302_READ(0x83)/16;ge=DS1302_READ(0x83)%16;LCD_WRITE_DAT(shi+0x30);LCD_WRITE_DAT(ge+0x30);LCD_WRITE_COM(0x94);//显示秒shi=DS1302_READ(0x81)/16;ge=DS1302_READ(0x81)%16;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);DSweek(DS1302_READ(0x8b)); //显示星期if(A==1) //显示闹钟{LCD_WRITE_COM(0x87);LCD_WRITE_DAT(0x20);LCD_WRITE_DAT(0x0e);}if(A!=1){LCD_WRITE_COM(0x87);LCD_WRITE_DAT(' ');}}//显示时间的星期模块void DSweek(uchar num){LCD_WRITE_COM(0x95); //95位置清空,96.97显示周一,二,三,四,五,六,日LCD_WRITE_DAT(0x20);LCD_WRITE_DAT(0x20);LCD_WRITE_COM(0x96);switch(num){case 1:LCD_WRITE_DAT(0xD6);LCD_WRITE_DAT(0xDC);LCD_WRITE_DAT(0xD2);LCD_WRITE_DAT(0xBB);break;case 2:LCD_WRITE_DAT(0xD6);LCD_WRITE_DAT(0xDC);LCD_WRITE_DAT(0xB6);LCD_WRITE_DAT(0xFE);break;case 3:LCD_WRITE_DAT(0xD6); LCD_WRITE_DAT(0xDC);LCD_WRITE_DAT(0xC8);LCD_WRITE_DAT(0xFD);break;case 4:LCD_WRITE_DAT(0xD6); LCD_WRITE_DAT(0xDC);LCD_WRITE_DAT(0xCB);LCD_WRITE_DAT(0xC4);break;case 5:LCD_WRITE_DAT(0xD6); LCD_WRITE_DAT(0xDC);LCD_WRITE_DAT(0xCE);LCD_WRITE_DAT(0xE5);break;case 6:LCD_WRITE_DAT(0xD6); LCD_WRITE_DAT(0xDC);LCD_WRITE_DAT(0xC1);LCD_WRITE_DAT(0xF9);break;case 7:LCD_WRITE_DAT(0xD6); LCD_WRITE_DAT(0xDC);LCD_WRITE_DAT(0xC8);LCD_WRITE_DAT(0xD5);break;}}//**********温度模块*******void DS1820RST() //DS18b20的初始化函数{ uchar x=0;DQ=1; //DQ复位DELAY_US(4); //延时DQ=0; //DQ拉低DELAY_US(100); //精确延时大于480usDQ=1; //拉高DELAY_US(40);x=DQ;}uchar DS1820RD() //读一个字节{uchar i=0,dat=0;for (i=8;i>0;i--){DQ=0; //给脉冲信号 dat=dat>>1;DQ=1; //给脉冲信号if(DQ==1)dat|=0x80;DELAY_US(10);}return dat; //写一个字节}void DS1820WR(uchar dat){char i=0;for (i=8;i>0;i--){DQ=0;DQ=dat&0x01;DELAY_US(10);DQ=1;dat=dat>>1;}}uchar READ_T(){uchar a,b;DS1820RST();DS1820WR(0xcc);//跳过读序列号(固定)DS1820WR(0x44);//启动温度转换DS1820RST();DS1820WR(0xcc);//跳过读序列号DS1820WR(0xbe);//读取温度a=DS1820RD();b=DS1820RD();b<<=4;b+=(a&0xf0)>>4;t=b;return t;}//显示温度void DISP_T(){ uchar R1;R1=READ_T();LCD_WRITE_COM(0xc8);LCD_WRITE_DAT(0xCE);LCD_WRITE_DAT(0xC2);LCD_WRITE_DAT(0xB6);LCD_WRITE_DAT(0xC8);LCD_WRITE_DAT(':');if(R1<0x81){LCD_WRITE_DAT(0x30+R1/100);} else{R1=~(R1)+1;LCD_WRITE_DAT('-');}LCD_WRITE_DAT(0x30+R1%100/10);LCD_WRITE_DAT(0x30+R1%10);LCD_WRITE_DAT(0xA1);LCD_WRITE_DAT(0xE6);}//*******键盘******//读暂停时的时间void TIME(){if(flag==0){m=DS1302_READ(0x81); //分别读出秒,分,时,星期,日,月,年(DS1302的读地址应用)f=DS1302_READ(0x83);s=DS1302_READ(0x85);x=DS1302_READ(0x8b);r=DS1302_READ(0x87);y=DS1302_READ(0x89);n=DS1302_READ(0x8d);}}//时间更新void TIME_UPDATE(){DS1302_WRITE(0x8e,0x00); //写允许DS1302_WRITE(0x80,m); //分别写出秒,分,时,星期,日,月,年(DS1302的写地址应用)DS1302_WRITE(0x82,f);DS1302_WRITE(0x84,s);DS1302_WRITE(0x8a,x);DS1302_WRITE(0x86,r);DS1302_WRITE(0x88,y);DS1302_WRITE(0x8c,n);DS1302_WRITE(0x8e,0x80); //禁止写}//闹钟void ALARM_CLOCK(){LCD_CLR();DELAY(10);DISL3(); //显示起床DELAY_A(100);FMQ=0; //蜂鸣器响flag_A=0;LCD_CLR();LCD_GD(); //固定显示}//闹钟开关显示void ALARM_KG(){if(x!=1){A=0;LCD_WRITE_COM(0x85);LCD_WRITE_DAT(0xb9);LCD_WRITE_DAT(0xd8);x=0;}if(x==1){A=1;LCD_WRITE_COM(0x85);LCD_WRITE_DAT(0xbf);LCD_WRITE_DAT(0xaa);}}//*******秒表******void CSH(){EA=1;ET0=1;TMOD=0x01;TH0=(65535-4900)/256;TL0=(65535-4900)%256;TR0=0;ss=0;M_a=M_b=M_c=M_d=0;}//秒表的键盘扫描void KEYSCAN_M(){ S=0;if(S2==0){DELAY(100);if(S2==0){while(S2==0);ss=~ss;TR0=1;if(ss==0) //S2可以暂停或者继续TR0=0;}}if(S3==0){DELAY(100);if(S3==0){while(S3==0){M_a=M_b=M_c=M_d=0;}}}}void TIME_M() interrupt 1{TH0=(65535-4900)/256;TL0=(65535-4900)%256;temp++;if(temp==2){temp=0;M_c++;if(M_c==10){M_c=0;M_b++;if(M_b==10){M_b=0;M_a++;if(M_a==10){M_a=0;M_d++;if(M_d==6){M_d=0;M_e++;if(M_e==0){M_e=0;M_f++;}}}}}}}//显示秒表,fe:da:bcvoid DISP_M(){ LCD_WRITE_COM(0x83); //显示秒表LCD_WRITE_DAT(0xc3);LCD_WRITE_DAT(0xeb);LCD_WRITE_DAT(0xb1);LCD_WRITE_DAT(0xed);LCD_WRITE_COM(0x91); //显示00:00::00LCD_WRITE_DAT(0x30+M_f);LCD_WRITE_DAT(0x30+M_e);LCD_WRITE_DAT(':');LCD_WRITE_COM(0x93);LCD_WRITE_DAT(0x30+M_d);LCD_WRITE_DAT(0x30+M_a);LCD_WRITE_DAT(':');LCD_WRITE_COM(0x95);LCD_WRITE_DAT(0x30+M_b);LCD_WRITE_DAT(0x30+M_c);}//按键扫描,s1为功能显示,s2为光标移动/秒表暂停(开始),s3为加时间和秒表复位,s4为减时间和停止闹钟void KEYSCAN(){ S=0;if(S1==0){DELAY(100);if(S1==0)while(!S1);{num1++; // 界面切换switch(num1){case 1: num2=0;m=A_m;f=A_f;s=A_s;x=A_x;LCD_CLR();LCD_WRITE_COM(0x80); //显示闹钟设置LCD_WRITE_DAT(0xC4);LCD_WRITE_DAT(0xD6);LCD_WRITE_DAT(0xD6);LCD_WRITE_DAT(0xD3);LCD_WRITE_DAT(0xC9);LCD_WRITE_DAT(0xE8);LCD_WRITE_DAT(0xD6);LCD_WRITE_DAT(0xC3);LCD_WRITE_COM(0x94);LCD_WRITE_DAT(0x30+m/16); LCD_WRITE_DAT(0x30+m%16);LCD_WRITE_COM(0x92);LCD_WRITE_DAT(0x30+f/16); LCD_WRITE_DAT(0x30+f%16); LCD_WRITE_DAT(':');LCD_WRITE_COM(0x90);LCD_WRITE_DAT(0x30+s/16); LCD_WRITE_DAT(0x30+s%16); LCD_WRITE_DAT(':');LCD_WRITE_COM(0x8b);ALARM_KG();break;case 2: LCD_WRITE_COM(0x0c); //开显示LCD_CLR();break;case 3: num2=0;num1=0;A_m=m;A_f=f;A_s=s;A_x=x;LCD_WRITE_COM(0x0c);flag=0; //读暂停的时间标志位LCD_CLR();LCD_GD(); //固定显示break;}}}}//光标移动void KEYMOVE(){ S=0;if(S2==0){ DELAY(100);if(S2==0)while(!S2);{num2++;}}if(num1==0) //调整时间,日期{switch(num2){case 1: //S2按下一次TIME(); //读出暂停的时间flag=1;LCD_WRITE_COM(0x97); //光标在星期位置闪烁LCD_WRITE_COM(0x0f);TIME_UPDATE(); //时间更新,写入设置的时间break;case 2:LCD_WRITE_COM(0x94); //光标在秒位置闪烁TIME_UPDATE(); //时间更新,写入设置的时间break;case 3:LCD_WRITE_COM(0x92); //光标在分钟位置闪烁TIME_UPDATE(); //时间更新,写入设置的时间break;case 4:LCD_WRITE_COM(0x90); //光标在时位置闪烁TIME_UPDATE(); //时间更新,写入设置的时间break;case 5:LCD_WRITE_COM(0x85); //光标在日期位置闪烁TIME_UPDATE(); //时间更新,写入设置的时间break;case 6:LCD_WRITE_COM(0x83); //光标在月份位置闪烁TIME_UPDATE(); //时间更新,写入设置的时间break;case 7:LCD_WRITE_COM(0x81); //光标在年份位置闪烁TIME_UPDATE(); //时间更新,写入设置的时间break;case 8:LCD_WRITE_COM(0x0c); //开显示flag=0; // 读暂停的时间标志位TIME_UPDATE(); //时间更新,写入设置的时间num2=0; //复位 break;}}if(num1==1) //调整闹钟{switch(num2){case 1:LCD_WRITE_COM(0x85); //是够开闹钟LCD_WRITE_COM(0x0f);break;case 2:LCD_WRITE_COM(0x94); //光标在秒位置闪烁break;case 3:LCD_WRITE_COM(0x92); //光标在分钟位置闪烁break;case 4:LCD_WRITE_COM(0x90); //光标在小时位置闪烁break;case 5:LCD_WRITE_COM(0x0c); //开显示num2=0; //复位 break;}}if(S3==0) //加时间{ DELAY(100);if(S3==0)while(!S3);{TIME_UP();}}if(S4==0) //减时间{ DELAY(100);if(S4==0)while(!S4);{TIME_DOWN();}}}//加时间void TIME_UP(){switch(num2){case 1:x++; //星期加1if(x==0x08) x=1;if(num1==0){ DS1302_WRITE(0x8e,0x00);//写允许DS1302_WRITE(0x8a,x);DS1302_WRITE(0x8e,0x80); //写禁止DSweek(DS1302_READ(0x8b));//显示时间的星期模块}if(num1==1){ ALARM_KG();} //闹钟开关显示 break;case 2: m++; //秒加1if(m%16==10) m=(m&0xf0)+0x10;if(m==0x60) m=0;shi=m>>4;ge=m&0x0f;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x94);break;case 3:f++; //分钟加1if(f%16==10) f=(f&0xf0)+0x10;if(f==0x60) f=0;shi=f>>4;ge=f&0x0f;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x92);break;case 4:s++; //小时加1if(s%16==10) s=(s&0xf0)+0x10; if(s==0x24) s=0;shi=s>>4;ge=s&0x0f;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x90);break;case 5:r++; //日期加1if(r%16==10) r=(r&0xf0)+0x10; if(r==0x32) r=1;shi=r>>4;ge=r&0x0f;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x85);break;case 6:y++; //月份加1if(y%16==10) y=(y&0xf0)+0x10; if(y==0x13) y=1;shi=y>>4;ge=y&0x0f;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x83);break;case 7:n++; //年份加1if(n%16==10) n=(n&0xf0)+0x10; if(n==0x99) n=1;shi=n>>4;ge=n&0x0f;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x81);break;}}//减时间void TIME_DOWN(){switch(num2){case 1:x--; //星期减1if(x==0x00) x=7;if(num1==0){DS1302_WRITE(0x8e,0x00); DS1302_WRITE(0x8a,x);DS1302_WRITE(0x8e,0x80);DSweek(DS1302_READ(0x8b));}if(num1==1){ ALARM_KG();}break;case 2:m--; //秒减1if(m%16==15) m=(m&0xf0)+0x09; if(m==0xf9) m=0x59;shi=m>>4;ge=m&0x0f;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x94);break;case 3:f--; //分钟减1if(f%16==15) f=(f&0xf0)+0x09; if(f==0xf9) f=0x59;shi=f>>4;ge=f&0x0f;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x92);break;case 4:s--; //小时减1if(s%16==15) s=(s&0xf0)+0x09; if(s==0xf9) s=0x23;shi=s>>4;ge=s&0x0f;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x90);break;case 5:r--; //日期减1if(r%16==15) r=(r&0xf0)+0x09; if(r==0xf9) r=0x31;shi=r>>4;ge=r&0x0f;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x95);break;case 6:y--; //月份减1if(y%16==15) y=(y&0xf0)+0x09; if(y==0xf9) y=0x12;shi=y>>4;ge=y&0x0f;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x93);break;case 7:n--; //年减1if(n%16==15) n=(n&0xf0)+0x09; if(n==0xf9) n=0x99;shi=n>>4;ge=n&0x0f;LCD_WRITE_DAT(0x30+shi);LCD_WRITE_DAT(0x30+ge);LCD_WRITE_COM(0x91);break;}}void main(){ CSH();LCD_CSH();DELAY(10);DS1302_CSH();DISL1(); //上电欢迎界面DELAY(10000);LCD_CLR(); //清屏LCD_GD(); //固定显示while(1){ KEYSCAN();if(num1==2){while(1){DISP_M();KEYSCAN_M();if(S1==0){DELAY(100);if(S1==0)break;}}}KEYMOVE();if(num1==0&&flag==0){DISP_TIME();DISP_T();if(DS1302_READ(0x83)==A_f&&DS1302_READ(0x85)==A_s&&DS1302_R EAD(0x81)==A_m&&A==1){ALARM_CLOCK();}}}}。
51单片机万年历实验原理解析
51单片机万年历实验原理解析51单片机万年历(Real-time Clock)是一种集成电路,可以提供精确的日期和时间信息。
它是基于包含计时器和日历的时钟电路设计的。
本文将深入解析51单片机万年历实验的原理和实现过程。
1. 引言在现代科技领域中,人们对时间的准确性和精确度要求越来越高。
而万年历是一种可靠的工具,能够提供准确的日期和时间信息,因此被广泛应用在各种领域,如电子设备、通信系统和科学研究等。
51单片机万年历作为一种常用的设计方案,本文将对其原理和实验进行解析。
2. 51单片机概述51单片机是一种基于哈佛架构的芯片,常应用于嵌入式系统开发。
它具有易于编程、高性能和低功耗的特点,因此在电子行业中得到广泛应用。
而51单片机万年历则是在该芯片上实现的一种功能,它通过连接外部时钟电路和定时器模块,实现了精确的时间显示和日期计算。
3. 51单片机万年历实验原理我们需要连接一个可靠的时钟电路到51单片机上,以提供准确的时间基准。
这个时钟电路可以是一个晶体振荡器,它会产生一个稳定的频率信号,用来驱动定时器模块。
我们需要配置定时器模块,以便实现时间的计算和显示功能。
定时器可以设置定时时间和工作模式,例如计时模式和计数模式。
通过定时器中断,我们可以精确地控制和记录时间的变化。
接下来,我们需要编写一段程序来读取定时器的计数值,并将其转换为具体的日期和时间。
这个过程涉及到将计数值分解为年、月、日、时、分、秒,然后进行相应的转换和计算。
我们将通过数码管、液晶显示屏或其他输出设备来显示转换后的日期和时间。
这些输出设备可以根据需要进行相应的驱动和控制,以实现清晰和直观的显示效果。
4. 实验结果和应用通过上述步骤,我们成功实现了51单片机万年历的原理和功能。
该实验可以应用于各种领域,如电子产品、智能家居和计时设备等。
在电子产品中,51单片机万年历可以作为一个重要的功能模块,为用户提供准确的日期和时间信息。
它还可以和其他模块进行联动,实现更复杂的功能,如定时开关、闹钟和事件提醒等。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
51单片机电子日历(电子时钟)硬件实验箱是伟福LAB2000实验箱。
程序代码:/* 电子日历,有时间显示、闹铃、日期、秒表及键盘设置功能 *//* 功能键A: 设置位数字+1 闹钟模式下为闹钟开关秒表模式下为记时开关 *//* 功能键B: 设置位数字-1 闹钟模式下为闹钟开关 *//* 功能键C:设置模式及设置位选择秒表模式下为清零键 */ /* 功能键D:在四种工作模式下切换设置闹钟开关 */#include#include/***************这里设置程序初始化时显示的时间****************/#define SET_HOUR 12 /*设置初始化小时*/#define SET_MINUTE 00 /*设置初始化分钟*/#define SET_SECOND 00 /*设置初始化秒数*//*************************系统地址****************************/#define BASE_PORT 0x8000 /*选通基地址*/#define KEY_LINE BASE_PORT+1 /*键盘行线地址#define KEY_COLUMN BASE_PORT+2 /*键盘列线地址*/#define LED_SEG BASE_PORT+4 /*数码管段选地址*/#define LED_BIT BASE_PORT+2 /*数码管位选地址*/#define LED_ON(x) XBYTE[LED_BIT]=(0x01<<X)&NBSP; *6位led的位选通,带参数宏,参数为0~5*=""#define LED_OFF XBYTE[LED_SEG]=0x00 /*LED显示空*//**************在设置模式下对秒分时的宏定义*****************/#define SECOND 0 /*对应数码管右边两位*/#define MINUTE 1 /*对应数码管中间两位*/#define HOUR 2 /*对应数码管左边两位*//********************定义四种工作模式***********************/#define CLOCK clockstr /*时钟模式*/#define ALART alartstr /*闹钟模式*/#define DATE datestr /*日期模式*/#define TIMER timerstr /*秒表模式*//****************以下是所有子函数的声明*********************/void sys_init(void); /*系统的初始化程序*/void display(void); /*动态刷新一次数码管子程序*/void clockplus(void); /*时间加1S的子程序*/void update_clockstr(void); /*更新时间显示编码*/void update_alartstr(void); /*更新闹钟时间的显示编码*/void update_datestr(void); /*更新日期显示编码*/void update_timerstr(void); /*更新秒表时间的显示编码*/void deley(int); /*延时子程序*/void update_dispbuf(unsigned char *); /*更新显示缓冲区*/unsigned char getkeycode(void); /*获取键值子程序*/void keyprocess(unsigned char); /*键值处理子程序*/unsigned char getmonthdays(unsigned int,unsigned char);/*计算某月的天数子程序*//*功能键功能子函数*/void Akey(void); /*当前设置位+1 开关闹钟开关秒表*/void Ckey(void); /*设置位选择秒表清零*/void Dkey(void); /*切换四种工作模式*//**********************全局变量声明部分unsigned charled[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};/*从0~9的LED编码*/unsigned char ledchar[3]={0x5c,0x54,0x71};/*o n f*///unsigned char key[24]={ /* 键值代码数组对应键位:*/// 0x70,0x71,0x72,0x73,0x74,0x75, /* 7 8 9 A TRACE RESET*/// 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5, /* 4 5 6 B STEP MON */// 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5, /* 1 2 3 C HERE LAST */// 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5}; /* 0 F E D EXEC NEXT */struct{ /*时间结构体变量*/unsigned char s;unsigned char m;unsigned char h;}clock={SET_SECOND,SET_MINUTE,SET_HOUR};struct{ /*闹铃时间结构体变量*/unsigned char m;unsigned char h;}alart={SET_MINUTE,SET_HOUR};struct{ /*日期结构体变量*/unsigned int year;unsigned char month;unsigned char day;}date={6,1,1};struct{ /*秒表时间结构体变量*/unsigned char ms;unsigned char s;unsigned char m;}timer={0,0,0};unsigned char dispbuf[6]; /*显示缓冲区数组*/unsigned char clockstr[6]; /*时间显示的数码管编码数组*/unsigned char alartstr[6]; /*闹钟显示的数码管编码数组*/unsigned char datestr[6]; /*日期显示的数码管编码数组*/unsigned char timerstr[6]; /*秒表显示的数码管编码数组*/unsigned int itime=0,idot; /*定时器0中断计数*/unsigned char itime1=0; /*定时器1中断计数*/sbit P3_1=P3^1; /*外接蜂鸣器的管脚*/bdata bit IsSet=0; /*设置模式标志位0:正常走时1:设置模式*/bdata bit Alart_EN=0; /*闹铃功能允许位0:禁止闹铃1:允许闹铃*/bdata bit IsBeep=0; /*响铃标志位0:未响铃1:正在响铃*/unsigned char SetSelect=0; /*在设置模式IsSet=1时,正在被设置的位,对应上面的宏*/unsigned char *CurrentMode; /*标志当前正设置的功能,如CurrentMode=CLOCK或CurrentMode=ALART等*/ void timerplus(void);/**************************函数部分*************************/void main(void){sys_init();{XBYTE[KEY_COLUMN,0x00]; /*给键盘列线赋全零扫描码,判断是否有键按下 */while((XBYTE[KEY_LINE]&0x0f)==0x0f) /*检测是否有键按下,无则一直进行LED的刷新显示*/{if(Alart_EN&&(clock.h==alart.h)&&(clock.m==alart.m)) {IsBeep=1;}else{ IsBeep=0;P3_1=0;}display();}keyprocess(getkeycode()); /*有键按下时得到键值,并送入键值处理程序*/display(); /*可要可不要*/}}void sys_init(void){TMOD=0x22; /*定时器0和1都设置为工作方式2,基准定时250×2=500us=0.5ms*/TH0=6; /*定时器0中断服务用来产生1秒时钟定时及闹钟蜂鸣器蜂鸣脉冲*/TL0=6; /*定时器1中断服务留给秒表使用,产生1/100秒定时*/TH1=6;TL1=6;ET0=1;ET1=1;EA=1;TR0=1;update_clockstr(); /*初始化时钟显示编码数组*/update_alartstr(); /*初始化闹钟显示编码数组*/update_datestr(); /*初始化日期显示编码数组*/update_timerstr(); /*初始化秒表显示编码数组*/update_dispbuf(clockstr);/*初始化显示缓冲数组*/CurrentMode=CLOCK; /*默认的显示摸式为时钟*/P3_1=0; /*蜂鸣器接线引脚复位*/}void timer0(void) interrupt 1 using 1 /*定时器0中断服务器,用来产生1秒定时*/{itime++;if(itime==1000){if(IsSet) /*在设置模式下,对正在设置的位闪烁显示*/{dispbuf[SetSelect*2]=0; /*对正在设置的位所对应的显示缓冲区元素赋0,使LED灭*/ dispbuf[SetSelect*2+1]=0;}if(IsBeep) P3_1=!P3_1; /*闹钟模式时,产生峰鸣器响脉冲*/if(CurrentMode==CLOCK){dispbuf[2]=dispbuf[2]&0x7f;}}if(itime==2000) /*两千次计数为1S 2000×0.5ms=1s*/{itime=0; /*定时1s时间到,软计数清零*/clockplus(); /*时间结构体变量秒数加1 */update_clockstr(); /* 更新时间显示编码数组 */if(CurrentMode!=TIMER) update_dispbuf(CurrentMode); /* 用时间编码数组更新显示缓冲区 */ }}void timer1(void) interrupt 3 using 2 /*定时器1中断服务器,用来产生1/100秒定时*/{idot++;if(++itime1==20) /*20*0.5ms=10ms*/{itime1=0;timerplus();update_timerstr();if(CurrentMode==TIMER){update_dispbuf(timerstr);dispbuf[2]=dispbuf[2]&0x7f; /*关闭小数点的显示*/dispbuf[4]=dispbuf[4]&0x7f;if(idot<1000) /*闪烁显示小数点*/{dispbuf[2]=dispbuf[2]|0x80;dispbuf[4]=dispbuf[4]|0x80;}else{dispbuf[2]=dispbuf[2]&0x7f;dispbuf[4]=dispbuf[4]&0x7f;}}}if(idot==2000) idot=0;}/*功能模块子函数*/void clockplus(void) /*时间加1s判断分,时子函数*/{if(++clock.s==60) /*秒位判断*/{clock.s=0;if(++clock.m==60) /*分位判断*/{clock.m=0;if(++clock.h==24) /*时位判断*/{if(++date.day==(getmonthdays(date.year,date.month)+1)){date.day=1;if(++date.month==13) date.month=1;}}}}}void timerplus() /*秒表1/100秒位加1,判断秒、分子程序*/{if(++timer.ms==100){timer.ms=0;if(++timer.s==60){timer.s=0;if(++timer.m==60){timer.m=0;}}}}void update_clockstr(void) /*更新时钟显示代码数组clockstr*/{clockstr[0]=led[clock.s%10]; /*给元素0赋相应数码管显示编码,编码序号是秒数的个位*/clockstr[1]=led[(int)(clock.s/10)]; /*给元素1赋相应数码管显示编码,编码序号是秒数的十位*/ clockstr[2]=led[clock.m%10]; /*以下类推*/clockstr[3]=led[(int)(clock.m/10)];clockstr[4]=led[clock.h%10];clockstr[5]=led[(int)(clock.h/10)];}void update_alartstr(void) /*更新闹钟显示代码数组alartstr*/{ /*右边两位显示on:闹钟开启 of:闹钟关闭*/if(Alart_EN) alartstr[0]=ledchar[1];/*显示字母n*/else alartstr[0]=ledchar[2]; /*显示字母f*/alartstr[1]=ledchar[0]; /*显示字母o*/alartstr[2]=led[alart.m%10];alartstr[3]=led[(int)(alart.m/10)];alartstr[4]=led[alart.h%10];alartstr[5]=led[(int)(alart.h/10)];}void update_datestr(void) /*更新日期显示代码数组datestr*/{datestr[0]=led[date.day%10];datestr[1]=led[(int)(date.day/10)];datestr[3]=led[(int)(date.month/10)];datestr[4]=led[date.year%10];datestr[5]=led[(int)(date.year/10)];}void update_timerstr(void) /*更新秒表显示代码数组timerstr*/{timerstr[0]=led[timer.ms%10];timerstr[1]=led[(int)(timer.ms/10)];timerstr[2]=led[timer.s%10];timerstr[3]=led[(int)(timer.s/10)];timerstr[4]=led[timer.m%10];timerstr[5]=led[(int)(timer.m/10)];}void display(void) /*刷新显示六位LED一次*/{unsigned char i;for(i=0;i<6;i++){LED_ON(i); /*选通相应位*/XBYTE[LED_SEG]=dispbuf[i]; /*写显示段码*/deley(50); /*延时显示*/LED_OFF; /*写LED全灭段码*/}}void update_dispbuf(unsigned char *str) /*更新显示缓冲区子函数,参数为要用来更新缓冲区的源字符数组的首地址*/ {dispbuf[0]=str[0]; /*将要更新的源字符数组内容COPY至dispbuf数组,用作显示缓冲区*/dispbuf[1]=str[1];dispbuf[2]=str[2]|0x80; /*默认把时位和分位后面的小数点显示出来,根据需要再取舍*/dispbuf[3]=str[3];dispbuf[4]=str[4]|0x80;dispbuf[5]=str[5];}void deley(int i) /*延时子函数*/{while(i--);}unsigned char getkeycode(void) /*键盘扫描子程序,返回获得的键码*/{unsigned char keycode; /*键码变量,一开始存行码*/unsigned char scancode=0x20; /*列扫描码*/unsigned char icolumn=0; /*键的列号*/display(); /*用刷新数码管显示的时间去抖*/XBYTE[KEY_COLUMN]=0x00;keycode=XBYTE[KEY_LINE]&0x0f; /*从行端口读入四位行码*/while((scancode&0x3f)!=0) /*取scancode的低六位,只要没变为全0,则执行循环*/{if((XBYTE[KEY_LINE]&0x0f)==keycode) break; /*检测按键所在的列跳出循环*/scancode=scancode>>1; /*列扫描码右移一位*/icolumn++; /*列号加1*/}keycode=keycode<<4; /*把行码移到高四位*/keycode=keycode|icolumn; /*由行码和列码组成键码*///等待按键放开XBYTE[KEY_COLUMN]=0x00;while((XBYTE[KEY_LINE]&0x0f)!=0x0f) display();return keycode;}void keyprocess(unsigned char keycode) /*键值处理函数*/{switch (keycode){case 0x73: Akey();break;case 0xB3: Bkey();break;case 0xD3: Ckey();break;case 0xE3: Dkey();break;default: break;}update_dispbuf(CurrentMode);}unsigned char getmonthdays(unsigned int year,unsigned char month)/*得到某月的天数*/ {unsigned char days;switch (month){case 4:case 6:case 9:case 11:days=30;break;case 2: if(year%4==0) days=29;else days=28;break;default:days=31;break;}return days;}/*功能键子函数部分*/void Akey(void) /*对当前设置位进行加一操作,如果设置秒位,则给秒位清零*/if(CurrentMode==TIMER) /*秒表模式下启闭走时*/{ TR1=!TR1;return;}if(!IsSet) return; /*如果不是设置模式退出*/switch (SetSelect){case SECOND:if(CurrentMode==CLOCK){clock.s=0; /*如果当前被设置位是秒位,则清零秒位*/update_clockstr();}if(CurrentMode==ALART){Alart_EN=!Alart_EN;update_alartstr();}if(CurrentMode==DATE){if(++date.day==(getmonthdays(date.year,date.month)+1)) date.day=1; update_datestr();}if(CurrentMode==TIMER){TR1=!TR1;}break;case MINUTE:if(CurrentMode==CLOCK){if(++clock.m==60) clock.m=0; /*如果当前被设置分位,则分位加1*/update_clockstr();}if(CurrentMode==ALART){if(++alart.m==60) alart.m=0;update_alartstr();}if(CurrentMode==DATE){if(++date.month==13) date.month=1;update_datestr();}break;case HOUR: if(CurrentMode==CLOCK){if(++clock.h==24) clock.h=0; /*如果当前被设置时位,则时位加1*/update_clockstr();if(CurrentMode==ALART){if(++alart.h==24) alart.h=0;update_alartstr();}if(CurrentMode==DATE){if(++date.year==100) date.year=0;update_datestr();}break;default: break;}update_dispbuf(CurrentMode);}void Bkey(void) /*对当前设置位进行减一操作,如果设置秒分,则给秒位清零,类比Akey()函数*/{if(!IsSet) return;switch (SetSelect){case SECOND:if(CurrentMode==CLOCK){clock.s=0;update_clockstr();}if(CurrentMode==ALART){Alart_EN=!Alart_EN;update_alartstr();}if(CurrentMode==DATE){if(--date.day==0x00) date.day=getmonthdays(date.year,date.month); update_datestr();}break;case MINUTE:if(CurrentMode==CLOCK){if(--clock.m==0xff) clock.m=59;update_clockstr();}if(CurrentMode==ALART){if(--alart.m==0xff) alart.m=59;update_alartstr();}if(CurrentMode==DATE)if(--date.month==0x00) date.month=12;update_datestr();}break;case HOUR: if(CurrentMode==CLOCK){if(--clock.h==0xff) clock.h=23;update_clockstr();}if(CurrentMode==ALART){if(--alart.h==0xff) alart.h=23;update_alartstr();}if(CurrentMode==DATE){if(--date.year==0xffff) date.year=99;update_datestr();}break;default: break;}update_dispbuf(CurrentMode);}void Ckey(void) /*正常走时模式和设置模式的切换*/{if(CurrentMode==TIMER){TR1=0; /*初始化定时器1设置,停止秒表记时*/TH1=6;TL1=6;timer.ms=0; /*初始化秒表数组*/timer.s=0;timer.m=0;update_timerstr();}else{if(IsSet==0) /*在非秒表模式下,第一次按下C键进入设置模式,设置时位*/{IsSet=1; /*置位标志位,进入设置模式 */SetSelect=HOUR;return;} /*第二次按C键设置分位,第三次按键设置秒位,第四次按键完成退出设置*/if(SetSelect==0) /*按到第四次,即设置完秒位后,将标志位IsSet置0,完成设置*/ {IsSet=0; /*复位标志位,进入正常走时模式*/return;if(SetSelect>0) SetSelect--; /*设置位的标志变量SetSelect=0:时位 1:分位 2:秒位*/}}void Dkey(void) /*工作状态切换:时钟、闹钟、日期、秒表*/{if(CurrentMode==CLOCK) /*切换至闹钟,同时开关闹钟*/{ CurrentMode=ALART;Alart_EN=!Alart_EN;update_alartstr();return;}if(CurrentMode==ALART) /*切换至日期*/{ CurrentMode=DATE;return;}if(CurrentMode==DATE)/*切换至秒表,同时关闭设置模式*/{CurrentMode=TIMER;IsSet=0;return;}if(CurrentMode==TIMER) /*切换至时钟*/{CurrentMode=CLOCK;return;}}11。