基于51单片机的时钟屏应用<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
⒈ 时钟屏简介
时钟屏是一块具有四位七段数码管(分别对应时、分)和秒点(有些还有上下午显示)的模块。本人使用的时钟屏上有30只发光二极管,如下图所示。
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
它是共阴的,其中:1A~1G为第一位(可以为小时十位),2A~2G为第二位(可以为小时个位),3A~3G为第三位(可以为分钟十位),4A~4G为第四位(可以为分钟个位),LC可以为秒点。
⒉ 硬件设计
时钟可以采用纯软件实现,也可以采用硬件时钟。软件时钟必须在每次上电时进行时间的初始化(包括校时);而硬件时钟一般使用专用的时钟芯片,它有自己的蓄电池,因此掉电后它还会继续走时,系统上电后时间仍然保证是正确的。
本应用采用PCF8563时钟芯片,51单片机采用AT89S52芯片,其详细的电原理图如下所示。
⒊ 软件设计
每隔一分钟读取一次PCF8563中的日期时间值并屏蔽无效位,四位时间(小时和分钟)加秒点或日期(月和日), 显示子函数中的LED数码管采用动态显示,每位5ms,周期为20~25ms。为了使看到的显示不闪烁,必须经常执行显示子函数。按键S2用于调整小时(或月)的值;按键S3用于调整分钟(或日)的值;按键S4用于使显示的时间改为日期。T0定时器用于产生25ms中断,在中断服务子函数中,使控制秒点闪烁的亮灭标志以一秒为周期变化;另外计时一分钟则使分钟标志为1。本应用详细的流程图如下所示。
基于51单片机的时钟屏应用详细C51程序如下所示。
/***************
PCF8563+时钟屏
****************/
#include "reg51.h"
#include<intrins.h> //包含_nop_()函数定义的头文件
#define uint unsigned int
#define uchar unsigned char
uchar code ddata[]={
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
}; //0,1,2,3....9的段码
sbit SS="P2"^0; //时十位
sbit SG="P2"^1; //时个位
sbit FS="P2"^2; //分十位
sbit FG="P2"^3; //分个位
sbit M="P2"^4; //秒
sbit S2=P2^5; //时或月+1键
sbit S3=P2^6; //分或日+1键
sbit S4=P2^7; //日期键
uchar min,hour,date,month; //分,时,日,月,
uchar int_c=0,sec_c=0,min_f,date_f;//中断计数,秒计数,分标志,日期标志
uchar time[7]; //日期时间缓存
uchar light_wink; //亮灭
uchar date_c=0; //日期显示计时器
void delay_1ms(uint x) //延时xms
{
uint j;
uchar k;
for(j=0;j<x;j++)
{
for(k=0;k<120;k++);
}
}
/**********以下为8563的读写子函数*************/
sbit SDA="P1"^6;
sbit SCL="P1"^7;
bdata char com_data;
sbit mos_bit=com_data^7;
sbit low_bit=com_data^0;
void start() //启动读写时序
{
SDA=1;SCL=1;_nop_();_nop_();
SDA=0;_nop_();_nop_();
SCL=0;
}
void stop() //停止操作
{
SDA=0;SCL=1;
_nop_();_nop_();
SDA=1;_nop_();_nop_();
SDA=0;SCL=0;
}
void ack() //应答
{
SDA=0;SCL=1;
_nop_();_nop_();
SCL=0;
SDA=1;
}
void shift8(char a) //8位移位输出
{
data uchar i;
com_data=a;
for(i=0;i<8;i++)
{
SDA=mos_bit;
SCL=1;_nop_();_nop_();
SCL=0;SDA=0;
com_data=com_data*2;
}
}
rd_8563(char a) //读8563中a地址单元连续的5个数据
{
data uchar i,j,command;
SDA=1;SCL=0;_nop_();_nop_();
start();
command=162;
shift8(command);
ack();
shift8(a);
ack();
start();
command=163;
shift8(command);
ack();
SDA=1;_nop_();_nop_();_nop_();_nop_();
for(j=0;j<5;j++)
{
for(i=0;i<8;i++)
{
com_data=com_data*2;
SDA=1;SCL=1;_nop_();_nop_();_nop_();_nop_();
low_bit=SDA;
SCL=0;_nop_();_nop_();
}
time[j]=com_data;
ack();
}
stop();
}
void wr_8563(char a,char b)//将b地址单元的数据写入8563的a地址单元中
{
data uchar command;
start();
command=162;
shift8(command);
ack();
shift8(a);
ack();
shift8(b);
ack();
stop();
}
time_display(void)
{
uchar i;
for(i=1;i<6;i++)
{
switch(i)
{
case 1:{P2=0xfe;if(((hour&0xf0)>>4)==0)P0=0xff;
else P0=ddata[(hour&0xf0)>>4];break;}//显示小时十位
case 2:P2=0xfd;P0=ddata[hour&0x0f];break; //显示小时个位
case 3:P2=0xfb;P0=ddata[(min&0xf0)>>4];break; //显示分钟十位
case 4:P2=0xf7;P0=ddata[min&0x0f];break; //显示分钟个位
case 5:if(light_wink)
{
P2=0xef;P0=0;
};break;
}
delay_1ms(5);
P2=0xff;
}
}
date_display()
{
uchar i;
for(i=1;i<5;i++)
{
switch(i)
{
case 1:{P2=0xfe;if(((month&0xf0)>>4)==0)P0=0xff;
else P0=ddata[(month&0xf0)>>4];break;} //显示月十位
case 2:P2=0xfd;P0=ddata[month&0x0f];break; //显示月个位
case 3:P2=0xfb;P0=ddata[(date&0xf0)>>4];break; //显示日十位
case 4:P2=0xf7;P0=ddata[date&0x0f];break; //显示日个位
}
delay_1ms(5);
P2=0xff;
}
}
main()
{
TMOD=0X01;
TH0=0X9E; //25ms
TL0=0X58;
TR0=1;ET0=1;EA=1;
rd_8563(0x03); //读日期时间
min=time[0]&0x7f; //屏蔽无效位
hour=time[1]&0x3f;
date=time[2]&0x3f;
month=time[4]&0x1f;
while(1)
{
if(date_f)
date_display(); //显示日期
else
time_display(); //显示时间
if(min_f)
{ //每分钟读取一次日期时间并屏蔽无效位
min_f=0;
rd_8563(0x03);
min=time[0]&0x7f;
hour=time[1]&0x3f;
date=time[2]&0x3f;
month=time[4]&0x1f;
}
if (S2==0)
{ //月或时+1
time_display(); //延时25ms
if(S2)
goto loop;
if(date_f)
{ //月+1
if(((month&0x0f)+1)>9)
month=(month&0xf0)+0x10;
else
month=month+1;
if(month>0x12)
month=0;
wr_8563(0x07,month); //写入月
date_c=0;
}
else
{
if(((hour&0x0f)+1)>9) //时+1
hour=(hour&0xf0)+0x10;
else
hour=hour+1;
if(hour>0x23)
hour=0;
wr_8563(0x04,hour); //写入时
}
for(;;)
if(S2)
goto loop;
if(date_f)
date_display(); //显示日期
else
time_display(); //显示时间
}
loop:if(S3==0)
{ //日或分+1键
time_display(); //延时25ms
if(S3)
goto loop;
if(date_f)
{ //日+1
if(((date&0x0f)+1)>9)
date=(date&0xf0)+0x10;
else
date=date+1;
if(date>0x31)
date=0;
wr_8563(0x05,date); //写入月
date_c=0;
}
else
{
if(((min&0x0f)+1)>9) //分+1
min=(min&0xf0)+0x10;
min=min+1;
if(min>0x59)
min=0;
wr_8563(0x03,min); //写入分
}
for(;;)
if(S3)
goto loop;
if(date_f)
date_display(); //显示日期
else
time_display(); //显示时间
}
if(S4==0)
{ //日期显示
time_display(); //延时25ms
if(S4)
goto loop;
date_display();
date_c=0;
date_f=1;
for(;;)
if(S4)
goto loop;
if(date_f)
date_display(); //显示日期
else
time_display(); //显示时间
}
}
}
void T0_int(void) interrupt 1
{
TH0=0X9E;TL0=0X58;
int_c++;
if(int_c==20)
light_wink=1;
if(int_c==40)
{ //一秒
int_c=0x00;
sec_c++;
light_wink=0;
date_c++;
if(date_c==10)
date_f=0;
if(sec_c==60)
{ //一分钟
sec_c=0;
min_f=1;
}
}
}
以使用MedWin软件为例 ,启动MedWin V3.0,执行菜单命令“项目管理”→“新建项目”创建项目(或利用原有项目),然后再执行菜单命令“项目管理”→“新建文件”,在编辑窗口中输入上述源程序,经编译无误后,在构建窗口中将显示无错误。注意:需执行菜单命令“设置”→“设置编译工具”→点选“指定路径下的编译工具”(该软件默认的是汇编语言),将C51文件夹放在适当的路径(可利用Keil中的C51)。
⒋ 仿真调试
本人使用的硬件仿真器是万利公司的“ME-52HU”,将仿真头插入目标板中的CPU插座,连通仿真器与PC的USB线,先后接通目标板和仿真器的电源,点击快捷按钮“产生代码并装入”,将出现十几个调试的快捷按钮,此时可以点击某个快捷按钮进行仿真调试。当排除了程序中所有错误后,点击“全速运行”按钮时,将会在时钟屏上看到正确的显示,如下图所示。
本人的邮箱:txxyc104@163.com,欢迎来信讨论.
文章评论(0条评论)
登录后参与讨论