基于C51单片机的简单万年历设计毕业设计.docx

上传人:b****5 文档编号:6221254 上传时间:2023-01-04 格式:DOCX 页数:38 大小:311.68KB
下载 相关 举报
基于C51单片机的简单万年历设计毕业设计.docx_第1页
第1页 / 共38页
基于C51单片机的简单万年历设计毕业设计.docx_第2页
第2页 / 共38页
基于C51单片机的简单万年历设计毕业设计.docx_第3页
第3页 / 共38页
基于C51单片机的简单万年历设计毕业设计.docx_第4页
第4页 / 共38页
基于C51单片机的简单万年历设计毕业设计.docx_第5页
第5页 / 共38页
点击查看更多>>
下载资源
资源描述

基于C51单片机的简单万年历设计毕业设计.docx

《基于C51单片机的简单万年历设计毕业设计.docx》由会员分享,可在线阅读,更多相关《基于C51单片机的简单万年历设计毕业设计.docx(38页珍藏版)》请在冰豆网上搜索。

基于C51单片机的简单万年历设计毕业设计.docx

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

 

作品名称:

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

1、绪论

众所周知单片机是一种集成在电路芯片,是采用超大规模集成电路技术把具有数据处理能力的中央处理器CPU随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计时器等功能(可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成到一块硅片上构成的一个小而完善的计算机系统。

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

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

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

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

2、系统功能

本次设计的是万年历,采用LCD1602显示,实现的功能如下:

1.显示当前时间,显示格式为年(四位),月(两位),日(两位),时(两位),分(两位),秒(两位);

2.时钟计时,自动进行时间的位切换;

3.键盘设置,能通过键盘设定当前时间和计时,并通过键盘人为设定时间。

3、

硬件电路

1.

2.

3.

3.1.电路设计框图

3.2.系统硬件概述

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

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

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

4、各模块功能及工作原理

4.

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.1AT89S52引脚结构图4.2.1LCD1602引脚结构

4.2.LCD1602显示模块

4.2.1LCD1602引脚介绍

LCD1602采用标准的14脚(无背光)或16脚(带背光)接口,引脚图如图图4.2.1:

各引脚接口说明如表4.2.2所示:

编号

符号

引脚说明

编号

符号

引脚说明

1

VSS

电源地

9

D2

数据

2

VDD

电源正极

10

D3

数据

3

VL

液晶显示偏压

11

D4

数据

4

RS

数据/命令选择

12

D5

数据

5

R/W

读/写选择

13

D6

数据

6

E

使能信号

14

D7

数据

7

D0

数据

15

BLA

背光源正极

8

D1

数据

16

BLK

背光源负极

表4.2.2LCD1602引脚说明

第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.3

编号

指令

RS

R/W

D7

D6

D5

D4

D3

D2

D1

D0

1

清显示

0

0

0

0

0

0

0

0

0

1

2

光标返回

0

0

0

0

0

0

0

0

1

*

3

置输入模式

0

0

0

0

0

0

0

1

I/D

S

4

显示开/关控制

0

0

0

0

0

0

1

D

C

B

5

光标或字符移位

0

0

0

0

0

1

S/C

R/L

*

*

6

置功能

0

0

0

0

1

DL

N

F

*

*

7

置字符发生存贮器地址

0

0

0

1

字符发生存贮器地址

8

置数据存贮器地址

0

0

1

显示数据存贮器地址

9

读忙标志或地址

0

1

BF

计数器地址

10

写数到CGRAM或DDRAM)

1

0

要写的数据内容

11

从CGRAM或DDRAM读数

1

1

读出的数据内容

表4.2.3LCD1602的指令说明

1602液晶模块的读写操作、屏幕和光标的操作都是通过指令编程来实现的。

(说明:

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.3LCD1602读/写操作

LCD1602基本操作时序说明,如表4.2.4

读状态

输入

RS=L,R/W=H,E=H

输出

D0—D7=状态字

写指令

输入

RS=L,R/W=L,D0—D7=指令码,E=高脉冲

输出

读数据

输入

RS=H,R/W=H,E=H

输出

D0—D7=数据

写数据

输入

RS=H,R/W=L,D0—D7=数据,E=高脉冲

输出

表4.2.4LCD1602基本操作时序说明

4.2.4LCD1602显示模块流程图

N

Y

4.3.键盘模块

本设计采用4乘4键盘的反转法控制万年历的计时和时间设定,4乘4键盘电路图如图图4.3.1

图4.3.2

 

4.3.1键盘功能图

 

4.3.2流程图

 

Y

N

Y

YN

YN

Y

N

Y

N

N

Y

N

N

Y

4.4计时模块

4.4.1计数器工作原理

计数器:

是单片机应用中的重要部件,工作方式灵活,可以对外部事件计数和检测等;实现计数功能。

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

实现原理:

对T0或T1引脚输入的外部脉冲计数,如下图:

4.4.2流程图

NY

YN

NY

YN

NY

YN

NY

N

Y

5、实现结果

6、系统的实用性及创新性

基本功能:

能显示当前时间和计时,实现基本时钟功能。

创新性:

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

 

7、调节中遇到的问题及心得体会

问题一

LCD1602的初始化不规范,导致无法正确在LCD1602上显示相应的字符,时间等。

解决

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

问题二

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

解决

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

问题三

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

解决

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

问题四

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

解决

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

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

 

8、程序设计

5.

6.

7.

8.

8.1.流程图

 

N

Y

Y

Y

8.2.源代码

#include

#defineucharunsignedchar

#defineuintunsignedint

ucharcodeBuffer[4]={0xfe,0xfd,0xfb,0xf7};

ucharcodetable[]={'0','1','2','3','4','5','6','7','8','9','<','>','+','-','!

','@'};

ucharcodew[14]={0x83,0x84,0x85,0x86,0x88,0x89,0x8b,0x8c,0xc4,0xc5,0xc7,0xc8,0xca,0xcb};

uchara[14];

sbitE=P2^7;//1602使能引脚

sbitRW=P2^6;//1602读写引脚

sbitRS=P2^5;//1602数据/命令选择引脚

voidDelay(uintxms);//延时

voidTime0_Init();//中断请求

voidtimer1_int(void);//中断

voidcharge();//判断秒分时日月等是否大于额外值,若大于向上一位加一

voidftime();//初始时间

voidtimeon();//计时

voidadd();//自加

voidsub();//自减

voidwcmd(uchardel);//1602写指令

voidwrite(uchardel);//1602写数据

voidL1602_init(void);//1602初始化

voidsecond();//秒显示

voidminute();//分显示

voidhours();//时显示

voiddays();//日显示

voidmonth();//月显示

voidyears();//年显示

voidother();//符号显示

voidnumber();//键盘输入数字

uintcount,t;//中断执行次数,光标位置参数

ucharsec=0;//初始秒

ucharmin=0;//初始分

ucharhour=0;//初始时

ucharday=0;//初始日

ucharmon=0;//初始月

ucharyearh=0;//初始年前两位

ucharyearl=0;//初始年后两位

//1602命令函数

voidwcmd(uchardel)

{

P0=del;

RS=0;

RW=0;

E=1;

Delay

(1);

E=0;

Delay

(1);

}

//1602写数据函数

voidwrite(uchardel)

{

P0=del;

RS=1;

RW=0;

E=1;

Delay

(1);

E=0;

Delay

(1);

}

//1602初始化

voidL1602_init(void)

{

wcmd(0x38);

wcmd(0x38);

wcmd(0x38);

wcmd(0x0e);

wcmd(0x06);

wcmd(0x01);

}

//显示秒

voidsecond()

{

uchari,j;

i=sec/10;

j=sec%10;

wcmd(0xca);

write(table[i]);

write(table[j]);

}

//显示分

voidminute()

{

uchari,j;

i=min/10;

j=min%10;

wcmd(0xc7);

write(table[i]);

write(table[j]);

}

//显示时

voidhours()

{

uchari,j;

i=hour/10;

j=hour%10;

wcmd(0xc4);

write(table[i]);

write(table[j]);

}

//显示日

voiddays()

{

uchari,j;

i=day/10;

j=day%10;

wcmd(0x8b);

write(table[i]);

write(table[j]);

}

//显示月

voidmonth()

{

uchari,j;

i=mon/10;

j=mon%10;

wcmd(0x88);

write(table[i]);

write(table[j]);

}

//显示年

voidyears()

{

uchari,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]);

}

//符号显示

voidother()

{

wcmd(0xc9);

write(':

');

wcmd(0xc6);

write(':

');

wcmd(0x87);

write('-');

wcmd(0x8a);

write('-');

}

//延时

voidDelay(uintxms)

{

uinti,j;

for(i=0;i

for(j=0;j<110;j++);

}

//计时

voidtimeon()

{

if(count>=100)

{count=0;

charge();

}

ftime();//显示初始时间

}

//时间初值

voidftime()

{

second();Delay(5);

minute();Delay(5);

hours();Delay(5);

days();Delay(5);

month();Delay(5);

years();Delay(5);

other();Delay(5);

}

//各个按键返回值

uintKeyvalue()

{uinti,j;

uchartemp;

uintKey_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;

}

}

returnKey_value;

}

}

}

//字符串显示

voidL1602_string(ucharhang,ucharlie,uchar*p)

{

uchara,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++;

}

}

//自增

voidadd()

{

wcmd(w[t]);//光标置位

if(t==1)//光标在第二个位置时

{

if(yearh<99)//判断年前两位是否小于99,是,年前两位加1,并显示

{

yearh++;

years();

}

}

elseif(t==3)//光标在第四个位置时

{

if(yearl<99)//判断年后两位是否小于99,是,年后两位加1,并显示

{

yearl++;

years();

}

}

elseif(t==5)//光标在第六个位置时

{

if(mon<12)//判断月是否小于12,是,月加1,并显示

{

mon++;

month();

}

}

elseif(t==7)//光标在第八个位置时

{

if(day<30)//判断日是否小于30,是,日加1,并显示

{

day++;

days();

}

}

elseif(t==9)//当光标在第十个位置时

{

if(hour<12)//判断时是否小于12,是,时加1,并显示

{

hour++;

hours();

}

}

elseif(t==11)//光标在第十二个位置时

{

if(min<59)//判断分是否小于59,是,分加1,并显示

{

min++;

minute();

}

}

elseif(t==13)//光标在第十四个位置时

{

if(sec<59)//判断秒是否小于59,是,秒加1,并显示

{

sec++;

second();

}

}

}

//自减

voidsub()

{

wcmd(w[t]);//光标置位

if(t==1)//光标在第二个位置时,判断年前两位是否大于0,是,年前两位减1,并显示

{

if(yearh>0)

{

yearh--;

years();

}

}

elseif(t==3)//光标在第四个位置时,判断年后两位是否大于0,是,年后两位减1,并显示

{

if(yearl>1)

{

yearl--;

years();

}

}

elseif(t==5)//光标在第六个位置时,判断月是否大于1,是,月减1,并显示

{

if(mon>1)

{

mon--;

month();

}

}

elseif(t==7)//光标在第八个位置时,判断日是否大于1,是,日减1,并显示

{

if(day>1)

{

day--;

days();

}

}

elseif(t==9)//光标在第十个位置时,判断时是否大于0,是,时减1,并显示

{

if(hour>0)

{

hour--;

hours();

}

}

elseif(t==11)//光标在第十二个位置时,判断分是否大于0,是,分减1,并显示

{

if(min>0)

{

min--;

minute();

}

}

elseif(t==13)//光标在第十四个位置时,判断秒是否大于0,是,秒减1,并显示

{

if(sec>0)

{

sec--;

second();

}

}

}

//主函数

voidmain()

{ucharkeys;

uinti,key;//数组a的参数

i=0;

t=0;//位置参数

L1602_init();//1602初始化

L1602_string(1,5,"Showtime");//初始界面显示

L1602_string(2,5,"Welcome!

");//初始界面显示

while

(1)

{

key=Keyvalue();//将键值赋值给key

keys=table[key];

if(keys=='@')//判断键值是否为@,是,执行计时

{

Time0_Init();

while

(1)

timeon();

}

elseif(keys=='!

')//判断键值是否为!

,是,显示初始时间,光标在第一个位置

{

ftime();

wcmd(0x83);}

elseif(keys=='-'||keys=='+')//判断键值是否为+或-

{

if(keys=='+')//键值为+,执行自加函数

{

add();

}

else//键值为-,执行自减函数

{

sub();

}

}

//判断键值是否为<,是,光标不在最左边时,光标左移一位

elseif(keys=='<')

{if(t>=1)

{

i--;

t=t-1;

wcm

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 党团工作 > 入党转正申请

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1