当前位置:文档之家› 基于单片机C51的简单万年历设计

基于单片机C51的简单万年历设计

第十届学生科技节-----单片机设计大赛作品名称:万年历专业:物联网班级:1****:***组长:邓清兰学号:3112113111组员:黄孟秋学号:3112113101目录一、绪论 0二、系统功能 0三、硬件电路 03.1. 电路设计框图 03.2. 系统硬件概述 (1)四、各模块功能及工作原理 (1)4.1. 单片机主控制模块的设计 (1)4.2. LCD1602显示模块 (2)4.3. 键盘模块 (5)五、实现结果 (8)六、系统的实用性及创新性 (9)七、调节中遇到的问题及心得体会 (9)八、程序设计 (10)8.1. 流程图 (10)8.2. 源代码 (11)九、总结及感谢 (23)一、绪论众所周知单片机是一种集成在电路芯片,是采用超大规模集成电路技术把具有数据处理能力的中央处理器CPU随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计时器等功能(可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成到一块硅片上构成的一个小而完善的计算机系统。

本设计要制作的就是单片机于生活中最为常见的几种应用——万年历。

本设计以AT89S52单片机作为核心,可以显示当前的日历和时间,时间也可以人为设定,显示格式为年(四位),月(两位),日(两位),时(两位),分(两位),秒(两位)。

设置时间的位切换、设定数值、启动定时器、切换日历通过外部中断来实现。

万年历显示电路由LCD1602组成,制作该装置的材料需要有软硬件的支持,硬件方面AT89C51单片机,晶振,电源,液晶屏LCD1602。

蜡變黲癟報伥铉锚鈰赘。

二、系统功能本次设计的是万年历,采用LCD1602显示,实现的功能如下:1.显示当前时间,显示格式为年(四位),月(两位),日(两位),时(两位),分(两位),秒(两位);2.时钟计时,自动进行时间的位切换;3.键盘设置,能通过键盘设定当前时间和计时,并通过键盘人为设定时间。

三、硬件电路3.1.电路设计框图3.2.系统硬件概述本电路是由AT89S52单片机为控制核心,具有在线编程功能,低功耗,能在3V超低压工作,本系统选用12MHZ的晶振,使得单片机有合理的运行速度;利用液晶屏LCD1602显示时间信息,通过对AT89S52单片机的编程控制液晶屏LCD1602的显示。

显示时间在LCD1602,通过按键切换选择。

外部按键可及时设定或调整时间信息。

買鲷鴯譖昙膚遙闫撷凄。

四、各模块功能及工作原理4.1.单片机主控制模块的设计AT89S52是一种低功耗、高性能CMOS8位微控制器,具有8K 在系统可编程Flash 存储器。

AT89S52单片机为40引脚双列直插芯片,有四个I/O口P0,P1,P2,P3, MCS-51单片机共有4个8位的I/O口(P0、P1、P2、P3),每一条I/O线都能独立地作输出或输入。

18引脚和19引脚接时钟电路,XTAL1接外部晶振和微调电容的一端,在片内它是振荡器倒相放大器的输入,XTAL2接外部晶振和微调电容的另一端,在片内它是振荡器倒相放大器的输出。

第9引脚为复位输入端,接上电容,电阻及开关后够上电复位电路,20引脚为接地端,40引脚为电源端。

引脚图如图图4.1:图4.1 AT89S52 引脚结构图4.2.1 LCD1602 引脚结构4.2.LCD1602显示模块4.2.1LCD1602引脚介绍LCD1602采用标准的14脚(无背光)或16脚(带背光)接口,引脚图第1脚:VSS为地电源。

第2脚:VDD接5V正电源。

第3脚:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度。

猫虿驢绘燈鮒诛髅貺庑。

第4脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。

第5脚:R/W为读写信号线,高电平时进行读操作,低电平时进行写操作。

当RS和R/W共同为低电平时可以写入指令或者显示地址,当RS为低电平R/W 为高电平时可以读忙信号,当RS为高电平R/W为低电平时可以写入数据。

锹籁饗迳琐筆襖鸥娅薔。

第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。

第7~14脚:D0~D7为8位双向数据线。

第15脚:背光源正极。

第16脚:背光源负极。

4.2.2LCD1602指令1602液晶模块内部的控制器共有11条控制指令,如表4.2.31602液晶模块的读写操作、屏幕和光标的操作都是通过指令编程来实现的。

(说明:1为高电平、0为低电平)構氽頑黉碩饨荠龈话骛。

指令1:清显示,指令码01H,光标复位到地址00H位置。

指令2:光标复位,光标返回到地址00H。

指令3:光标和显示模式设置I/D:光标移动方向,高电平右移,低电平左移S:屏幕上所有文字是否左移或者右移。

高电平表示有效,低电平则无效。

輒峄陽檉簖疖網儂號泶。

指令4:显示开关控制。

D:控制整体显示的开与关,高电平表示开显示,低电平表示关显示C:控制光标的开与关,高电平表示有光标,低电平表示无光标B:控制光标是否闪烁,高电平闪烁,低电平不闪烁。

尧侧閆繭絳闕绚勵蜆贅。

指令5:光标或显示移位S/C:高电平时移动显示的文字,低电平时移动光标。

指令6:功能设置命令DL:高电平时为4位总线,低电平时为8位总线N:低电平时为单行显示,高电平时双行显示F: 低电平时显示5x7的点阵字符,高电平时显示5x10的点阵字符。

识饒鎂錕缢灩筧嚌俨淒。

指令7:字符发生器RAM地址设置。

指令8:DDRAM地址设置。

指令9:读忙信号和光标地址BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。

凍鈹鋨劳臘锴痫婦胫籴。

指令10:写数据。

指令11:读数据。

4.2.3 LCD1602读/写操作LCD1602基本操作时序说明,如表4.2.44.2.4 LCD1602显示模块流程图4.3.键盘模块本设计采用4乘4键盘的反转法控制万年历的计时和时间设定,4乘4键盘电路图如图图4.3.1图4.3.24.3.1键盘功能图0 1 2 34 8 + 59-6 7<!>@YY N Y NYN YN N4.4计时模块 4.4.1计数器工作原理计数器:是单片机应用中的重要部件,工作方式灵活,可以对外部事件计数和检测等;实现计数功能。

其核心是特殊功能寄存器中能进行加1计数的寄存器。

实现原理:对T0或T1引脚输入的外部脉冲计数,如下图:4.4.2N YY N N YNY五、实现结果六、系统的实用性及创新性基本功能:能显示当前时间和计时,实现基本时钟功能。

创新性:界面友好,能实现计时和键盘数字的时间设定和自增、自减的时间设定。

七、调节中遇到的问题及心得体会问题一LCD1602的初始化不规范,导致无法正确在LCD1602上显示相应的字符,时间等。

解决在网上及课本上查找有关LCD1602初始化的程序,找出错误的地方并及时改正,最终解决了LCD1602的初始化问题。

问题二在对秒、分、时、日、月、年等的大小判断时,采用逐位判断,导致程序冗长,不利于程序的优化。

解决通过老师的指导及其他相关程序的借鉴,对时间大小的判断进行优化,将秒的十位和各位合为一个整体进行判断,分、时、日、月、年类似。

问题三在计时模块中,因为细节问题,找不到导致不能正常计时的原因。

解决通过老师的精心指导,找到不能正常计时的原因,将中断次数的判断if(count>100)改为if(count>=100)即可。

问题四在键盘设计模块中,对空数组的定义无效。

解决通过查找相关资料,了解到空数组的定义要放在data中,不能放在code 中。

将空数组定义为uchar a[14]使其默认放在data中就解决了该问题。

八、程序设计8.1.流程图8.2.源代码#include<reg52.h>#define uchar unsigned char#define uint unsigned intuchar code Buffer[4]={0xfe,0xfd,0xfb,0xf7};uchar code table[]={'0','1','2','3','4','5','6','7','8','9','<','>','+','-','!','@'};uchar code w[14]={0x83,0x84,0x85,0x86,0x88,0x89,0x8b,0x8c,0xc4,0xc5,0xc7,0xc8,0xca,0xcb}; uchar a[14];sbit E=P2^7; //1602使能引脚sbit RW=P2^6; //1602读写引脚sbit RS=P2^5; //1602数据/命令选择引脚void Delay(uint xms); //延时void Time0_Init(); //中断请求void timer1_int(void); //中断void charge(); //判断秒分时日月等是否大于额外值,若大于向上一位加一void ftime(); //初始时间void timeon(); //计时void add(); //自加void sub(); //自减void wcmd(uchar del); //1602写指令void write(uchar del); //1602写数据void L1602_init(void); //1602初始化void second(); //秒显示void minute(); //分显示void hours(); //时显示void days(); //日显示void month(); //月显示void years(); //年显示void other(); //符号显示void number(); //键盘输入数字uint count,t; //中断执行次数,光标位置参数uchar sec=0; //初始秒uchar min=0; //初始分uchar hour=0; //初始时uchar day=0; //初始日uchar mon=0; //初始月uchar yearh=0; //初始年前两位uchar yearl=0; //初始年后两位//1602命令函数void wcmd(uchar del){P0 = del;RS = 0;RW = 0;E = 1;Delay(1);E = 0;Delay(1);}// 1602写数据函数void write(uchar del){P0 = del;RS = 1;RW = 0;E = 1;Delay(1);E = 0;Delay(1);}// 1602初始化void L1602_init(void) {wcmd(0x38);wcmd(0x38);wcmd(0x38);wcmd(0x0e);wcmd(0x06);wcmd(0x01);}//显示秒void second(){uchar i,j;i=sec/10;j=sec%10;wcmd(0xca);write(table[i]);write(table[j]); }//显示分void minute(){uchar i,j;i=min/10;j=min%10;wcmd(0xc7);write(table[i]);write(table[j]); }//显示时void hours(){uchar i,j;i=hour/10;j=hour%10;wcmd(0xc4);write(table[i]);write(table[j]); }//显示日void days(){uchar i,j;i=day/10;j=day%10;wcmd(0x8b);write(table[i]);write(table[j]); }//显示月void month(){uchar i,j;i=mon/10;j=mon%10;wcmd(0x88);write(table[i]);write(table[j]); }//显示年void years(){uchar i,j,m,n;i=yearh/10;j=yearh%10;m=yearl/10;n=yearl%10;wcmd(0x83);write(table[i]);write(table[j]);write(table[m]);write(table[n]);}//符号显示void other(){wcmd(0xc9);write(':');wcmd(0xc6);write(':');wcmd(0x87);write('-');wcmd(0x8a);write('-');}//延时void Delay(uint xms){uint i,j;for(i=0;i<xms;i++)for(j=0;j<110;j++);}//计时void timeon(){if(count>=100){ count=0;charge();}ftime(); //显示初始时间}//时间初值void ftime(){second();Delay(5);minute();Delay(5);hours();Delay(5);days();Delay(5);month();Delay(5);years();Delay(5);other();Delay(5);}//各个按键返回值uint Keyvalue(){ uint i,j;uchar temp;uint Key_value;while(1){P1=0xf0;if(P1!=0xf0){Delay(100);if(P1!=0xf0)for(j=0;j<4;j++){P1=Buffer[j];temp=0x10;for(i=0;i<4;i++){if(!(P1&temp)){Key_value=i+j*4;}temp<<=1;}}return Key_value;}}}//字符串显示void L1602_string(uchar hang,uchar lie,uchar *p) {uchar a,b=0;if(hang == 1) a = 0x80;if(hang == 2) a = 0xc0;a = a + lie - 1;while(1){wcmd(a++);b++;if((*p == '\0')||(b==16)) break;write(*p);Delay(500);p++;}}//自增void add(){wcmd(w[t]); //光标置位if(t==1) //光标在第二个位置时{if(yearh<99) //判断年前两位是否小于99,是,年前两位加1,并显示{yearh++;years();}}else if(t==3) //光标在第四个位置时{if(yearl<99) //判断年后两位是否小于99,是,年后两位加1,并显示{yearl++;years();}}else if(t==5) //光标在第六个位置时{if(mon<12) //判断月是否小于12,是,月加1,并显示{mon++;month();}}else if(t==7) //光标在第八个位置时{if(day<30) //判断日是否小于30,是,日加1,并显示{day++;days();}}else if(t==9) //当光标在第十个位置时{if(hour<12) //判断时是否小于12,是,时加1,并显示{hour++;hours();}}else if(t==11) //光标在第十二个位置时{if(min<59) //判断分是否小于59,是,分加1,并显示{min++;minute();}}else if(t==13) //光标在第十四个位置时{if(sec<59) //判断秒是否小于59,是,秒加1,并显示{sec++;second();}}}//自减void sub(){wcmd(w[t]); //光标置位if(t==1) //光标在第二个位置时,判断年前两位是否大于0,是,年前两位减1,并显示{if(yearh>0){yearh--;years();}}else if(t==3) //光标在第四个位置时,判断年后两位是否大于0,是,年后两位减1,并显示{if(yearl>1){yearl--;years();}}else if(t==5) //光标在第六个位置时,判断月是否大于1,是,月减1,并显示{if(mon>1){mon--;month();}}else if(t==7) //光标在第八个位置时,判断日是否大于1,是,日减1,并显示{if(day>1){day--;days();}}else if(t==9) //光标在第十个位置时,判断时是否大于0,是,时减1,并显示{if(hour>0){hour--;hours();}}else if(t==11) //光标在第十二个位置时,判断分是否大于0,是,分减1,并显示{if(min>0){min--;minute();}}else if(t==13) //光标在第十四个位置时,判断秒是否大于0,是,秒减1,并显示{if(sec>0){sec--;second();}}}//主函数void main(){ uchar keys;uint i,key; //数组a的参数i=0;t=0; //位置参数L1602_init(); //1602初始化L1602_string(1,5,"Showtime"); //初始界面显示L1602_string(2,5,"Welcome!"); //初始界面显示while(1){key=Keyvalue(); //将键值赋值给keykeys=table[key];if(keys=='@') //判断键值是否为@,是,执行计时{Time0_Init();while(1)timeon();}else if(keys=='!') //判断键值是否为!,是,显示初始时间,光标在第一个位置{ftime();wcmd(0x83);}else if(keys=='-'||keys=='+') //判断键值是否为+或-{if(keys=='+') //键值为+,执行自加函数{add();}else //键值为-,执行自减函数{sub();}}//判断键值是否为<,是,光标不在最左边时,光标左移一位else if(keys=='<'){if(t>=1){i--;t=t-1;wcmd(w[t]);}}else if(keys=='>') //判断键值是否为>,是,光标不在最右边时,光标右移一位{if(t<13){i++;t=t+1;wcmd(w[t]);}}else //键值为数字键时,将键值放入a数组中,在调用函数,显示当前设置的时间{ number();i=i%14;a[i]=key;i++;t++;number();}}}//输入数字void number(){yearh=a[0]*10+a[1];yearl=a[2]*10+a[3];mon=a[4]*10+a[5];day=a[6]*10+a[7];hour=a[8]*10+a[9];min=a[10]*10+a[11];sec=a[12]*10+a[13];ftime();}//中断请求void Time0_Init(){TMOD=0X10;TH1=(65536-10000)/256;TL1=(65536-10000)%256;EA=1;ET1=1;TR1=1;}//中断void timer1_int(void) interrupt 3{count++;TH1=(65536-10000)/256;TL1=(65536-10000)%256;}//判断年月日是否超过额外值void charge(){sec++; //秒加1if(sec==60) //秒等于60时,分加1,秒置0{ sec=0;min++; }if(min==60) //分等于60时,时加1 ,分置0{ m in=0;hour++; }if(hour==24) //时等于24时,日加1,时置0{ hour=0;day++; }if(day==31) //日等于30时,月加1,日置1{ day=1;mon++; }if(mon==13) //月等于613时,年后两位加1,月置1{ mon=1;yearl++; }if(yearl==99) //年后两位等于99时,年前两位加1,年后两位置0{yearh=0;yearh++;if(yearh==99) //年前两位等于99时,年前两位置0yearh=0;}}九、总结及感谢在用单片机实现万年历的设计中使用到了AT89S52、LCD1602及其4*4键盘的使用。

相关主题