基于AT89C51单片机的定时闹钟设计.docx

上传人:b****2 文档编号:2574127 上传时间:2023-05-04 格式:DOCX 页数:43 大小:236.51KB
下载 相关 举报
基于AT89C51单片机的定时闹钟设计.docx_第1页
第1页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第2页
第2页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第3页
第3页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第4页
第4页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第5页
第5页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第6页
第6页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第7页
第7页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第8页
第8页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第9页
第9页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第10页
第10页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第11页
第11页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第12页
第12页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第13页
第13页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第14页
第14页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第15页
第15页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第16页
第16页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第17页
第17页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第18页
第18页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第19页
第19页 / 共43页
基于AT89C51单片机的定时闹钟设计.docx_第20页
第20页 / 共43页
亲,该文档总共43页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

基于AT89C51单片机的定时闹钟设计.docx

《基于AT89C51单片机的定时闹钟设计.docx》由会员分享,可在线阅读,更多相关《基于AT89C51单片机的定时闹钟设计.docx(43页珍藏版)》请在冰点文库上搜索。

基于AT89C51单片机的定时闹钟设计.docx

基于AT89C51单片机的定时闹钟设计

学号:

班级:

姓名:

基于89C51单片机的电子闹钟设计

一、设计要求

(1)设计并实现一个具有计时功能的电子时钟系统,电子表的时间精确到秒,并可以显示年、月、日、时、分、秒。

(2)利用液晶显示器显示定时器的日期和时间。

(3)使用键盘进行设置时间和设置闹钟。

(4)定时时间到通过蜂鸣器报警和发光二极管闪烁通知,并持续60s。

二、硬件设计

(1)系统设计框图

(2)选择硬件设备

单片机:

选择AT89C51;

液晶显示器:

具有16字符显示功能的1601

键盘:

选择4行*4列的矩阵键盘

LED:

选择红色的发光二极管

E2PROM:

X2545

图2时钟信号发生单元

基本元件:

蜂鸣器,电容,晶体振荡器,电阻,开关

电源:

使用+5v直流稳压源

基本模块的构成

①时钟信号发生单元如右图2

利用晶振和电容以及单片机内部

电路,构成晶体并联振荡器,产

生12MHz的时钟频率

②复位电路如右图3

利用一个简单的电容和按键实现

图3复位单元电路

实现对系统的复位功能

由此基本模块可以实现最小的单片机系统

(3)电子时钟硬件原理图

图4硬件电路原理电路

(4)主要器件的原理

①液晶显示原理

液晶显示器种类繁多,按输出样式分为,图案式,数码式,点阵式。

本设计方案利用的是点阵式液晶显示器,而液晶驱动方式又和数码管驱动截然不同,虽然比数码管需要更小的工作电压,但是其结构所需要的扫描方式较数码管来说,是比较复杂的,而且输入输出数据速度慢,市场上是常用点阵式液晶驱动器的,常用的有1601、1602……,“16”代表显示字符共有几列,“01”、“02”代表输出字符共有几行。

下面是驱动1601的驱动方法。

驱动1601的一个很重要的方面就是液晶显示器的初始化,主要是利用控制、数据复用总线来输入指令,进行初始化。

基本操作

读状态:

输入:

RS=L,RW=H,E=H输出:

D0~D7=状态字

写指令:

输入:

RS=L,RW=L,D0~D7=指令码,E=高脉冲输出:

读数据:

输入:

RS=H,RW=H,E=H输出:

D0~D7=数据

写数据:

输入:

RS=H,RW=L,D0~D7=数据,E=高脉冲输出:

状态字说明

Sta7

(读写操作能)

D7

Sta6

D6

Sta5

D5

Sta4

D4

Sta3

D3

Sta2

D2

Sta1

D0

Sta0

D0

1:

禁止

0:

允许

当前数据指针的数值

利用Sta7可以用来检测当前驱动器是否处于忙状态,这样的话可以避免在忙状态的情况下,进行写数据会产生漏写的错误;或者可以利用延时,具体时间可以参照datasheet,一般都为几毫秒左右。

初始化设置

初始化时首先要进行写命令操作,然后按照datasheet的说明进行写指令

显示模式设置

指令

指令使能

指令码

描述

RS

RW

DB7

DB6

DB5

DB4

DB3

DB2

DB1

DB0

显示模式

0

0

0

0

1

1

1

0

0

0

5*7点阵显示,8位数据接口

1)常用显示指令

指令

指令使能

指令码

描述

RS

RW

DB7

DB6

DB5

DB4

DB3

DB2

DB1

DB0

清屏

0

0

0

0

0

0

0

0

0

1

数据指针清零

所有显示清零

回车

0

0

0

0

0

0

0

0

1

0

数据指针清零

显示控制

0

0

0

0

0

0

1

D

C

B

D=1:

开;D=0:

C=1:

光标显示

C=1:

光标不显示

B=1:

光标闪烁

B=0:

光标不闪烁

2)屏幕移动指令

指令

指令使能

指令码

描述

RS

RW

DB7

DB6

DB5

DB4

DB3

DB2

DB1

DB0

入口

方式

设置

0

0

0

0

0

0

0

1

I/D

S

I/D=1:

cincrease;

I/D=0:

cdecrease;

S=1:

整屏移动;

S=0:

指针

显示移动

0

0

0

0

0

1

S/C

R/L

*

*

S/C=0,R/L=0:

指针左移显示不移

S/C=0,R/L=1:

指针右移显示不移

S/C=1,R/L=0:

显示和指针左移

S/C=1,R/L=1:

显示和指针右移

3)设置内部RAM地址

指令

指令使能

指令码

描述

RS

RW

DB7

DB6

DB5

DB4

DB3

DB2

DB1

DB0

设置CG

ADDR

0

0

0

1

A

A

A

A

A

A

对字符的点阵

进行编程

设置DD

ADDR

0

0

1

A

A

A

A

A

A

A

设置显示数据

存储器地址

②键盘扫描原理

键盘分为编码键盘,和非编码键盘。

编码键盘是靠硬件电路对每个键位进行编码,当有键按下时,输出固定的数码,并用来判断键位。

常见的编码键盘如PC键盘;非编码键盘是指,键盘不是靠固定的编码来实现对键位的识别的,而是靠一定的算法来对键位进行扫描,矩阵键盘就是常见的一种非编码键盘。

编码键盘一般需要较多的硬件电路,所以成本较非编码键盘较高,故一般的单片机系统都采用非编码键盘,目的是充分利用单片机的丰富的软件资源,弥补硬件的不足。

机械按键在按动的过程中,往往会产程抖动,这又是对一个控制系统的致命伤,因此必须采取措施消除抖动。

目前,消除抖动的方法主要有两种:

利用硬件RS锁存器进行消除抖动,将复杂跳变的信号变成稳定的信号;利用软件算法实现对抖动的排除,灵活性大,较常用,一般的,抖动的持续时间为数毫秒,所以为了简单起见,本设计方案利用延时来消除抖动。

对于键盘的扫描常用的有逐行扫描法和线反转法。

逐行或列扫描法的思路是,利用列线,做输入,行线做输出。

首先使四根列线的某一根置零,当在此列的某一行有键按下时,该行线即为低电平,其余均为高电平,这样这个键就被编码;然后再使另一列线置零,检测下一列是否有键按下;不断这样循环,就可以对整个键盘进行逐列扫描了。

线翻转法的思路是,把列线当做线,所以,对其中某条线置低电平,其余为高电平,然后去读行线状态,那么线的状态和行的状态相连就是当前按下的键的键值,例如,线的状态为1011,读得行的状态为1101,则当前按键的键值为10111101。

综观这两种方法,逐行扫描法,需要多次循环才能对整个键盘进行全扫描,速度慢,而且还有可能导致有些键检测不出,而线翻转法只需两句程序就可以扫描到键值,因此本设计采用先翻转法对键盘进行扫描。

键名

7

8

9

编码值

0x0EE

0x0DE

0x0BE

0x07E

索引值

7

8

9

15

键名

4

5

6

编码值

0x0ED

0x0DD

0x0BD

0x07D

索引值

4

5

6

14

键名

1

2

3

编码值

0xEb

0xDB

0xBB

0x07B

索引值

1

2

3

13

键名

0

编码值

0x0E7

0xD7

0x0B7

0x077

索引值

11

0

12

10

③E2PROM原理

 

三、软件仿真设计

(1)不同软件模块间的同步调度的总体设计

本系统的软件部分的功能模主要有初始化模块、定时器模块、液晶显示模块、键盘扫描模块、键盘识别模块、模式转换模块、常用调用子程序模块。

为了充分利用单片机的资源,对不同模块之间需要合理调度,对于只有一个任务的系统来说,实现该系统的程序往往是顺序执行的程序,各种模块之间也只是调用与被调用的关系,对于更复杂的系统,如菜单系统,也只不过是一个分支结构,菜单反复在死循环里检查输入状态,一旦选中某个选项,马上跳入分支当中顺序执行,执行完毕后立即返回到菜单检测模块。

对于这样的系统,往往不用考虑模块之间的并行关系,只需考虑顺序执行即可。

但是对于本次实现的系统中定时器模块属于中断模块,独立于任何模块;液晶显示模块,键盘扫描模块,键盘识别模块,模式转换模块均为同步的模块(从宏观上讲,各个模块是同步执行的,从微观上讲,是各个模块之间通过一定的标志变量顺序执行的),而初始化模块,子程序调用模块为调用与被调用的模块,无需考虑它与其他模块之间的同步关系。

现在介绍本次设计的各相互同步模块之间的调度关系。

定时器模块为本设计最为独立模块主要又定时器0和定时器1构成:

定时器0工作在方式1,为50ms的定时器,中断服务程序中软计数为键盘扫描模块、计时器运算模块的执行标志变量置位。

键盘扫描的执行可以是随机顺序执行或者时间片轮转方式执行,但是通过分析可以明确,随机顺序方法容易出现扫描失误。

所以采用中断方式的时间片轮转法调度键盘扫描模块,实践证明30ms的周期扫描键盘调度较为合适,但是考虑到计时器的计数触发基本单元是依靠定时器0得到1s的脉冲,而定时器0为50ms的定时间隔,所以利用定时器0产生的每50ms的中断信号,进行为键盘扫描模块的执行标志变量置位。

键盘扫描执行完毕后,进行键位判断,对于模式键的识别时,要调用模式变换模块。

软件框图如下:

 

 

(2)主要的功能模块设计说明

Ⅰ.应用的主要C语言关键字

A.位域的使用

设计中会用到很多标志变量,状态控制变量,如果单单利用CHAR型变量,会有0~255的个状态,会浪费很多存储器容量;所以这里使用位域,可以对一字节的某一位或者几位来操作,可以节省很多空间,利用一字节就可以设计出很多标志变量;然而对于状态变量,对状态变量会进行加减操作,往往还要判断是否溢出,而使用几位的位域变量来说,他的状态只在那几位所允许的状态,如3位,状态为0~7。

位域的声明方法:

Struct位域结构名

{

类型说明符位域名:

位域长度;

……;

……;

}

B.将数组声明成code类型的数据

(Const)数据类型名code变量名[]={…,…,…,…,…,…}

这样做可以节省较少的RAM空间,利用较大的ROM空间。

Ⅱ.初始化模块

A.定时器模块初始化:

定时器0和定时器1的初始化:

定时器0工作在方式1,初值为#3CB0,定时时间为50ms;定时器1工作方式1,初值为#FB1E,定时时间为1.25ms,产生400hz的脉冲。

B.液晶显示器驱动模块

显示模式初始化,数据总线初始化,光标显示初始化,当输入数据时,光标和显示右移。

C.标志变量初始化,系统变量初始化,状态变量初始化。

Ⅲ.定时器模块

键盘执行标志变量置位,计时器执行计数标志变量置位,闹钟检测模块执行标志变量置位

Ⅳ.液晶显示模块

反复对LcdStr字符数组进行扫描显示

Ⅴ.键盘扫描模块

利用线反转法进行扫描,调用keyscan()子程序。

Ⅵ.计时器模块

主要包括闹表检测,闹表输出系统,冒号闪烁,闰年检测和月份检测,时间运算及时间显示。

另外还有加减校时系统,当本系统工作模式0时,可进行加减校时,并可以进行转换,进而加减校分。

Ⅶ.键位识别模块

当检测到有键按下时,读取返回键的编码,利用Switch语句进行分支判断,利用各个键的索引进行判断,分别调用不同的模块。

Ⅷ.模式变换模块

模式共有四个模式分别为模式0、模式1、模式2、模式3;

模式0:

计时器模块,显示万年历功能,精确到秒,可以显示年月日,并可以进行加减校时,并可以像真正的电子时钟一样,“:

”一闪一闪的显示,以体现秒的计时。

模式1:

闹表预置数定义,可以逐个置数,若置数值为非法值则显示错误信息,按确定键返回,继续输入当前的闹表预置数;在预置数时,可以按“+/-”来设置当前设置的闹钟索引。

模式2:

可以直接定义当前的时间,只能定义时分秒,同样具有非法值检测,可以重新输入,输入正确后可以继续设置新值,或者转换到其他模式。

模式3:

显示已设置闹表的时刻值,同样利用“+/-”可以进行显示当前闹表时刻值的索引,进行加减变化

对于这些模块中的可以输入的模块,可以利用“=”进行将已输入的值清空。

常用的调用子程序模块

常用的子程序有:

延时子程序,键盘扫描子程序,清屏子程序,字符串复制子程序,显示子程序。

(3)程序流程图

 

 

四、系统总体功能实现

具体功能:

在开机时刻,欢迎界面滚动显示“welcome”,按“on/c”进入正常模式。

数字输入功能:

在模式1,模式2可以进行手动输入时刻值。

模式切换模式:

按“除”号进行切换模式加减校时、校分的功能,并可以正常溢出返回初值。

利用“*”进行时分切换校准。

在闹表设置和显示的模式中,可以利用“+”,“-”进行选择设置闹表预置数的索引。

键名

7

8

9

功能

数字7

数字8

数字9

切换工作

模式

键名

4

5

6

功能

数字7

数字5

数字6

确定/分时切换

键名

1

2

3

功能

数字1

数字2

数字3

时刻减一/闹表

索引减一

键名

0

功能

启动电子时钟

数字0

模式1/2清屏

时刻加一/闹表

索引加一

键盘各键位的功能

五、程序清单

/*电子时钟设计实验(fosc=6MHZ)*/

#include"reg51.h"

//externchar*strcpy(char*s1,char*s2);

sbitP3_0=P3^0;

sbitRS=P3^3;/*寄存器选择信号*/

sbitRW=P3^4;/*读/写控制信号*/

sbitE=P3^5;/*使能信号*/

sbitCON=P3^6;

sbitCLK=P3^7;

sbitAcc_b=ACC^7;/*定义累加器的第七位*/

sbitP0_0=P0^0;

sbitsign=PSW^5;

unsignedcharkey_value;

structfg

{

unsignedtim:

1;//秒增一允许标志位

unsigneddip:

1;//欢迎界面标志位

unsignedkey:

1;//有键按下标志位

unsignedkey1:

1;//键盘扫描调度时钟标志位

unsignedmode:

2;//状态模式控制

unsignederror:

1;//写数据超出范围错误标志位

unsignedsmart:

1;//闹表信号翻转标志位

}flag;

structffg

{

unsignedf1:

1;//

unsignedclk:

1;//

unsigneddm:

1;//校时较分标志位

unsignedclock:

3;//设置闹表时间索引

unsignedfclk:

1;//

unsignedf:

1;//

}ffg,ffg1;

structyear

{

unsignedren:

1;

unsignedeven:

1;

unsigneder:

1;

unsignedodd:

1;

unsignedfff:

4;

}y;

unsignedchartime,n,nn,sec,min,hour,day,month,cmin[8]={0},chour[8]={0};

unsignedintyear;

constcharcodedday[]={32,31,30,29};

constcharcodeerror[]="pleaseinputagain";

constcharcodeclock[]="CLOCK:

[]";

constcharcodetime1[]="settime:

";

constcharcodecurclk[]="CURCLOCK:

[]";

unsignedintcodetab[]={0xD7,0xEb,0xDB,0xBB,0x0ED,0x0DD,0x0bd,0x0EE,

0x0DE,0x0BE,0x077,0x0E7,0x0B7,0x07B,0x07D,0x07E};/*键码表*/

unsignedcharcodelt[]={0x7f,0x0bf,0x0df,0x0ef};/*行扫描码*/

unsignedcharcodedigit[]={0xc0,0xf9,0xa4,0xb0};

voiddelay(unsignedcharn)

{

unsignedchari,j;

while(n--)

{

for(i=0;i<9;i++)

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

;

}

}

voidstrcpy(char*p1,char*p2)

{

while(*p2!

='\0')

{

*(p1++)=*p2;

p2++;

}

*p1='\0';

}

voidPrint(void)/*写指令子程序*/

{

unsignedcharj;

RW=0;/*RW=0,写指令*/

E=1;

E=0;

for(j=0;j++<100;);/*延时*/

return;

}

voidclear(char*p)

{

char*pstr;

pstr=p;

while(*pstr!

='\0')

{

*(pstr++)='';

}

pstr=p;

}

void_clo_ck()interrupt3

{

//ffg.clk=~ffg.clk;

CLK=~CLK;

TH1=0xFB;

TL1=0x1E;

return;

}

voidtimeint()interrupt1using3/*定时器0中断程序,使用第3组寄存器*/

{

time--;/*循环次数减1*/

n--;

flag.key1=1;

if(time==0)/*若循环值为0,则对P1.0取反*/

{//cibuweifuzhugongnengbushibixude

time=20;/*恢复循环初值*/

flag.tim=1;

}

if(n==0)

{

n=10;

flag.smart=1;

sign=~sign;

}

TH0=0x3c;/*恢复计数初值*/

TL0=0x0b0;

return;/*中断返回*/

}

charscankey(void)

{

unsignedinti,a,b,y;

P2=0x0f;/*行线输出低电平,并判断是否有键按下*/

i=0;/*行计数器清零*/

b=lt[i];

for(i;i<4;i++)/*取行扫描码*/

{b=lt[i];

P2=b;/*送行扫描码到P2口*/

a=P2;/*读入列值*/

a=a&0x0f;/*保留低4位*/

if(a!

=0x0f)

break;

}

if(i<=3)

{/*循环到该列有键按下为止*/

b=b&0x0f0;/*取行扫描码的高4位*/

b=b|a;/*合并成为按键的扫描码*/

i=0;/*计数器清零*/

for(;b!

=tab[i];i++);/*在键值表中查找相应的键值*/

for(y=0;y<2000;y++);/*延时,去抖动*/

key_value=i;

for(P2=0x0f;P2!

=0x0f;);/*判断按键是否结束*/

for(y=0;y<2000;y++);/*延时*/

flag.key=1;

}

return(key_value);

}

display(unsignedchartemp)

{

unsignedk;

for(k=0;k++<100;);

RS=1;/*送出一个字母*/

RW=0;

E=1;

P1=temp;

for(k=0;k++<100;);/*延时*/

E=0;

}

lcd_init(void)

{

unsignedchari,j;

RS=0;/*向LCD写入3条30H?

使之复位*/

RW=0;

P1=0x30;

for(i=3;i>0;i--)

{E=1;

E=0;

for(j=0;j++<100;);/*延时*/

}

P1=0x38;/*设置8位数据总线方式*/

print();

P1=1;/*清屏指令01H*/

Print();/*调向LCD写指令子程序*/

P1=6;/*设置输入方式:

AC加1计数,光标右移1个字符*/

Print();/*设置显示方式:

开显示,光标显示;闪烁*/

P1=0x0c;

Print();

E=1;

E=0;

//j=0;/*显示计数器*/

RS=0;

RW=0;

P1=1;/*设置8位数据总线方式*/

print();

E=1;

E=0;

}

voidinit(void)

{

P0=0xc0;

n=10;

nn=12;

flag.tim=0;

ffg.f1=1;

ffg.clock=0;

}

time_init(void)

{

TMOD=0x11;/*设置计数器0工作方式1*/

TH0=0x3c;/*送计数器初值*/

TL0=0x0b0;

TH1=0xFB;

TL1=0x1E;

ET1=1;

TR1=1;

EA=1;/*开中断*/

ET0=1;

TR0=1;

time=20;/*设置循环次数初值*/

//TR0=1;/*开始计时*/

}

main()/*主程序*/

{

unsignedchari,j,k,m,p=0,vp;

charLcdStr[16]={"welcome"};/*定义数组并初始化*/

char*pstr=LcdStr;

m=10;

lcd_init();

init();

time_init();

j=0;/*显示计数器*/

CON=0;

for(i=0;i<16;i++)/*显示字母*/

{

//busy();/*判忙*/

RS=1;/*送出一个字母*/

RW=0;

E=1;

P1=LcdStr[i];

for(k=0;k++<100;);/*延时*/

E=0;

j++;/*显示计数器加1*/

//if(j==8)/*不到显示位置9跳转*/

//{

//for(k=

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

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2