基于STC89C52的时钟系统.docx
《基于STC89C52的时钟系统.docx》由会员分享,可在线阅读,更多相关《基于STC89C52的时钟系统.docx(24页珍藏版)》请在冰豆网上搜索。
基于STC89C52的时钟系统
基于STC89C52的时钟系统
摘要
结合锐志RZ-51V2.0学习时钟设计的要求,把单片机中数字时钟的设计作为一个项目,在达到熟练使用相应软件的前提下,通过仿真器进行硬件仿真,以完成一个程序完整的设计过程。
并作出相应的结论。
利用单片机进行设计等,且各有特点。
其中利用单片机
实现时钟的设计方法,具有电路简单、编程灵活、便于扩展、精确度高、稳定性好等优点,
关键词:
单片机;数字时钟;设计;仿真
1.引言………………………………………………………………………………………3
2.设计要求…………………………………………………………………………………3
3.电路模块……………………………………………………………………………………3
3.1单片机模块………………………………………………………………………3
3.2DS1302数字芯片模块……………………………………………………3
3.3数码管显示模块…………………………………………………………………4
4.软件程序工作原理…………………………………………………………………………4
4.1时钟设计部分程序…………………………………………………………
5.程序代码…………………………………………………………………………………6
结论…………………………………………………………………………………………20
参考文献……………………………………………………………………………………21
1、引言
现代电子系统的基本核心是单片机,而单片机的应用能使爱好者既动脑、又动手地进行软件设计和硬件制作,再加上单片机原理与应用课程是目前职业学校电类专业的主干课之一,其操作性很强,利用AT89C51单片机,实现断电自动保护显示数据的功能
2、设计要求
1.时间显示在1602液晶上,并且按秒实时更新。
2.能够使用板上的按键随时调节时钟的时、分、秒,按键可设计三个有效键,分别为功能选择键,数值增大键和数值减小键。
3.每次有键按下时,蜂鸣器都以短“滴”声报警。
4.利用板上AT24C08设计实现断电自动保护显示数据的功能,当下次上电时会接着上次断电前的时间数据继续运行。
5.扩展显示年、月、日、星期功能。
3、电路模块
3.1单片机模块
本温度控制系统的核心部分即单片机模块采用STC89C52RC单片机,该单片机指令代码完全兼容传统8051单片机。
STC89C52单片机的工作电压为5.5V-3.4V,工作频率范围0-80MHz,程序存储器flash容量为8KB,随机存储器RAM空间为512字节,完全满足设计温度控制系统的要求。
3、2DS1302数字芯片模块
DS1302是美国DALLAS公司推出的一种高性能、低功耗的实时时钟芯片,附加31字节静态RAM,采用SPI三线接口与CPU进行通信,并可采用突发方式一次传送多个字节的时钟信号和RAM数据。
实时时钟可提供秒、分、时、日、星期、月和年,一个月小与31天时可以自动调整,且具有闰年补偿功能。
工作电压宽达2.5~5.5V。
采用双电源供电(主电源和备用电源),可设置备用电源充电方式,提供了对后备电源进行涓细电流充电的能力。
3、3数码显示模块
本温度控制系统选用的显示部分由7段LED数码管并列组成动态驱动是将所有数码管的8个显示笔划"a,b,c,d,e,f,g,dp"的同名端连在一起,另外为每个数码管的公共极COM增加位选通控制电路,位选通由各自独立的I/O线控制,当单片机输出字形码时,所有数码管都接收到相同的字形码,但究竟是那个数码管会显示出字形,取决于单片机对位选通COM端电路的控制,所以我们只要将需要显示的数码管的选通控制打开,该位就显示出字形,没有选通的数码管就不会亮。
通过分时轮流控制各个数码管的的COM端,就使各个数码管轮流受控显示,这就是动态驱动。
在轮流显示过程中,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据。
4、软件程序设计原理
把数字时钟设计作为一个项目时,应该对单片机应用系统的开发过程有一个比较深入的了解。
整个开发过程包括三个部分:
总体设计;硬件设计调试;软件设计调试。
软件设计调试由流程图(先粗后细),分配I/O,内存,编程,编辑,汇编,仿真调试等7部分组成。
在教学中可以先通过几个简单程序的编写,熟悉程序的设计过程。
第一个程序设计要求在仿真器的个位上显示“5”字,程序
如下:
movp2,#0efh;显示位数
movp0,#0a7h;显示“5”
sjmp$
end
第二个程序:
让“F”循环移位,每秒移位一位,熟悉延时子程序的应用。
loop:
movp2,#0efhdelay:
movr3,#0ffh
movp0,#047hdelay1:
movr4,#0ffh
acalldelaydelay2:
nop
movp2,#0dfhnop
movp0,#047hnop
acalldelaydjnzr4,delay2
:
djnzr3,delay1
:
ret
延时子程序如右方所示end
完整程序见网址
通过以上程序的设计,使设计者对软件调试过程有了一定的了解后。
下一步进行数字时钟的设计。
此软件程序由主程序、动态显示子程序、定时器中断服务程序和延时子程序组成。
系统初始化后,启动T0,定时时间为50ms,然后累加20次的方法定时1s,计满60s就让分单元加1,秒单元清零,循环往复,直至显示59min59s后全部清零,重新开始计数。
笔者用四位数码管个位,十位代表秒显示,从零开始计数,百位,千位代表分显示,十位和百位中间用小数点闪烁显示。
下面是一个完整的数字时钟显示程序框图。
根据框图设计一个完整的数字时钟显示程序,然后在KeilC51环境中,进行一个项目的建立,输入源文件。
源文件程序可登陆下载。
调试软件
5、程序代码
//按4X4键盘的E键进入设定状态
//D键是减设定键
//C按键加设定键
#include"reg52.h"
#include//包含随机函数rand()的定义文件
#include//包含_nop_()函数定义的头文件
#defineOP_READ0xa1//器件地址以及读取操作,0xa1即为10100001B
#defineucharunsignedchar
#defineOP_WRITE0xa0//器件地址以及写入操作,0xa1即为10100000B
sbitSDA=P3^5;//将串行数据总线SDA位定义在为P3.5引脚
sbitSCL=P3^4;//将串行时钟总线SDA位定义在为P3.4引脚
sbitRW=P2^1;
sbitRS=P2^0;
sbitE=P2^2;
sbitBEEP=P3^6;
bitat=0;
uchardisplay_buffer1[16];//显示缓冲区
ucharcodeshen[]={"Hello!
!
"};
ucharcodeword[]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x20};
uchardispbuf[8],h,m,s,counter,counter1=0,y,mo,d,t=1;
/*****************************************************
函数功能:
延时1ms
(3j+2)*i=(3×33+2)×10=1010(微秒),可以认为是1毫秒
***************************************************/
voiddelay1ms()
{
unsignedchari,j;
for(i=0;i<10;i++)
for(j=0;j<33;j++)
;
}
/*****************************************************
函数功能:
延时若干毫秒
入口参数:
n
***************************************************/
voiddelaynms(unsignedcharn)
{
unsignedchari;
for(i=0;idelay1ms();
}
/*********************************************************/
voidbeep()//蜂鸣器响一声函数
{
unsignedchari;
for(i=0;i<25;i++)
{
delaynms
(2);
BEEP=!
BEEP;//BEEP取反
}
BEEP=1;//关闭蜂鸣器
delaynms(100);//延时
}
voidstart()
//开始位
{
SDA=1;//SDA初始化为高电平“1”
SCL=1;//开始数据传送时,要求SCL为高电平“1”
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
SDA=0;//SDA的下降沿被认为是开始信号
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
SCL=0;//SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递)
}
/***************************************************
函数功能:
结束数据传送
***************************************************/
voidstop()
//停止位
{
SDA=0;//SDA初始化为低电平“0”_n
SCL=1;//结束数据传送时,要求SCL为高电平“1”
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
SDA=1;//SDA的上升沿被认为是结束信号
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
SDA=0;
SCL=0;
}
/***************************************************
函数功能:
从AT24Cxx读取数据
出口参数:
x
***************************************************/
unsignedcharReadData()
//从AT24Cxx移入数据到MCU
{
unsignedchari;
unsignedcharx;//储存从AT24Cxx中读出的数据
for(i=0;i<8;i++)
{
SCL=1;//SCL置为高电平
x<<=1;//将x中的各二进位向左移一位
x|=(unsignedchar)SDA;//将SDA上的数据通过按位“或“运算存入x中
SCL=0;//在SCL的下降沿读出数据
}
return(x);//将读取的数据返回
}
/***************************************************
函数功能:
向AT24Cxx的当前地址写入数据
入口参数:
y(储存待写入的数据)
***************************************************/
//在调用此数据写入函数前需首先调用开始函数start(),所以SCL=0
bitWriteCurrent(unsignedchary)
{
unsignedchari;
bitack_bit;//储存应答位
for(i=0;i<8;i++)//循环移入8个位
{
SDA=(bit)(y&0x80);//通过按位“与”运算将最高位数据送到S
//因为传送时高位在前,低位在后
_nop_();//等待一个机器周期
SCL=1;//在SCL的上升沿将数据写入AT24Cxx
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
SCL=0;//将SCL重新置为低电平,以在SCL线形成传送数据所需的8个脉冲
y<<=1;//将y中的各二进位向左移一位
}
SDA=1;//发送设备(主机)应在时钟脉冲的高电平期间(SCL=1)释放SDA线,
//以让SDA线转由接收设备(AT24Cxx)控制
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
SCL=1;//根据上述规定,SCL应为高电平
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
_nop_();//等待一个机器周期
ack_bit=SDA;//接受设备(AT24Cxx)向SDA送低电平,表示已经接收到一个字节
//若送高电平,表示没有接收到,传送异常
SCL=0;//SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递)
returnack_bit;//返回AT24Cxx应答位
}
/***************************************************
函数功能:
向AT24Cxx中的指定地址写入数据
入口参数:
add(储存指定的地址);dat(储存待写入的数据)
***************************************************/
voidWriteSet(unsignedcharadd,unsignedchardat)
//在指定地址addr处写入数据WriteCurrent
{
start();//开始数据传递
WriteCurrent(OP_WRITE);//选择要操作的AT24Cxx芯片,并告知要对其写入数据
WriteCurrent(add);//写入指定地址
WriteCurrent(dat);//向当前地址(上面指定的地址)写入数据
stop();//停止数据传递
delaynms(4);//1个字节的写入周期为1ms,最好延时1ms以上
}
/***************************************************
函数功能:
从AT24Cxx中的当前地址读取数据
出口参数:
x(储存读出的数据)
***************************************************/
unsignedcharReadCurrent()
{
unsignedcharx;
start();//开始数据传递
WriteCurrent(OP_READ);//选择要操作的AT24Cxx芯片,并告知要读其数据
x=ReadData();//将读取的数据存入x
stop();//停止数据传递
returnx;//返回读取的数据
}
/***************************************************
函数功能:
从AT24Cxx中的指定地址读取数据
入口参数:
set_addr
出口参数:
x
***************************************************/
unsignedcharReadSet(unsignedcharset_addr)
//在指定地址读取
{
start();//开始数据传递
WriteCurrent(OP_WRITE);//选择要操作的AT24Cxx芯片,并告知要对其写入数据
WriteCurrent(set_addr);//写入指定地址
return(ReadCurrent());//从指定地址读出数据并返回
}
voiddelay()
{
uchari;
for(i=0;i<255;i++);
}
/*******写命令**********/
voidlcd_wmc(uchari)
{
P0=i;
RS=0;
RW=0;
E=0;
delay();
E=1;
}
/*******写数据***********/
voidlcd_wmd(uchari)
{
P0=i;
RS=1;
RW=0;
E=0;
delay();
E=1;
}
/*******初始化液晶*******/
voidlcd_init()
{
uchari;
lcd_wmc(0x01);//清屏幕指令,将以前的显示内容清除
lcd_wmc(0x38);//显示模式设置:
16×2显示,5×7点阵,8位数据接口
lcd_wmc(0x0c);
lcd_wmc(0x06);//显示模式设置:
光标右移,字符不移
lcd_wmc(0xc9);
for(i=0;i<7;i++)
lcd_wmd(shen[i]);
lcd_wmc(0xc0);
}
/*******更新缓冲区子程序*******/
voidnewbuf()
{uchartmp2;
WriteSet(0x33,s);
WriteSet(0x34,m);
WriteSet(0x35,h);
WriteSet(0x36,y);
WriteSet(0x37,mo);
WriteSet(0x38,d);
WriteSet(0x39,t);
display_buffer1[0]='2';
display_buffer1[1]='0';
display_buffer1[2]=y/10;//+0x30;
display_buffer1[3]=y%10;//+0x30;
display_buffer1[4]='_';
display_buffer1[5]=mo/10;//+0x30;
display_buffer1[6]=mo%10;//+0x30;
display_buffer1[7]='_';
display_buffer1[8]=d/10;//+0x30;
display_buffer1[9]=d%10;//+0x30;
display_buffer1[10]='';
display_buffer1[11]='';
tmp2=display_buffer1[2];
tmp2=word[tmp2];
display_buffer1[2]=tmp2;
tmp2=display_buffer1[3];
tmp2=word[tmp2];
display_buffer1[3]=tmp2;
tmp2=display_buffer1[5];
tmp2=word[tmp2];
display_buffer1[5]=tmp2;
tmp2=display_buffer1[6];
tmp2=word[tmp2];
display_buffer1[6]=tmp2;
tmp2=display_buffer1[8];
tmp2=word[tmp2];
display_buffer1[8]=tmp2;
tmp2=display_buffer1[9];
tmp2=word[tmp2];
display_buffer1[9]=tmp2;
switch(t%16)
{
case1:
{display_buffer1[12]='M';
display_buffer1[13]='o';
display_buffer1[14]='n';}break;
case2:
{display_buffer1[12]='T';
display_buffer1[13]='u';
display_buffer1[14]='e';}break;
case3:
{display_buffer1[12]='W';
display_buffer1[13]='e';
display_buffer1[14]='d';}break;
case4:
{display_buffer1[12]='T';
display_buffer1[13]='h';
display_buffer1[14]='u';}break;
case5:
{display_buffer1[12]='F';
display_buffer1[13]='r';
display_buffer1[14]='i';}break;
case6:
{display_buffer1[12]='S';
display_buffer1[13]='a';
display_buffer1[14]='t';}break;
case7:
{display_buffer1[12]='S';
display_buffer1[13]='u';
display_buffer1[14]='n';}break;
default:
break;
}
dispbuf[0]=s%10;
dispbuf[1]=s/10;
dispbuf[3]=m%10;
dispbuf[4]=m/10;
dispbuf[6]=h%10;
dispbuf[7]=h/10;
}
/*******显示子程序**********/
voiddisp1(uchardispadd)
{
uchari=0;
lcd_wmc(dispadd