第二章电子时钟
2.1电子时钟介绍
1957年,Ventura发明了世界上第一个电子表,从而奠定了电子时钟的基础,电子时钟开始迅速发展起来。
现代的电子时钟是基于单片机的一种计时工具,采用延时程序产生一定的时间中断,用于一秒的定义,通过计数方式进行满六十秒分钟进一,满六十分小时进一,满二十四小时小时清零。
从而达到计时的功能,是人民日常生活补课缺少的工具。
2.2电子时钟的基本特点
现在高精度的计时工具大多数都使用了石英晶体振荡器,因为电子钟、石英钟、石英表都采用了石英技术,因此走时精度高,稳定性好,使用方便,不需要经常调试,数字式电子钟用集成电路计时,译码代替机械式传动,用LED显示器代替指针显示进而显示时间和日期,减小了误差,这种表具有时、分、秒显示时间的功能和年月日显示日期的功能,还可以进行校对,片选的灵活性好。
2.3电子时钟的原理
该电子时钟由AT89C51,74LS373,BUTTON,数码管等构成,采用晶振电路作为驱动电路,由延时程序和循环程序产生的一秒定时,达到时分秒的计时,六十秒为一分钟,六十分钟为一小时,满二十四小时为一天,满三十天<闰年二月满二十九天,平年二月满二十八天)为一个月,满十二个月为一年。
电路中的键控1实现“年”和“分”的调节,键控2实现“月”和“时”的调节,键控3实现“日”和“分”的调节。
每按一次就加一。
2.4电子时钟元件列表
序号
名称
数量
序号
名称
数量
1
单片机AT89S51
1片
9
2.7K电阻
7只
2
12M晶振
1个
10
104瓷片电容
4只
3
共阳数码管
6只
11
30P瓷片电容
2只
4
PNP管8550
7只
12
200Ω电阻
1只
5
74LS244
1片
13
560Ω电阻
8只
6
蜂鸣器
1个
14
10K电阻
12只
7
微动按键
5个
15
100Ω电阻
4只
8
10UF电容
1只
2.5电子时钟现象
单片机电子时钟,是利用单片机技术实现计时、时间显示、时间调整、定时调整、闹铃等功能。
完成该项目能够有效的训练大家的单片机应用能力,电子时钟的设计过程中,除了应用单片机前六课题的基础知识外,还使用了外部中断技术、定时器中断技术、键盘查询及动态显示技术,希望大家通过该项目的设计,对单片机应用技术有一个较深的认识。
2.6单片机电子时钟功能
计时显示
2.7系统的硬件描述
1.控制器用AT89S51,12M晶振
2.数码管动态扫描驱动——P2口
3.数码管段码驱动——P1口
4.闹铃驱动——P1.0
5.调整键K1——P3.2(外部中断0,正常、调时、调分、调秒>
6.定时/正常切换键K2——P3.3
7.时间参数低位加1键K3——P3.4
8.时间参数高位加1键K4——P3.5
2.8系统的设计构思
1.主流程是取时间参数,显示时间参数。
2.利用T0中断来完成计时、比较定时时间、驱动闹铃。
3.利用T1中断完成动态显示中,调整时间闪烁效果的定时。
4.利用外部中断0来完成调整选择功能。
5.利用外部中断1完成定时显示,当前时间显示的切换
6.K3、K4键完成时间参数的循环加1操作。
第三章控制系统的硬件设计
3.1单片机型号的选择
通过对多种单片机性能的分析,最终认为89C51是最理想的电子时钟开发芯片。
89C51是一种带4K字节闪烁可编程可擦除只读存储器的低电压,高性能CMOS8位微处理器,器件采用ATMEL高密度非易失存储器制造技术制造,与工业标准的MCS-51指令集和输出管脚相兼容。
因为将多功能8位CPU和闪烁存储器组合在单个芯片中,ATMEL的89C51是一种高效微控制器,而且它与MCS-51兼容,且具有4K字节可编程闪烁存储器和1000写/擦循环,数据保留时间为10年等特点,是最好的选择。
3.2数码管显示工作原理
数码管是一种把多个LED显示段集成在一起的显示设备。
有两种类型,一种是共阳型,一种是共阴型。
共阳型就是把多个LED显示段的阳极接在一起,又称为公共端。
共阴型就是把多个LED显示段的阴极接在一起,即为公共商。
阳极即为二极管的正极,又称为正极,阴极即为二极管的负极,又称为负极。
通常的数码管又分为8段,即8个LED显示段,这是为项目应用方便如设计的,分别为A、B、C、D、E、F、G、DP,其中DP是小数点位段。
而多位数码管,除某一位的公共端会连接在一起,不同位的数码管的相同端也会连接在一起。
即,所有的A段都会连在一起,其它的段也是如此,这是实际最常用的用法。
数码管显示方法可分为静态显示和动态显示两种。
静态显示就是数码管的8段输入及其公共端电平一直有效。
动态显示的原理是,各个数码管的相同段连接在一起,共同占用8位段引管线;每位数码管的阳极连在一起组成公共端。
利用人眼的视觉暂留性,依次给出各个数码管公共端加有效信号,在此同时给出该数码管加有效的数据信号,当全段扫描速度大于视觉暂留速度时,显示就会清晰显示出来。
图2共阳极数码管
3.374LS244介绍:
该电路使用芯片74LS244来驱动发光两极管,74LS244芯片说明书如下,使用时注意判断芯片的引脚号,引脚示意图如下:
3.4电子时钟原理图
第四章软件设计
4.1模块划分
主程序函数
LED显示函数
键盘检测函数
查键值函数
时钟设置函数
闹钟设置函数
闹钟判断函数
定时器中断函数
加1修改功能函数
图5.1
ftion<)
4.1.1时钟设置函数ftion<)
关定时器
禁止闹钟修改、清闹钟修改标志
设置时钟秒单元为显示首地址
时钟修改记录值加1
记录值为1吗?
将时钟秒修改标志置1
将时钟分修改标志置1
记录值为2吗?
将时钟小时修改标志置1
记录值为3吗?
时钟单元修改位置记录值清零
开定时器
返回
图5.2时钟设置函数流
4.1.2主程序函数main<)
main
定时器、中断初始化
时钟显示单元首地址初始化
有键按下吗?
调用键盘检测函数
N
调用LED显示函数延时去抖
调用LED显示函数
是抖动吗?
YY
闹钟设定否?
N
N
调用查键值函数
Y
调用闹钟判断启动函数
是时钟修改键吗?
Y
调用时钟设置函数
N
调用闹钟设置函数
是闹钟修改键吗?
NY
N
是加1功能键吗?
Y
调用加1修改功能函数
N
图5.3主程序函数main流程
4.1.3闹钟设置函数ftion1
ftion1
禁止时钟修改
将闹钟秒单元设为显示首地址
闹钟修改记录值加1
将闹钟分单元修改
标志位置1
记录值为1吗?
将闹钟小时单元修改
标志位置1
记录值为2吗?
将闹钟已设定标志位置1
闹钟修改记录值清零
恢复时钟显示单元首地址
返回
图5.4闹钟设置函数流程
返回
4.1.4闹钟判断启动函数alarm
闹钟值与计时值时、
分单元相等否?
启动闹钟标志置位
Y
闹钟10s计时到否?
N
N
停闹、清楚闹钟标志和10s计时标志
返回
图5.5闹钟判断启动函数流程
返回
4.1.5加1修改功能函数cum
秒单元内容加1,十进制调整
记录值为2吗?
到60s否?
清零
记录值为2吗?
分单元内容加1,十进制调整
到60min否?
清零
小时单元内容加1,十进制调整
记录值为2吗?
到24h否?
清零
记录值为2吗?
分单元内容加1,十进制调整
到60min否?
清零
记录值为2吗?
秒单元内容加1,十进制调整
到24h否?
清零
返回
图5.6加1修改功能函数流程
4.1.6定时器中断函数clock
时单元加1
时单元加1
时单元加1
250um中断服务开始
关中断
满40次<10ms)否?
250us次数清零
1s到否?
10ms次数清零
如果已启闹铃则闹铃时间变量加1
满40次<10ms)否?
秒单元清零
时单元加1
分单元加1
时单元清零
分单元清零
24h到否?
1h到否?
中断返回
开中断
N
NY
Y
N
NY
Y
N
Y
Y
图5.7定时器中断函数流程
4.2系统源程序
/***************数字钟程序***************/
#include
#defineucharunsignedchar
sbitbuzzer=P1^0。
sbitkey1=P3^2。
sbitkey2=P3^3。
sbitkey3=P3^4。
sbitkey4=P3^5。
/**************函数声明*************/
voiddisplay(uchar*p>。
ucharkeyscan(>。
//扫描键盘有无键按下
voidalarm(>。
//闹钟判断启动
voidftion0(>。
//时钟修改
voidftion1(>。
//闹钟修改
voidcum1(>。
//加1修改
voidcum2(>。
/***************全局变量定义****************/
ucharclockbuf[3]={0,0,0}。
//存放时钟时分秒的十进制数
ucharbellbuf[3]={0,0,0}。
//存放闹钟时分秒的十进制数
ucharmsec1。
//10ms中断次数
ucharmsec2。
//1s循环次数
uchartimdata,rtimdata。
//时钟和闹钟修改位置标志
ucharcount。
//闹钟启动后10s计时单元
uchar*dis_p。
//显示缓冲区指针
bitarmbit。
//闹钟标志,为0闹钟未设定,为1已设定
bitrtimbit。
//闹钟是否启动标志,为1已启动
bitrhourbit。
//闹钟小时修改标志,为1正在修改闹钟小时
bitrminbit。
//闹钟分修改标志,为1正在修改闹钟分
bithourbit。
//时钟小时修改标志,为1正在修改时钟小时
bitminbit。
//时钟分修改标志,为1正在修改时钟分
bitsecbit。
//时钟秒修改标志,为1正在修改时钟秒
/****************主函数****************/
voidmain(>
{
uchara。
armbit=0。
//清零闹钟标志位
msec1=0。
//设置10ms中断次数初值
msec2=0。
//设置1s中断次数初值
timdata=0。
//时钟内容修改位置记忆单元清零
rtimdata=0。
//闹钟内容修改位置记忆单元清零
count=0。
//闹钟启动后保持10s计时单元清零
TMOD=0x02。
//定时器T0为工作方式2
TL0=0x06。
//定时初始值为250us
TH0=0x06。
EA=1。
//中断总允许位开启
ET0=1。
//定时器T0开中断
TR0=1。
//启动定时器T0
dis_p=clockbuf。
//将时钟值所在地址送入显示指针
while(1>
{
a=keyscan(>。
//调用按键查询函数
switch(a>{
case0:
if(armbit==1>alarm(>。
display(dis_p>。
break。
case1:
ftion0(>。
break。
//是时钟参数修改功能键,调用时钟设置子函数
case2:
ftion1(>。
break。
//是闹钟参数修改功能键,调用闹钟设置子函数
case3:
cum1(>。
break。
//是加1功能键,调用加1修改功能子函数
case4:
cum2(>。
break。
default:
break。
}
}
}
/***************6位LED显示函数***************/
voiddisplay(uchar*p>
{
ucharbuffer[6]。
uchark,I,j,m,temp。
ucharled[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}。
//0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f
buffer[0]=p[0]/10。
buffer[1]=p[0]%10。
buffer[2]=p[1]/10。
buffer[3]=p[1]%10。
buffer[4]=p[2]/10。
buffer[5]=p[2]%10。
for(k=0。
k<2。
k++>
{temp=0x01。
for(i=0。
i<6。
i++>
{
P0=0xff。
j=buffer[i]。
P0=led[j]。
P2=~temp。
temp<<=1。
for(m=0。
m<100。
m++>。
//每一位显示延时
}
}
}/**************按键查询函数*************/
ucharkeyscan(>
{
uchare=0。
if(key1==0||key2==0||key3==0||key4==0>
{
display(dis_p>。
if(key1==0>e=1。
elseif(key2==0>e=2。
elseif(key3==0>e=3。
elseif(key4==0>e=4。
}
while(!
(key1&&key2&&key3&&key4>>display(dis_p>。
return(e>。
}
/***********闹钟判断启动函数*************/
voidalarm(>
{
if((clockbuf[0]==bellbuf[0]>&&
(clockbuf[1]==bellbuf[1]>>
{buzzer=0。
rtimbit=1。
}//设置闹钟计时标志,时钟将进行10s计时标志
if(count==10>//判断闹钟保持10s时间到否
{
count=0。
//清除闹钟保持10s计时
buzzer=1。
//清除闹钟
armbit=0。
//清闹钟标志,否则闹钟设置将继续有效
rtimbit=0。
}
}
/*************时钟设置函数*************/
voidftion0(>
{
if(rhourbit==1||rtimbit==1>
{secbit=0。
minbit=0。
hourbit=0。
timdata=0。
}
//禁止闹钟时间参数修改,清闹钟修改标志
else{
TR0=0。
//关定时器
dis_p=clockbuf。
//将时钟缓冲区首地址送显示指针
timdata++。
//将时钟修改记录值加1
switch(timdata>{
case0x01:
secbit=1。
break。
//记录值为1,则将时钟秒修改标志置1
case0x02:
secbit=0。
minbit=1。
break。
//记录值为2,则将时钟分修改标志置1
case0x03:
minbit=0。
hourbit=1。
break。
//记录值3,则将时钟时修改标志置1
case0x04:
timdata=0。
hourbit=0。
TR0=1。
break。
//按4次则清时钟单元修改位置
//记录,定时器重新开启
default:
break。
}
}
}
/***************闹钟设置函数**************/
voidftion1(>
{
if(secbit==1||minbit==1||hourbit==1>
{rhourbit=0。
rminbit=0。
rtimdata=0。
}//禁止时钟时间修改
else
{
dis_p=bellbuf。
//设置闹钟显示标志
rtimdata++。
//将闹钟修改记录值加1
switch(rtimdata>{
case0x01:
rminbit=1。
break。
//记录值为1,将闹钟分修改标志置1
case0x02:
rminbit=0。
rhourbit=1。
break。
//记录值为2,将时钟分修改标志置1
case0x03:
rtimdata=0。
rhourbit=0。
//按3次则清闹钟单元修改位置记录
armbit=1。
//设置闹钟已设置标志位
dis_p=clockbuf。
//恢复时钟显示标志
break。
default:
break。
}
}
}
/*************加1修改功能函数**************/
voidcum1(>
{
if(secbit==1>{//时钟秒修改标志为1,秒单元内容加1
if(clockbuf[2]==59>clockbuf[2]=0。
elseclockbuf[2]++。
}
elseif(minbit==1>{//时钟分修改标志为1,分单元内容加1
if(clockbuf[1]==59>clockbuf[1]=0。
elseclockbuf[1]++。
}
elseif(hourbit==1>{//时钟小时修改标志为1,小时单元内容加1
if(clockbuf[0]==23>clockbuf[0]=0。
elseclockbuf[0]++。
}
e