基于单片机的数字钟课程设计.docx
《基于单片机的数字钟课程设计.docx》由会员分享,可在线阅读,更多相关《基于单片机的数字钟课程设计.docx(41页珍藏版)》请在冰点文库上搜索。
基于单片机的数字钟课程设计
摘要
单片计算机即单片微型计算机。
由RAM,ROM,CPU构成,定时,计数和多种接口于一体的微控制器。
它体积小,成本低,功能强,广泛应用于智能产业和工业自动化上。
而51系列单片机是各单片机中最为典型和最有代表性的一种。
这次课程设计通过对它的学习,应用,从而达到学习、设计、开发软、硬的能力。
本设计是通过单片机设计一个多功能电子表,要求不仅具有电子时钟的功能还具有闹铃、设定闹铃时间、整点报时、生日提醒功能,而且能够预置生日时间
关键字:
单片机;多功能电子表;跑表;数码管显示。
ABSTRACT
Singlechipcomputeristhesinglechipcomputer.ByRAM,ROM,CPUconstitute,time,countandmultipleinterfaceintheintegrationofmicrocontroller.Itssmallsize,lowcost,thefunctionisstrong,widelyusedinintelligentindustryandindustrialautomation.And51seriesmicrocontrolleristhemosttypicalandvariousmicrocontrollermostrepresentativeone.Thiscurriculumdesignthroughtoitsstudy,application,thusachievedthestudy,design,development,softandhardability.
ThisisdesignedbySCMdesignamulti-functionalelectronicwatch,requirementnotonlyhasthefunctionofelectronicclockstillhasalarm,setalarmtime,integralpointtoannounce,birthdayremindsfunction,andcanpresetbirthdaytime
Keyword:
SCM,Multi-functiondigitalwatches,PaoBiao;Digitalpipedisplay.
摘要0
ABSTRACT0
目录1
引言2
1、AT89S52单片机介绍3
2、设计功能及说明6
3、整体设计方案8
4、单模块流程设计及程序设计8
4.1初始化模块8
4.2开关检测模块9
4.3显示主程序9
4.4闹铃及整点报时判断程序(EIGHT)11
4.5中断(TO)计时程序11
4.6中断(T1)预置程序11
4.7中断(INT0)设定程序12
5、单模块软件测试12
5.1编码中常用的程序结构说明12
5.2单模块软件测试中的问题及解决14
6、软件部分烧写调试14
5、总结16
致谢17
参考文献:
18
附录A19
附录B:
19
引言
1957年,Ventura发明了世界上第一个电子表,从而奠定了电子表的基础,电子表开始迅速发展起来。
现代的电子表是基于单片机的一种计时工具,采用延时程序产生一定的时间中断,用于一秒的定义,通过计数方式进行满六十秒分钟进一,满六十分小时进一,满二十四小时小时清零。
从而达到计时的功能,是人民日常生活不可缺少的工具。
现在高精度的计时工具大多数都使用了石英晶体振荡器,由于电子钟、石英钟、石英表都采用了石英技术,因此走时精度高,稳定性好,使用方便,不需要经常调试,数字式电子钟用集成电路计时时,译码代替机械式传动,用LED显示器代替指针显示进而显示时间,减小了计时误差,这种表具有时、分、秒显示时间的功能,还可以进行时和分的校对,片选的灵活性好。
1、AT89S52单片机介绍
AT89S52具有以下标准功能:
8k字节Flash,256字节RAM,32位I/O口线,看门狗定时器,2个数据指针,三个16位定时器/计数器,一个6向量2级中断结构,全双工串行口,片内晶振及时钟电路。
另外,AT89S52可降至0Hz静态逻辑操作,支持2种软件可选择节电模式。
空闲模式下,CPU停止工作,允许RAM、定时器/计数器、串口、中断继续工作。
掉电保护方式下,RAM内容被保存,振荡器被冻结,单片机一切工作停止,直到下一个中断或硬件复位为止。
图1.1单片机引脚图
VCC:
电源GND:
地
P0口:
P0口是一个8位漏极开路的双向I/O口。
作为输出口,每位能驱动8个TTL逻辑电平。
对P0端口写“1”时,引脚用作高阻抗输入。
当访问外部程序和数据存储器时,P0口也被作为低8位地址/数据复用。
在这种模式下,P0具有内部上拉电阻。
在flash编程时,P0口也用来接收指令字节;在程序校验时,输出指令字节。
程序校时,需要外部上拉电阻。
P1口:
P1口是一个具有内部上拉电阻的8位双向I/O口,p1输出缓冲器能驱动4个TTL逻辑电平。
对P1端口写“1”时,内部上拉电阻把端口拉高,此时可以作为输入口使用。
作为输入使用时,被外部拉低的引脚由于内部电阻的原因,将输出电流(IIL)。
此外,P1.0和P1.2分别作定时器/计数器2的外部计数输入(P1.0/T2)和时器/计数器2的触发输入(P1.1/T2EX),具体如下表所示。
表1.1AT89S52P1口第二功能表
脚号
第二功能
P1.0
T2(定时器/计数器T2的外部计数输入),时钟输出
P1.1
T2EX(定时器/计数器T2的捕捉/重载触发信号和方向控制)
P1.5
MOSI(在系统编程用)
P1.6
MISO(在系统编程用)
P1.7
SCK(在系统编程用)
P2口:
P2口是一个具有内部上拉电阻的8位双向I/O口,P2输出缓冲器能驱动4个TTL逻辑电平。
对P2端口写“1”时,内部上拉电阻把端口拉高,此时可以作为输入口使用。
作为输入使用时,被外部拉低的引脚由于内部电阻的原因,将输出电流(IIL)在访问外部程序存储器或用16位地址读取外部数据存储器(例如执行MOVX@DPTR)时,P2口送出高八位地址
P3口:
P3口是一个具有内部上拉电阻的8位双向I/O口,p2输出缓冲器能驱动4个TTL逻辑电平。
对P3端口写“1”时,内部上拉电阻把端口拉高,此时可以作为输入口使用。
作为输入使用时,被外部拉低的引脚由于内部电阻的原因,将输出电流(IIL)。
P3口亦作为AT89S52特殊功能(第二功能)使用,如下表所示。
表1.2AT89S52P3口第二功能表
脚号
第二功能
P3.0
RXD(串行输入)
P3.1
TXD(串行输出)
P3.2
INT0(外部中断0)
P3.3
INT0(外部中断0)
P3.4
T0(定时器0外部输入)
P3.5
T1(定时器1外部输入)
P3.6
WR(外部数据存储器写选通)
P3.7
RD(外部数据存储器写选通)
RST:
复位输入。
晶振工作时,RST脚持续2个机器周期高电平将使单片机复位。
看门狗计时完成后,RST脚输出96个晶振周期的高电平。
特殊寄存器AUXR(地址8EH)上的DISRTO位可以使此功能无效。
DISRTO默认状态下,复位高电平有效。
ALE/PROG:
地址锁存控制信号(ALE)是访问外部程序存储器时,锁存低8位地址的输出脉冲。
在flash编程时,此引脚(PROG)也用作编程输入脉冲。
在一般情况下,ALE以晶振六分之一的固定频率输出脉冲,可用来作为外部定时器或时钟使用。
然而,特别强调,在每次访问外部数据存储器时,ALE脉冲将会跳过。
PSEN:
外部程序存储器选通信号(PSEN)是外部程序存储器选通信号。
当AT89S52从外部程序存储器执行外部代码时,PSEN在每个机器周期被激活两次,而在访问外部数据存储器时,PSEN将不被激活。
EA/VPP:
访问外部程序存储器控制信号。
为使能从0000H到FFFFH的外部程序存储器读取指令,EA必须接GND。
为了执行内部程序指令,EA应该接VCC。
在flash编程期间,EA也接收12伏VPP电压。
XTAL1:
振荡器反相放大器和内部时钟发生电路的输入端。
XTAL2:
振荡器反相放大器的输出端。
2、设计功能及说明
电子数字钟实现的主要功能:
①实现正常走时(秒→分→时→日→月→年进位)
②能够预置时间和日期
③能够自动区分平闰年和大小月
④具有闹铃及设定闹铃时间功能
⑤具有整点报时功能
⑥具有生日提醒功能,能够预置生日时间
⑦在任何一种预置状态下,预置项目会闪烁显示
其它设计说明:
①优先级:
预置>闹铃>报时>生日提醒>正常显示,即预置状态下闹铃、报时功能及生日提醒功能都无效
②生日时间到五个数码管显示“HAPPY”
③生日显示状态下,按下中断INT0恢复正常显示且此后只有在下更改生日日期或第二年生日显示才有效。
④正常显示状态下按下中断INT0进入预置状态,再按一次恢复。
⑤用数码管(K0,K1)控制显示状态:
00:
时间,01:
闹铃时间,10:
日期,11。
生日时间且显示样板如下图所示:
生日:
6月1日生日,最闹铃:
于早晨6点10分响,最后后两位bd为birthday的缩写一位为一表示使能有效。
日期:
08年2月29日时间:
12点39分45秒
⑥预置由拨码开关K2/K3/K4分别控制数码管12/34/56显示值,预置优先级:
LED34>LED12>LED56(相应数码管显示的项目,显示状态下才能预置)
⑦整点报时声音为59分51、53、55、57秒的后半秒报四声低音,59秒的后半秒报一声高音
⑧闹铃每次响一分钟
⑨不论是预置状态还是正常走时状态均能够自动区分平闰年和大小月
⑩闹铃使能由拨码开关K5控制并实时显示
3、整体设计方案
系统硬件电路根据系统功能可知,需要六个数码管,一个外部中断INT0,一个扬声器,由此结合单片机课程学习中对实验面板的了解,做出如下电路:
图3.1系统硬件电路图
如图,晶振频率12MHz,拨码开关数值由P0口读入;P2口的输出作为控制数码管的段控信号,经74LS573即8位数据锁存器接到数码管中。
数码管采用共阴接法(图中未标示);P1.0-P1.5作为数码管的位控信号出端;开关W2经一个基本RS锁存器接入INT0,每按一次开关INTO取反次,按两次输入一个下降沿;扬声器由P1.6口控制。
4、单模块流程设计及程序设计
在完成了系统总流程设计后,便进入到了系统设计中工作量最大的模块流程设计。
由于系统模块划分虽然是系统设计的基础,但比较简单而且模块划分有多种方法,因此单模块的设计可以认为是本程序中最关键的部分。
单模块设计的好坏与否决定了系统功能能否被稳定地实现以及下一阶段调试程序的难度。
鉴于单模块设计的重要性,我在设计中并不急于对模块进行编码,而是反复论证模块的可行性和流程图,为下一阶段的工作做好准备,同时也能够尽早发现系统模块划分的错误,加以解决。
4.1初始化模块
初始化模块包含了各中断的跳转语句、存储资源的分配、各存储器和寄存器的赋值以及中断及计时器的状态设置。
源程序中共使用了17字节的内部数据存储地址,其中包含3字节位寻址地址以便于进行位判断。
三字节的位寻址地址分配如下:
22H—秒的存储地址,方便对整点报时条件进行判断
23H—月的存储地址,方便对大小月平闰年进行判断
另外一个位寻址字节00H地址拆开用做程序状态标志位
00H(bit,BS)—生日标志位,到生日那天为1使程序转入生日显示段
01H(bit,SS)—预置标志,以INT0控制其值,控制预置闪烁的实现
02H(bit,GS)—闪烁标志,在预置标志有效时控制闪烁项目的亮和灭两状态
03H(bit,FC)—频率控制,在整点报时时控制产生低音还是高音
04H(bit,NB)—忽略生日标志,控制预置及报时的优先级高于生日显示
05H(bit)—控制生日信号的有效与否,其使用目的参见调试过程
源程序中使用了两个寄存器R1、R0,R1用于控制闪烁的间隔时间,R0用于显示延时。
在初始化模块中设定了中断的优先级、定时计数器的工作模式、并打开了定时计数器TO开始计时其他资源分配项目参见源程序集合及注释。
初始化模块指令很大一部分都是伪指令,方便了后面程序的编写。
4.2开关检测模块
开关检测模块主要执行两个任务:
⒈读取闹铃使能控制拨码开关K5的值并赋予闹铃使能相应值;
⒉读取显示选择开关(K0,K1)的值并将要显示的项目值送入显示缓存LED12、LED34、LED56当k5为1时将A1H送入RE反之送入A1H,其中A为“—”的字型码查表偏移量。
当显示生日日期时,将生日Birthday的缩写“bd”的字型码送入显示缓存LED56。
图4.1开关检测流程图
4.3显示主程序
显示主程序是程序中最复杂的模块,实现的功能也最多,占用了程序运行过程当中90%以上的执行时间。
流程图中由判断SS(预置信号)开始分成两个分支,当SS=1,即预置状态下程序进入预置闪烁部分。
图4.2显示主程序流程图
预置闪烁的优先级是:
LED34>LED12>LED56即同预置优先级相同(参见调试部分的详细说明)闪烁首先判断是否对LED34进行判断闪烁,如果是则另外两个显示值直接显示,否则判断是否对LED12进行闪烁,如果是则直接显示LED56的值,只有当前两个显示值都不闪烁的时候,才判断是否对LED56进行闪烁,这样就确保了每次只有一个显示值闪烁。
预置通过GS标志位判断预置的亮/灭,由R1控制每50轮显示对GS取反一次。
当SS=0时进入判断闹铃/报时及正常显示模块,模块EIGHT(参见其说明)对闹铃及报时条件进行判断,符合条件则对P1.6取反一次并使NB(忽略生日)有效,此时行对BS(生日标志)的判断,即使响铃优先级高于生日显示(其实也可以对程序进行修改使响铃和生日显示不发生冲突,修改方法参见程序优化)。
当不响铃并且生日标志BS有效时显示HAPPY其单列开来。
本程序由生日标志BS控制,当到达生日时间时,T0中断程序会将BS置1使之有效在显示主程序中通过判断BS的置来控制是否执行本模块标志BS有效时显示HAPPY其单列开来。
本程序由生日标志BS控制,当到达生日时间时,T0中断程序会将BS置1使之有效在显示主程序中通过判断BS的置来控制是否执行本模块
4.4闹铃及整点报时判断程序(EIGHT)
在显示主程序中每隔一段时间调用一次本程序来实现闹铃及报时功能。
符合闹铃条件时,每调用一次本程序对P1.6取反一次,调节调用的时间间隔便可以调节报时的输出频率。
在主程序中设置每完成对一个数码管的显示变调用一次本程序,显示一个数码管的时间大约为1ms,因此响铃频率高音约为500Hz,低音约为250Hz。
程序判断闹铃使能RE后分成两个分支,当RE=A1时判断闹铃条件是否满足,满足则对P1.6取反一次,否则跳转判断整点报时条件是否满足。
当RE=0时,直接怕暖整点报时条件是否满足。
判断整点报时条件时当当前时间满足:
(MIN=59)∩(SECOND=59)∩(COUNT>10)
时报高音;当满足:
(MIN=59)∩(SECOND=51/53/55/57)∩(COUNT>10)
时报低音。
利用位寻址区来存储SECOND的方法,(SECOND>50)∩(SECOND.0=1)时便满足报低音的秒条件。
利用TO中断次数存储器COUNT的大小可以判断是否到了后半秒(T0每50ms中断一次,中断20次到达一秒)。
通过增加一个标志位FC来时下对高低音的区分,高音不受FC的影响,低音只有当FC=0的时候才对P1.6取反一次。
并且每调用一次本程序便对FC取反一次,便达到每调用两次取反一次P1.6的效果。
本程序的执行时间小于40us,因此多次调用不过分影响执行。
4.5中断(TO)计时程序
中断计时程序的流程比较简单。
程序除实现正常走时外还需要判断是否到达生日时间置生日标志位有效。
程序的输出均为BCD码,方便了显示程序对高低位的分离并查表显示。
程序比较复杂的地方在于对平闰年及大小月的判断。
把月MONTH存储在位寻址区,当MONTH=02H时判断是否是平闰年,先将年由BCD码转化为二进制码,此时当年的后两位为全零的时候为闰年(由于只显示年的个位和十位,由2000年开始,00年、04年…为闰年,其共同特点就是二进制后两位为零),否则为平年。
当MONTH≠2时判断是否为大小月,MOUTH<7时单月(即MONTH.0=1的月)为大月,否则为小月;当MONTH>7时双月为大月。
4.6中断(T1)预置程序
T1通过中断INT0控制开启或关闭,每次定时50毫秒,存储地址COUNTS计数15次执行一次中断程序。
由开关(K0,K1)控制预置项目,通过开关K2/K3/K4来分别控制LED12/LED34/LED56的预置,当两个开关都是有效时,具有优先级的区分,确保无论按几个开关都最多只有一个项目被预置。
判断预置的优先级顺序是:
LED34>LED12>LED56
这样安排的原因是由于根据生活经验LED34所对应的4个项目(小时、闹铃分、月、生日日)被预置的可能性最大。
在预置日期和生日是要注意对大小月及平闰年进行判断(判断方法与中断计时程序中说明的方法类似,但不需要考虑进位)且月和日在溢出后应置一而不是归零。
4.7中断(INT0)设定程序
中断INT0设置是否进行预置以及对生日标志的清除。
且有如下设置顺序:
清除预置信号并关T1>清除生日标志>设定预置信号并开T1。
每按一次中断都只执行其中一个项目
5、单模块软件测试
5.1编码中常用的程序结构说明
①JNBSS,THREEB;
SJMPTHREEA
THREEB:
LJMPTHREE11;
THREEA:
JNBP0.3,THREE1
由于使用JNB等跳转指令的时候,跳转长度比较短,故需要跳转到比较远的地方时需要用
这个结构。
相当于:
JNBSS,THREE11
JNBP0.3,THREE1
②THREE1:
MOVA,LED34
ANLA,#0FH
MOVCA,@A+DPTR
MOVP2,A
THREE13:
SETBP1.3
NOP
DJNZR0,THREE13
CLRP1.3
MOVA,LED34
SWAPA
ANLA,#0FH
MOVCA,@A+DPTR
MOVP2,A
THREE14:
SETBP1.2
NOP
DJNZR0,THREE14
CLRP1.2
显示主程序中常用的语句段,分离LED34中的高半位和低半位作为字形码段控信号查表偏移量,通过R0延时,延时时间大约为每个数码管1ms。
③TEN6A:
MOVA,MONTH
CJNEA,#08H,TEN7A
TEN7A:
JCTEN9A
判断MONTH是否大于08H,由于CJNE指令不能对直接地址进行比较只能通过A来判断。
④MOVA,YEAR
ADDA,#01
DAA
MOVYEAR,A
对T0及T1中断程序中常用的对某项目进行加一并进行BCD码调整的的程序段,由于“INCA”指令不影响标志位,故不能和“DAA”指令结合使用。
5.2单模块软件测试中的问题及解决
由于此前花费大量的经历在模块的流程图设计当中,因此在模块调试的过程中并没有出现太多的问题。
主要问题在于语法错误,比如前述程序结果说明中的①、④语句和一些输入的误。
并且由于程序过于长,难以进行完整的软件调试,调试过程中发现的问题主要出现在烧写调试过程中。
6、软件部分烧写调试
我一共进行了三次软件部分烧写调试,纠正了许多程序错误,主要修改部分如下:
第一次调试:
①中断预置程序无法进行正常的加一程序
②计时进位有误,无法正常进位
③日期无法正常显示
④闪烁有错误,无法正常闪烁
⑤显示的数字不稳定,有许多干扰
从第一次调试的结果上看,几乎所有的功能都无法正常显示。
事实上,在第一次调试后对程序进行检查后发现,造成这些问题的主要原因是程序有十几处的输入有误,并不是程序流程上的问题。
因此这里不做过多的叙述。
第二次调试:
经过第一次调试以后的修改,解决了第一次调试中出现的前四个问题,但第五个问题“显示的数字不稳定,有许多干扰”仍然无法发现问题的所在,在机房进行了多次修改都不成功。
经过思考和对跟其他同学的显示语句进行比对,我判断问题的原因可能是我在过去编程时养成的一个不好的习惯,我经常使用的一个延时办法,容易把干扰放大。
对于数码管的显示及延时我经常使用以下语句:
“AAA:
MOVP1,#位控
MOVP2,#段控
DJNZR0,AAA”
这个语句段比较简单,不需要另外编写一个延时程序,延时时间大约1.3ms,用在干扰语句比较少的地方很方便。
但却可能出现一个隐患,假如P1或者P2在赋值过程中会出现干扰,那么对其重复的赋值便会放大干扰。
并且由于常常只需要给其中一个数码管赋值,而这个语句却一次性给所有的数码管赋位控信号,可能将单个数码管段控信号中出现的干扰传递到其他管中。
因此我对以上程序做了修改如下:
MOVP2,#段控
THREE13:
SETBP1.3
NOP
DJNZR0,THREE13
CLRP1.3
这样便解决了以上提出的问题。
实际上,通过调用延时程序的方法进行延时同样也不会出现以上问题,由此可见,在软件设计中养成一个规范的设计习惯极为重要。
在第二天调试的时候发现这个语句的确能够避免干扰的出现。
第三次调试:
第三次调试基本解决了上述出现的问题,但又发现了两个新问题:
①在程序执行的过程中,一旦进入生日显示状态便无法通过INT0跳出,除非日期走过一天。
②在同时拨下两个以上的预置开关时,预置和闪烁可能出现不同步的情况。
比如预置时间时可能出现小时在闪烁,但改变的确实分钟的值。
由于对程序的流程比较熟悉,产生这两个问题原因很快就被我发现。
第一个问题产生原因主要是虽然每次按下INT0都将清除生日标志,但生日标志的设定却是每秒进行一次,因此生日标志被清除后马上就又会出现,所以无法跳出生日显示。
为了解决这个问题,我增加了一个标志位05H(bit)来控制生日标志的设定,只有在05H(bit)有效的情况下,生日显示才有效,在按INT0清除生日表示的时候同时清除05H(bit),而05H(bit)只有在重新预置生日时间以后或是过了新的一年才会有效,其设定不受外界控制。
第二个问题产生的原因主要是比较简单,预置时间时,判断哪个项目闪烁的优先级,和判断哪个项目数值改变的优先级不一致,由于是中断程序和显示主程序同时控制这个功能,因此这两个优先级必须一样,否则当同时按下两个项目的预置开关的时候,中断程序首先判断一个开关有效,改变其对应值,而显示主程序却首先判断第二个开关有效,对第二个开关的显示值进行闪烁。
因此只要修改其优先级一致即可解决这个问题。
程序中设置的优先级是K3>K2>K4。
5