以AT89S51单片机为核心的数字电子钟控制器.docx
- 文档编号:18501021
- 上传时间:2023-08-18
- 格式:DOCX
- 页数:27
- 大小:325.44KB
以AT89S51单片机为核心的数字电子钟控制器.docx
《以AT89S51单片机为核心的数字电子钟控制器.docx》由会员分享,可在线阅读,更多相关《以AT89S51单片机为核心的数字电子钟控制器.docx(27页珍藏版)》请在冰点文库上搜索。
以AT89S51单片机为核心的数字电子钟控制器
一、设计题目:
设计一个以AT89S51单片机为核心的数字电子钟控制器,实现电子钟的时间、日期交替显示、闹钟功能,并可通过按钮开关或键盘切换显示内容、调整参数、设置闹钟,在单片机实验板上模拟调试实现控制器的功能。
二、设计目的:
通过单片机应用产品的设计与调试过程,巩固课程所学理论知识,初步了解单片机应用系统设计与调试的方法。
三、设计要求:
1.开机自检,检查相关接口及数码管显示器、指示灯、蜂鸣器等外设是否正常。
2.8位数码管显示器平常以一定的时间间隔、合适的格式显示时间和日期信息,时间显示时、分、秒;日期显示年(2000~2099)、月、日;设置闹钟功能时显示时、分、开/关状态。
3.可通过按键设定时间、日期、闹钟等参数、手动切换显示。
按键可用独立式按键或行列式键盘实现。
设定参数过程有合适的方式指示当前可修改的内容。
4.对开关量输入进行软件消抖动处理,参数的设定有容错处理,如:
小时不能超过23,日期中每月最大天数、闰年等。
5.用Protel设计可实现上述功能的控制器的原理图(最小应用系统)。
四、总体方案设计及说明:
本设计电子时钟系统,主控芯片采用AT89S51芯片。
配合软件延时实现时、分、秒的计时。
本系统设计采用此种软件控制方法来实现计时,通过定时器定时中断,使时间缓存单元数据加一,提供系统时间。
同时可以通过独立键盘对电子时钟开始计时,暂停计时和结束计时的控制。
单片机内的数据通过并行I/O接口输入输出。
并驱动八位LED数码管(时、分、秒分别用2位数码管显示),动态显示数据。
晶振及复位电路为单片机提供工作脉冲及复位信号。
五、系统资源分配说明:
接口电路逻辑图:
使用定时计数器0,TMOD设置为0x01;晶振频率11.0592MHz下,定时50ms的计数初值经过计算为4c00H(2^16-46080=4c00H),TH0设置为0x4c,TL0设置为0x00。
五、软件流程图及说明:
1.闹钟显示
闹钟显示子程序,8个数码管显示已经设置好的闹钟时间,进行按键扫描,判断有没有按键,若有就去执行相应的功能,日期显示子程序和时间显示子程序和闹钟显示子程序类似。
2.键盘子程序
按键扫描:
功能扫描:
3.中断子程序
六、系统功能与操作说明:
数字时钟能显示实时时间(HH-MM-SS)、日期(20YY.MM.DD)于LED数码管,并可以修改时间、日期。
可以设置闹钟,时间到时,蜂鸣器会响起。
按切换键可以在时间、日期、闹钟时间中切换。
时间、日期、闹钟时间可调,按外部中断键时间暂停,进入时间、日期、闹钟时间设置,此时可以矩阵键盘输入时间日期,输入完后确认键时间按照设置的值接着走。
键盘用行列式键盘,按键定义如下图所示。
主要的按键有:
(1)数字键“0”~“9”,用于设置时输入时间和日期,正常工作时无效。
(2)“时间设置”、“日期设置”、“闹钟设置”,用于进入相应功能的设置状态。
(3)“闹钟开关”在闹钟设置状态时,用于设置闹钟开或关,正常工作时,用于手动关闹铃的声音。
(4)“确认”用于设置参数的确认,并检查参数是否合理,如果符合要求,则参数有效;否则本次修改无效,保持原值。
正常工作情况下按该键无操作。
(5)“显示切换”用于正常工作时手动切换时间、日期的显示。
七、调试记录:
1、确定课程设计课题为数字电子钟,并查找相关资料。
2、熟悉实验板数码管、两个8255、键盘、LED等硬件资源的使用,弄清楚其对应的地址。
3、调试出可以扫描矩阵键盘输入的数的程序,并在数码管上显示出来当前按下的键值。
4、调试出可以显示时分秒和日期的时钟程序,并可以通过4个按键进行控制(分别为进入\退出时间设置、时增1、分增1、秒增1),并可以切换时间和日期。
5、调试闹钟的显示和设置程序,时间到了所设置的时间时蜂鸣器响起。
6、时间可以走,但只能通过在程序中给对应时间日期字节地址预先设置初始值,时钟根据该初始时间接着走,不能通过矩阵键盘输入时间日期。
最后查阅资料并询问同学终于解决了这个问题。
7、对整个程序做进一步优化处理,使其更稳定,修改其中的一些瑕疵,加上注释,方便以后读程序。
八、实验设计总结:
通过努力终于设计出一个以AT89S51单片机为核心的数字电子钟控制器,实现电子钟的时间、日期交替显示、闹钟功能,并可通过按钮开关或键盘切换显示内容、调整参数、设置闹钟。
这次实验我收获了许多,得到了许多书本上学不到的知识。
1、单片机的软件设计通常要考虑以下几个方面的问题:
(1)根据软件功能要求,将系统软件划分为若干个相对独立的部分,设计出合理的总体结构,使软件开发清晰、简洁和流程合理;
(2)培养良好的编程风格,如考虑结构化程序设计、实行模块化、子程序化。
既便于调试、链接,又便于移植和修改;
(3)绘制程序流程图,再根据流程图写程序;
(4)合理分配系统资源;
(5)为程序加入注释,提高可读性,便于以后修改移植复用;
2、关于程序的调试问题:
(1)软件程序的调试应先分模块调试,统调是最后一环。
通过各个模块程序的调试,使程序逐渐趋于完整、正确,最后将各个模块组合起来,统调程序。
(2)充分利用实验板上的其他空余硬件资源来协助调试程序,比如,可以用CPLP1.X来设置指示灯,用来指示有没有调用键盘扫描子程序、用来指示有没有扫描到键值、用来指示外部中断、定时器0和定时器1有没有工作等等。
附录:
#include
#include
#definePAXBYTE[0x0fefc]
#definePBXBYTE[0x0fefd]
#definePCXBYTE[0x0fefe]
#defineCTLXBYTE[0x0feff]
#defineucharunsignedchar
ucharms20;
sbitsound=P3^2;
ucharopen1=-1;//闹钟关闭
codeled[19]={0x0C0,0x0F9,0x0A4,0x0B0,0x99,0x92,0x82,0x0F8,0x80,0x90,0x88,0x83,
0x0c6,0x0a1,0x86,0x8e,0x0bf,0x8c,0x0c7};
codeTable[16]={0x7e,0x0ee,0x0ed,0x0eb,0x0de,0x0dd,0x0db,0x0be,0x0bd,0x0bb,
0x0e7,0x0d7,0x0b7,0x77,0x7b,0x7d};
struct
{
ucharhour;
ucharminute;
ucharsecond;
ucharhour1;//闹钟小时
ucharminute1;//闹钟分钟
intyear;
ucharmonth;
ucharday;
}time={23,59,55,00,00,2011,11,11};
voiddelay()
{
uchari;
for(i=0;i<200;i++);/*200个时钟周期*/
}
voidscan(ucharx)
{
uchari,tb,tc=0x80;
for(i=0;i<8;i++)
{
tb=DBYTE[x+i];
PA=led[tb];/*送字段码*/
PB=tc;/*送字位码*/
tc>>=1;/*右移一位*/
delay();
}
}
intfastfound()
{
ucharkeyin;
CTL=0x81;//PC3~PC0为输入,PC7~PC4为输出
PC=0x0f;//所有行(PC7~PC4)置0
keyin=PC;//读取键盘
if(keyin==0x0f)return0;//无键按下,返回0
elsereturn1;//有键按下,返回1
}
intkeyfound()
{
ucharkeyvalue,keyin_i,keyin_j,keyin;
uchari;
CTL=0x81;//PC3~PC0为输入,PC7~PC4为输出
PC=0x0f;
keyin_j=PC;//读取列值
CTL=0x88;//PC3~PC0为输出,PC7~PC4(所有行)为输入
PC=0x0f0;
keyin_i=PC;//读取行值
keyin=keyin_j|keyin_i;//形成行列组合
for(i=0;i<16;i++)
if(Table[i]==keyin)
{
keyvalue=i;
break;
}
returnkeyvalue;
}
voidshowclock()//显示闹钟
{
inta;
ucharkey=1;
while(key==1)key=fastfound();//消除按功能键时产生的影响
for(a=0;a<800;a++)
{
if(open1==1)
{
DBYTE[0x57]=0;
DBYTE[0x58]=17;//显示"P",与前一个单元组合成"OP",表示闹钟已经打开
}
else
{
DBYTE[0x57]=0x0c;
DBYTE[0x58]=18;//显示"L",与前一个单元组合成"CL",表示闹钟已经关闭
}
DBYTE[0x59]=0x10;
DBYTE[0x5a]=time.hour1/10;
DBYTE[0x5b]=time.hour1%10;
DBYTE[0x5c]=0x10;
DBYTE[0x5d]=time.minute1/10;
DBYTE[0x5e]=time.minute1%10;
scan(0x57);
if(fastfound()==1)
{
key=keyfound();
if(key==0x0a)
{
break;
}
}
}
}
voidrq()//显示日期
{
inta;
ucharkey=1;
while(key==1)key=fastfound();//消除按功能键时产生的影响
for(a=0;a<800;a++)
{
DBYTE[0x47]=time.year/1000;
DBYTE[0x48]=time.year%1000/100;
DBYTE[0x49]=time.year%100/10;
DBYTE[0x4a]=time.year%10;
DBYTE[0x4b]=time.month/10;
DBYTE[0x4c]=time.month%10;
DBYTE[0x4d]=time.day/10;
DBYTE[0x4e]=time.day%10;x`
scan(0x47);
if(fastfound()==1)//有键按下
{
key=keyfound();//获得键值
if(key==0x0a)//如果又按了显示切换键,则显示闹钟时间
{
showclock();
break;
}
}
}
}
ucharisleap(intx)//判断闰年
{
if((x%4==0&&x%100!
=0)||(x%400==0))return1;
elsereturn0;
}
voiddatechange()
{
while
(1)
{
if(time.month==1&&time.day<31){time.day++;break;}
if(time.month==1&&time.day==31){time.month++;time.day=1;break;}
if(isleap(time.year))//是闰年
{
if(time.month==2&&time.day<29){time.day++;break;}
if(time.month==2&&time.day==29){time.month++;time.day=1;break;}
}
else//不是闰年
{
if(time.month==2&&time.day<28){time.day++;break;}
if(time.month==2&&time.day==28){time.month++;time.day=1;break;}
}
if(time.month==3&&time.day<31){time.day++;break;}
if(time.month==3&&time.day==31){time.month++;time.day=1;break;}
if(time.month==4&&time.day<30){time.day++;break;}
if(time.month==4&&time.day==30){time.month++;time.day=1;break;}
if(time.month==5&&time.day<31){time.day++;break;}
if(time.month==5&&time.day==31){time.month++;time.day=1;break;}
if(time.month==6&&time.day<30){time.day++;break;}
if(time.month==6&&time.day==30){time.month++;time.day=1;break;}
if(time.month==7&&time.day<31){time.day++;break;}
if(time.month==7&&time.day==31){time.month++;time.day=1;break;}
if(time.month==8&&time.day<31){time.day++;break;}
if(time.month==8&&time.day==31){time.month++;time.day=1;break;}
if(time.month==9&&time.day<30){time.day++;break;}
if(time.month==9&&time.day==30){time.month++;time.day=1;break;}
if(time.month==10&&time.day<31){time.day++;break;}
if(time.month==10&&time.day==31){time.month++;time.day=1;break;}
if(time.month==11&&time.day<30){time.day++;break;}
if(time.month==11&&time.day==30){time.month++;time.day=1;break;}
if(time.month==12&&time.day<31){time.day++;break;}
if(time.month==12&&time.day==31){time.month=1;if(time.year!
=2099)time.year++;break;}
}
}
ucharcheckall(ucharx)
{
uchari,h,m,s,month,d;
inty;
if(x==0x0c)//对闹钟设置进行判断
{
for(i=3;i<8;i++)
{
if(i==5)continue;//如果是原来的"-",就去判断下一个单元的按键是否合法
if(DBYTE[0x57+i]==0x10)return0;//如果按键不是数字,结束
}
h=DBYTE[0x5a]*10+DBYTE[0x5b];
m=DBYTE[0x5d]*10+DBYTE[0x5e];
if(h>24||(h==24&&(h!
=0||s!
=0)))return0;
if(m>=60)return0;
return1;//按键合法返回1
}
if(x==0x0f)//对时间设置进行判断
{
for(i=0;i<8;i++)
{
if((i+1)%3==0)continue;/*如果是原来的"-",就去判断下一个单元的
按键是否合法*/
if(DBYTE[0x57+i]==0x10)return0;//如果按键不是数字,结束
}
h=DBYTE[0x57]*10+DBYTE[0x58];
m=DBYTE[0x5a]*10+DBYTE[0x5b];
s=DBYTE[0x5d]*10+DBYTE[0x5e];
if(h>24||(h==24&&(h!
=0||s!
=0)))return0;
if(m>=60)return0;
if(s>=60)return0;
return1;//按键合法返回1
}
if(x==0x0e)//对日期设置进行判断
{
for(i=0;i<8;i++)
{if(DBYTE[0x57+i]==0x10)return0;}//如果按键不是数字,结束
y=DBYTE[0x57]*1000+DBYTE[0x58]*100+DBYTE[0x59]*10+DBYTE[0x5a];
month=DBYTE[0x5b]*10+DBYTE[0x5c];
d=DBYTE[0x5d]*10+DBYTE[0x5e];
if(y<2000||y>2099)return0;
if(month==0||month>12)return0;
if(d==0||d>31)return0;
if(month==1&&d>31)return0;
if(month==3&&d>31)return0;
if(month==4&&d>30)return0;
if(month==5&&d>31)return0;
if(month==6&&d>30)return0;
if(month==7&&d>31)return0;
if(month==8&&d>31)return0;
if(month==9&&d>30)return0;
if(month==10&&d>31)return0;
if(month==11&&d>30)return0;
if(month==12&&d>31)return0;
if(isleap(y))//是闰年
{
if(month==2&&d>29)return0;
}
else//不是闰年
{
if(month==2&&d>28)return0;
}
return1;
}
}
voidsetclock()
{
uchari,j,k,key,keyin;
k=1;key=1;
while(key==1)key=fastfound();//消除按功能键时产生的影响
DBYTE[0x57]=0x10;
DBYTE[0x58]=0x10;
DBYTE[0x59]=0x10;
DBYTE[0x5a]=0;
DBYTE[0x5b]=0;
DBYTE[0x5c]=0x10;
DBYTE[0x5d]=0;
DBYTE[0x5e]=0;
for(i=0;i<2;i++)
{
for(j=0;j<2;j++)
{
while(k)
{
key=fastfound();
if(key==1)
{
keyin=keyfound();k=0;}/*如果有键按下,则退出while;否则,
等待按键*/
scan(0x57);//显示按键
}
if(keyin<0||keyin>9)keyin=0x10;//如果按键的值非法,置为"-"
DBYTE[0x5a+i*3+j]=keyin;
while(key==1)key=fastfound();/*判断按键以后,手是否已放开;如果没
放开,等待*/
k=1;//用于进入下一次按键状态
}
DBYTE[0x5a+i*3+j]=0x10;//设置该单元显示"-"
}
while
(1)//设置时间完成后,判断是否按了确认键
{
key=fastfound();
scan(0x57);
if(key==1)keyin=keyfound();
if(keyin==0x0d)/*判断按键是否为确认键,如果是,则进行按键的判断;
如果不是,则等待*/
{
if(checkall(0x0c)==0)break;//如果按键非法,退出,保持原来的值
time.hour1=DBYTE[0x5a]*10+DBYTE[0x5b];
time.minute1=DBYTE[0x5d]*10+DBYTE[0x5e];
break;
}
}
}
voidsetdate()
{
uchari,k,key,keyin;
k=1;key=1;
while(key==1)key=fastfound();//消除按功能键时产生的影响
DBYTE[0x57]=time.year/1000;
DBYTE[0x58]=time.year%1000/100;
DBYTE[0x59]=time.year%100/10;
DBYTE[0x5a]=time.year%10;
DBYTE[0x5b]=time.month/10;
DBYTE[0x5c]=time.month%10;
DBYTE[0x5d]=time.day/10;
DBYTE[0x5e]=time.day%10;
for(i=0;i<8;i++)
{
while(k)
{
key=fastfound();
if(key==1)
{
keyin=keyfound();k=0;}//如果有键按下,则退出while;否则,等待按键
scan(0x57);//显示按键
}
if(keyin<0||keyin>9)keyin=0x10;//如果按键的值非法,置为"-"
DBYTE[0x57+i]=keyin;
while(key==1)key=fastfound();/*判断按键以
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- AT89S51 单片机 核心 数字 电子钟 控制器