A. 基于单片机的数字时钟怎么做
#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int
sbit la=P2^6;
sbit wela=P2^7;
sbit rs=P3^5;
sbit lcden=P3^4;
sbit s1=P3^0;
sbit s2=P3^1;
sbit s3=P3^2;
sbit rd=P3^7;
uchar count,s1num;
char miao,shi,fen;
uchar code table[]=" 2007-7-30 MON";
uchar code table1[]=" 00:00:00";
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void write_com(uchar com)
{
rs=0;
lcden=0;
P0=com;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void write_date(uchar date)
{
rs=1;
lcden=0;
P0=date;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void init()
{
uchar num;
la=0;
wela=0;
lcden=0;
// fen=59;
// miao=53;
// shi=23;
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
write_com(0x80);
for(num=0;num<15;num++)
{
write_date(table[num]);
delay(5);
}
write_com(0x80+0x40);
for(num=0;num<12;num++)
{
write_date(table1[num]);
delay(5);
}
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;
ET0=1;
TR0=1;
}
void write_sfm(uchar add,uchar date)
{
uchar shi,ge;
shi=date/10;
ge=date%10;
write_com(0x80+0x40+add);
write_date(0x30+shi);
write_date(0x30+ge);
}
void keyscan()
{
rd=0;
if(s1==0)
{
delay(5);
if(s1==0)
{ s1num++;
while(!s1);
if(s1num==1)
{
TR0=0;
write_com(0x80+0x40+10);
write_com(0x0f);
}
}
if(s1num==2)
{
write_com(0x80+0x40+7);
}
if(s1num==3)
{
write_com(0x80+0x40+4);
}
if(s1num==4)
{
s1num=0;
write_com(0x0c);
TR0=1;
}
}
if(s1num!=0)
{
if(s2==0)
{
delay(5);
if(s2==0)
{
while(!s2);
if(s1num==1)
{
miao++;
if(miao==60)
miao=0;
write_sfm(10,miao);
write_com(0x80+0x40+10);
}
if(s1num==2)
{
fen++;
if(fen==60)
fen=0;
write_sfm(7,fen);
write_com(0x80+0x40+7);
}
if(s1num==3)
{
shi++;
if(shi==24)
shi=0;
write_sfm(4,shi);
write_com(0x80+0x40+4);
}
}
}
if(s3==0)
{
delay(5);
if(s3==0)
{
while(!s3);
if(s1num==1)
{
/* if(miao==0)
{
miao=59;
write_sfm(10,miao);
write_com(0x80+0x40+10);
}*/
miao--;
if(miao==-1)
miao=59;
write_sfm(10,miao);
write_com(0x80+0x40+10);
}
if(s1num==2)
{
fen--;
if(fen==-1)
fen=59;
write_sfm(7,fen);
write_com(0x80+0x40+7);
}
if(s1num==3)
{
shi--;
if(shi==-1)
shi=23;
write_sfm(4,shi);
write_com(0x80+0x40+4);
}
}
}
}
}
void main()
{
init();
while(1)
{
keyscan();
}
// while(1);
}
void timer0() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
count++;
if(count==18)
{
count=0;
miao++;
if(miao==60)
{
miao=0;
fen++;
if(fen==60)
{
fen=0;
shi++;
if(shi==24)
{
shi=0;
}
write_sfm(4,shi);
}
write_sfm(7,fen);
}
write_sfm(10,miao);
}
}
这个是数字时钟的源程序,用12864夜晶显示。
B. 单片机数字时钟原理
给你个程序看看,主要是看时分显示哪里!这个程序已经调试通过了,在走时的同时流水灯进行流动,时分之间有一个小数点作为分隔。还有整点报时功能,在早上八点到中午十二点以及下午三点到晚上八点两个时间段内逢整点报时,其他时间不报时(是因为考虑到人们要午休及晚间休息),除此之外还有调时、调分功能。整个程序基于单片机AT89S52(可用C51、C52、S51等代替)。
#include <reg52.h>
#define uint unsigned int
sbit P3_0=P3^0;
sbit K1=P3^2;
sbit K2=P3^3;
sbit K3=P3^4;
sbit K4=P3^5;
uint count,min,hour,i,j=0;
uint code tab1[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
uint code tab2[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};
uint code tab3[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f,0xff,
0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0xff,0x00,0xff,0x00,0xff,
0xfe,0xfb,0xef,0xbf,0xfd,0xf7,0xdf,0x7f,0x7e,0x3c,0x18,0x00,0x81,
0xc3,0xe7,0xff,0xe7,0xdb,0xbd,0x7e,0xff,0x7e,0xbd,0xdb,0xe7,0xff,
0x00,0xff,0x00,0xff,0xfe,0xfc,0xf8,0xf0,0xe0,0xc0,0x80,0x00,0x80,
0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff,0x00,0xff,0x00,0xff,0xfe,0xfc,
0xf8,0xf0,0xe0,0xc0,0x80,0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,
0xff,0xfc,0xf9,0xf3,0xe7,0xcf,0x9f,0x3f,0xff,0x00,0xff,0x00,0xff};//流水灯
void adjust(void)
{
if(!K3) //分调整
{
for(i=0;i<20000;i++);min++;
if(min==60)min=0;
}
if(!K4) //时调整
{
for(i=0;i<20000;i++);hour++;
if(hour==24)hour=0;
}
}
void display(void)
{
P0=tab1[min%10];P2=0xf7;for(i=0;i<5;i++);P2=0xff;//分个位显示
P0=tab1[min/10];P2=0xfb;for(i=0;i<5;i++);P2=0xff;//分十位显示
P0=tab2[hour%10];P2=0xfd;for(i=0;i<5;i++);P2=0xff;//时个位显示
P0=tab1[hour/10];P2=0xfe;for(i=0;i<5;i++);P2=0xff;//时十位显示
}
void ring(void)
{
if(hour/10==0&&(hour%10>=8&&hour%10<=9))P3_0=0;//早上7:00到晚上7:00自动整点报时,其中13、14点不报时
if(hour/10==1&&(hour%10>=0&&hour%10<=2))P3_0=0;
if(hour/10==1&&(hour%10>=5&&hour%10<=9))P3_0=0;
}
void update(void)
{
if(count==1200)
{
count=0;min++;
if(min==60)
{
min=0;hour++;
if(hour==24)hour=0;
ring();
}
}
}
void main(void)
{
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
TR0=1;
EA=1;
ET0=1;
while(1)
{
if(count==120)P3_0=1;//报时六秒后自动关闭蜂鸣器
adjust();
display();
}
}
void timer0_rupt(void) interrupt 1 // 定时器0中断
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
count++;
if(count%10==0)
{
P1=tab3[j];
j++;
if(j>99)j=0;
}
update();
}
C. 基于单片机多功能数字钟
带万年历的LCD显示多功能数字钟
程序清单:
;SMC1602接口程序(MCS51模拟口线方式)
;***************************************************************************
;连线图:*LCM---8031**LCM---8031**LCM------------8031*
;*DB0---P0.0**DB4---P0.4**RS-------------P2.0*
;*DB1---P0.1**DB5---P0.5**RW-------------P2.1*
;*DB2---P0.2**DB6---P0.6**E--------------P2.2*
;*DB3---P0.3**DB7---P0.7**VLCD接10K可调电阻到GND*
;注:8051的晶振频率为11.0592MHzR0设置数据存放S8为设置键S7为加键S4为确认键
;**************************************************************************
;P0口液晶显示
;P2.4位选键P2.3加一键P2.2返回键及报时功能键闹钟设置P2.0控制显示键
;寄存器clrrs0setbrs1R0R4R5在按键中用到延时中用到SETBRS0SETBRS1的R0R1R2
;SETBRS0CLRRS1显示中用到CLRRS0CLRRS1在闹钟调整子程序中用到R1
;*********************************************************************
;
;报时功能P2.6和闹钟功能P2.5闹钟调整时间功能已经可以设置显示的切换方式变了再关闭的情况下一切正常运行
;*****************************
RSPINBITP1.2
RWPINBITP1.1
EPINBITP1.0
WEIXUANBITP2.4;位选键设置键
JIAJIANBITP2.3;加一键
JIANJIANBITP2.2;减一键
FANHUIBITP2.1;返回键
NZKGBITP2.0;闹钟设置键
SECONTLEQU30H;秒
SECONTHEQU31H;秒
MAOHAOLEQU32H;:
MINIUTLEQU33H;分
MINIUTHEQU34H;分
MAOHAOHEQU35H;:
HOURLEQU36H;时
HOURHEQU37H;时
;*****************************日期部分***************************
DAYLEQU40H;日个
DAYHEQU41H;日十
HENLEQU42H;-
MONTHLEQU43H;yue
MONTHHEQU44H;yue
HENHEQU45H;-
YEAROEQU46H;年
YEARTEQU47H
YEARSEQU48H
YEARFEQU49H
TEMPEQU50H;用做存储单元显示子程序(DISPLAY)里用到
BIJIAOEQU51H;加一子程序(JIAYI)中用到
ZDRQOEQU55H;中断加一程序中用到
ZDRQTEQU56H;中断加一程序中用到
ZDRQSEQU57H;中断加一程序中用到
ZDRQFEQU58H;中断加一程序中用到
DIZHIEQU59H;键盘(JIANPAN)子程序里用到
NZBZBIT60H;定时标志位在(闹钟开关部分)
;38H到3FH没用到
BSCSBZBIT39H;报时次数转换标志(BSCS)里用到
BSCSCCEQU3AH;报时次数存储(BSCS)里用到
ZDBSBZBIT3BH;整点报时标志(SSBS)里用到
ZMBSBZBIT3CH;整秒闪烁标志(SSBS)里用到
BSKGBZBIT3DH;报时开启标志(BSKG)里用到
XKQHBZBIT3EH;显示开启关闭标志(XIANKONG)里用到
ORG0000H
AJMPMAIN
ORG000BH
AJMPZD
ORG0030H
MAIN:MOVSP,#60H;给堆栈指针赋初值
MOV30H,#05H;秒
MOV31H,#05H;秒
MOV32H,#3AH;:
MOV33H,#09H;分
MOV34H,#05H;分
MOV35H,#3AH;:
MOV36H,#03H;时
MOV37H,#01H;时
;*****************************日期部分***************************
MOV40H,#09H;日个
MOV41H,#02H;日十
MOV42H,#2DH;-
MOV43H,#09H;yue
MOV44H,#00H;yue
MOV45H,#2DH;-
MOV46H,#09H;年
MOV47H,#09H
MOV48H,#09H
MOV49H,#02H
CLRP2.5;闹钟
CLRP2.6;整点报时
MOV03H,#00H;闹钟分个位
MOV04H,#00H;闹钟分十位
MOV06H,#04H;闹钟时个位
MOV07H,#01H;闹钟时十位
;CLRP1.5
;CLRP1.4
MOVB,#20
MOVTMOD,#01H;定时器工作方式1
MOVTH0,#4CH
MOVTL0,#08H
SETBET0;允许T0中断
SETBEA;总中断开放
LCALLLCDRESET;初始化LCD
LCALLDISPSTART;调用显示初始状态
SETBTR0;开启定时器
CLRNZBZ;闹钟开启关闭标志
CLRBSKGBZ;报时开启关闭标志
CLRXKQHBZ;显示开启关闭标志
LOOP:LCALLJIANPAN;按键子程序
LCALLDISPLAY;显示子程序
LCALLNZBF;NAOZHONGBUFENG
LCALLNZBJ;NAOZHONGBIJIAO
LCALLBSKG;BAOSHIKAIGUAN
LCALLBSCS;BAOSHICISHU
LCALLSSBS;闪烁报时
SJMPLOOP
;==============================中断加一程序===================================
ZD:PUSHACC
MOVTH0,#4CH
MOVTL0,#08H
DJNZB,ZDEND
MOVB,#20
INCSECONTL
SETBZMBSBZ
MOVA,SECONTL
CJNEA,#0AH,ZDEND
MOVSECONTL,#00H
INCSECONTH
MOVA,SECONTH
CJNEA,#06H,ZDEND
MOVSECONTH,#00H
INCMINIUTL
MOVA,MINIUTL
CJNEA,#0AH,ZDEND
MOVMINIUTL,#00H
INCMINIUTH
MOVA,MINIUTH
CJNEA,#06H,ZDEND
MOVMINIUTH,#00H
INCHOURL
SETBBSCSBZ
SETBZDBSBZ
MOVA,HOURL;24小时的判断
CJNEA,#04H,ZDF
MOVA,HOURH
CJNEA,#02H,ZDEND
MOVHOURL,#00H
MOVHOURH,#00H
LCALLZDRQ
SJMPZDEND
ZDF:CJNEA,#0AH,ZDEND
MOVHOURL,#00H
INCHOURH
ZDEND:POPACC
RETI
;日期部分
ZDRQ:PUSHACC
INCDAYL
;判断天数28,30,31部分
MOVA,MONTHH;用于判断月份时为31天30天28天
CJNEA,#01H,LL
MOV55H,#0AH;存放月份的十位转化为个位是0AH
MOVA,MONTHL
ADDA,55H;月分高低相加用于查表
SJMPLL3
LL:MOVA,MONTHL
ADDA,MONTHH
LL3:MOV56H,A;存放相加后的月份数据
MOVDPTR,#TAB5
MOVCA,@A+DPTR;查十位和天数的十位比较
MOV57H,A
MOVA,56H
MOVDPTR,#TAB6;查个位和天数的个位比较
MOVCA,@A+DPTR
MOV58H,A
MOVA,DAYH
CJNEA,57H,LL2
MOVA,DAYL
CJNEA,58H,LL2
MOVDAYH,#00H
MOVDAYL,#01H
SJMPLL4
LL2:MOVA,DAYL
CJNEA,#0AH,ZDRQEND
MOVDAYL,#00H
INCDAYH
MOVA,DAYH;比较天数是否要进位
CJNEA,57H,ZDRQEND
MOVA,DAYL
CJNEA,58H,ZDRQEND
MOVDAYH,#00H
MOVDAYL,#01H
;月份和年份
LL4:INCMONTHL
MOVA,MONTHL
CJNEA,#03H,ZDRQ1;用于判断月份时为12月时进位
MOVA,MONTHH
CJNEA,#01H,ZDRQ1
MOVMONTHL,#01H;天数符合要求
MOVMONTHH,#00H
SJMPZDRQ2
ZDRQ1:CJNEA,#0AH,ZDRQEND
MOVMONTHL,#00H
INCMONTHH
SJMPZDRQEND
ZDRQ2:INCYEARO;年的加一
MOVA,YEARO
CJNEA,#0AH,ZDRQEND
MOVYEARO,#00H
INCYEART;年的进位
MOVA,YEART
CJNEA,#0AH,ZDRQEND
MOVYEART,#00H
INCYEARS
MOVA,YEARS
CJNEA,#0AH,ZDRQEND
MOVYEARS,#00H
INCYEARF
MOVA,YEARF
CJNEA,#0AH,ZDRQEND
MOVYEARF,#00H
ZDRQEND:
POPACC
RET
;液晶初始化
;========================初始化程序=======================================
LCDRESET:;初始化程序
LCALLDELAY5MS;延时15MS
LCALLDELAY5MS
LCALLDELAY5MS
MOVA,#38H;显示模式设置(不检测忙信号)
LCALLLCDWCN;共三次
LCALLDELAY5MS
MOVA,#38H
LCALLLCDWCN
LCALLDELAY5MS
MOVA,#38H
LCALLLCDWCN
MOVA,#38H;显示模式设置(以后均检测忙信号)
LCALLLCDWC
MOVA,#08H;显示关闭
LCALLLCDWC
MOVA,#01H;显示清屏
LCALLLCDWC
MOVA,#06H;显示光标移动设置
LCALLLCDWC
MOVA,#0CH;显示开及光标设置
LCALLLCDWC
RET
;==============================写指令===================================
LCDWC:;送控制字子程序(检测忙信号)
LCALLWAITIDLE
;******写指令*******;送控制字子程序(不检测忙信号)
LCDWCN:CLRRSPIN;RS=0RW=0E=高脉冲
CLRRWPIN
MOVP0,A
SETBEPIN;(
NOP;给高电平脉冲
CLREPIN;)
RET
;==========================写数据=======================================
LCDWD:;写字符子程序
LCALLWAITIDLE
SETBRSPIN;RS=1RW=0E=高脉冲
CLRRWPIN
MOVP0,A
SETBEPIN
NOP
CLREPIN
RET
;===============================等待控制器空闲==================================
WAITIDLE:
PUSHACC;正常读写操作之前必须检测LCD控制器状态
MOVP0,#0FFH
lcallDELAY5MS;666666666666666666666666
CLRRSPIN;RS=0RW=1E=高电平
SETBRWPIN
SETBEPIN
lcallDELAY5MS;98
WTD_PA:NOP;DB7:0LCD控制器空闲
JBP0.7,WTD_PA;1LCD控制器忙
CLREPIN
POPACC
RET
;***********************初始状态子程序*******************************
DISPSTART:;显示初始状态子程序
PUSHACC
MOVA,#80H
LCALLLCDWC
MOVDPTR,#TAB;显示字符
DISP1:CLRA
MOVCA,@A+DPTR
JZDISP2
LCALLLCDWD
INCDPTR
SJMPDISP1
DISP2:MOVA,#0C0H
LCALLLCDWC
MOVDPTR,#TAB1
DISP3:CLRA
MOVCA,@A+DPTR
JZDISP_END
LCALLLCDWD
INCDPTR
SJMPDISP3
DISP_END:
POPACC
RET
;==========================显示子程序=======================================
DISPLAY:PUSHPSW
PUSHACC
SETBRS0
CLRRS1
MOVA,#85H
LCALLLCDWC
MOVR1,#38H;第一行显示数据存储单元的启始地址
MOVTEMP,#30H;第一行显示数据存储单元的结束地址
MOVR6,#2
PLAY:DECR1
CJNER1,#32H,PLY;判断是否为冒号位
MOVA,MAOHAOL;是者送冒号的ACSI码
SJMPPLAY1
PLY:CJNER1,#35H,PLY1
MOVA,MAOHAOH
SJMPPLAY1
PLY1:CJNER1,#42H,PLY2;判断是否为横杠的位置
MOVA,HENL;是者送横杠的ACSI码
SJMPPLAY1
PLY2:CJNER1,#45H,PLY3
MOVA,HENH
SJMPPLAY1
PLY3:MOVA,@R1;用查表法送要显示的数据
MOVDPTR,#TAB2
MOVCA,@A+DPTR
PLAY1:LCALLLCDWD
MOVA,R1
CJNEA,TEMP,PLAY;判断第一行是否显示好了
MOVA,#0C5H
LCALLLCDWC
MOVR1,#4AH;第二行显示数据存储单元的启始地址
MOVTEMP,#40H;第二行的结束地址
DJNZR6,PLAY;R6为0时两行显示结束
POPACC
POPPSW
RET
;************************按键子程序************************
JIANPAN:clrrs0;键盘子程序
setbrs1;键盘子程序
JBWEIXUAN,JIAN_ZEND;设置键的判断
LCALLDELAY5MS
JBWEIXUAN,JIAN_ZEND
PUSHACC
JNBWEIXUAN,$
;MOVA,#0FH;开启光标闪烁设置
;LCALLLCDWCN
fah:JNBNZBZ,JPNZSJ;为“1”闹钟为“0”时钟
LCALLJPXS
MOVR4,#86H;闹钟设定时间R4存放显示位地址
MOVR5,#2;闹钟设定时间第一行第二行•••••位置改变次数
MOVDIZHI,#8CH;闹钟设定时间第一行结束地址
MOVR0,#06H
SJMPGIVED
JPNZSJ:CLRTR0
MOVR4,#86H;R4存放显示位地址
MOVR5,#2;第一行第二行•••••位置改变次数
MOVDIZHI,#8DH;第一行结束地址
MOVR0,#36H
GIVED:MOVA,#0FH;开启光标闪烁设置
LCALLLCDWCN
MOVA,R4
LCALLLCDWC
UPONE:JBFANHUI,UPtwo;返回键的判断
LCALLDELAY5MS
JBFANHUI,UPtwo
JNBFANHUI,$
SJMPGOBACK
JIAN_ZEND:SJMPJIAN_END;中继跳转
UPtwo:JBJIAJIAN,IAM;加一键的判断
LCALLDELAY5MS
JBJIAJIAN,IAM
JNBJIAJIAN,$
LCALLJIAYI;调用按键加一子程序
;MOVA,#0FH;开启光标闪烁设置
;LCALLLCDWCN
IAM:JBJIANJIAN,IAM22;减一键的判断
LCALLDELAY5MS
JBJIANJIAN,IAM22
JNBJIANJIAN,$
LCALLJIANYI;调用按键减一子程序
AJMPGIVED
IAM22:JBWEIXUAN,UPONE;位选键的判断
LCALLDELAY5MS
JBWEIXUAN,UPONE
JNBWEIXUAN,$
DECR0
INCR4
LCALLJIAN;调用位选比较程序
CJNEr4,#88H,JJ11
DECR0
INCR4
SJMPJJ22
JJ11:CJNEr4,#8BH,JJ22
DECR0
INCR4
JJ22:MOVA,R4
CJNEA,DIZHI,GIVED
JBNZBZ,fah;为“1”闹钟为“0”时钟
MOVR4,#0C5H
MOVDIZHI,#0CFH
MOVR0,#49H
DJNZR5,GIVED
LJMPfah
GOBACK:MOVA,#0CH;关闭光标闪烁设置
LCALLLCDWCN
POPACC
JIAN_END:
SETBTR0
RET
;111111
;1111
JPXS:MOVA,#85H
LCALLLCDWC
MOVR1,#08H;第一行显示数据存储单元的启始地址
JPXS_LL:DECR1
CJNER1,#05H,JPXS_LP
MOVA,#3AH
SJMPJPXS_LY
JPXS_LP:MOVA,@R1;用查表法送要显示的数据
MOVDPTR,#TAB2
MOVCA,@A+DPTR
JPXS_LY:LCALLLCDWD
CJNER1,#03H,JPXS_LL
RET
;
;
;************************位选比较控制程序************************
;在调整时间时这几位是符号冒号和横杠的数据
;在这几位时跳过到下一位
JIAN:CJNER4,#87H,JIAN5
DECR0
INCR4
SJMPJIANEND
JIAN5:CJNER4,#8AH,JIAN1
DECR0
INCR4
SJMPJIANEND
JIAN1:CJNER4,#0C9H,JIAN2
DECR0
INCR4
SJMPJIANEND
JIAN2:CJNER4,#0CCH,JIANEND
DECR0
INCR4
JIANEND:RET
;************************按键加一子程序********************
JIAYI:INC@R0
CJNE@R0,#0AH,JIAYI_END
MOV@R0,#0
CJNER4,#86H,JIAYI55
SJMPJIAYI33
JIAYI55:CJNEr4,#89H,JIAYI11
SJMPJIAYI33
JIAYI11:CJNEr4,#8CH,JIAYI22
SJMPJIAYI33
JIAYI22:CJNER0,#30H,JIAYI_END
JIAYI33:INCR0
INC@R0
CJNE@R0,#06H,JIAYI44
MOV@R0,#0
JIAYI44:DECR0
JIAYI_END:MOVA,#0CH;开启光标闪烁设置
LCALLLCDWCN
JNBNZBZ,JIAYI88;为“1”闹钟为“0”时钟
LCALLJPXS
SJMPJIAYI99
JIAYI88:LCALLDISPLAY;显示子程序
JIAYI99:RET
;%%%%%%%%%%%%%%%%%%减一%%%%%%%%%%%%%%%%%%%%%%%%
JIANYI:DEC@R0
CJNE@R0,#00H,JIANYI_END
MOV@R0,#9
CJNER4,#86H,JIANYI55
SJMPJIANYI33
JIANYI55:CJNEr4,#89H,JIANYI11
SJMPJIANYI33
JIANYI11:CJNEr4,#8CH,JIANYI22
SJMPJIANYI33
JIANYI22:CJNER0,#30H,JIANYI_END
JIANYI33:INCR0
DEC@R0
CJNE@R0,#0FFH,JIANYI44
MOV@R0,#5
JIANYI44:DECR0
JIANYI_END:MOVA,#0CH;开启光标闪烁设置
LCALLLCDWCN
JNBNZBZ,JIANYI88;为“1”闹钟为“0”时钟
LCALLJPXS
SJMPJIANYI99
JIANYI88:LCALLDISPLAY;显示子程序
JIANYI99:RET
;******************************闹钟开关部分************************
NZBF:JBNZKG,NZBF_END
LCALLDELAY5MS
JBNZKG,NZBF_END
JNBNZKG,$
CPLNZBZ
JBNZBZ,NZBF_LL
MOVA,#01H;显示清屏
LCALLLCDWC
CLRP2.5
LCALLDISPSTART;调用显示初始状态
LCALLDISPLAY;调用显示初始状态
JBBSKGBZ,NZBF_LP;|
SJMPNZBF_END;|
NZBF_LP:MOVA,#8FH;报时标志显示位置;|
LCALLLCDWC;|保护报时标志在清屏是不被误清
MOVA,#25H;显示报时标志;|
LCALLLCDWD;|
SJMPNZBF_END;|
NZBF_LL:MOVA,#8EH;闹钟标志显示位置
LCALLLCDWC
MOVA,#26H;显示闹钟标志
LCALLLCDWD
NZBF_END:RET
;******************************闹钟比较部分************************
NZBJ:JBNZBZ,NZBJ_LL
SJMPNZBJ_END
NZBJ_LL:MOVA,07H
CJNEA,37H,NZBJ_END
MOVA,06H
CJNEA,36H,NZBJ_END
MOVA,04H
CJNEA,34H,NZBJ_END
MOVA,03H
CJNEA,33H,NZBJ_END
SETBP2.5
NZBJ_END:RET
;***************报时开关********************
BSKG:JBFANHUI,BSKG_END
LCALLDELAY5MS
JBFANHUI,BSKG_END
JNBFANHUI,$
CPLBSKGBZ
JBBSKGBZ,BSKG_LL
MOVA,#01H;显示清屏
LCALLLCDWC
CLRP2.6
LCALLDISPSTART;调用显示初始状态
LCALLDISPLAY;调用显示
JBNZBZ,BSKG_LP;|
SJMPBSKG_END;|
BSKG_LP:MOVA,#8EH;|
LCALLLCDWC;|保护闹钟标志在清屏是不被误清
MOVA,#26H;|
LCALLLCDWD;|
SJMPBSKG_END;|
BSKG_LL:MOVA,#8FH;报时标志显示位置
LCALLLCDWC
MOVA,#25H;显示报时标志
LCALLLCDWD
BSKG_END:RET
;*********************报时次数********************
BSCS:JNBBSKGBZ,BSCS_END
JNBBSCSBZ,BSCS_END
MOVA,37H
CJNEA,#01H,BSCS_LL
MOVA,#0AH
SJMPBSCS_LP
BSCS_LL:CJNEA,#02H,BSCS_LP
MOVA,#14H
BSCS_LP:ADDA,36H
RLA
MOVBSCSCC,A
CLRBSCSBZ
BSCS_END:RET
;**********************闪烁报时********************************
SSBS:JNBBSKGBZ,SSBS_END
JNBZDBSBZ,SSBS_END
JNBZMBSBZ,SSBS_END
CPLP2.6
CPLP2.7
CLRZMBSBZ
DJNZBSCSCC,SSBS_END
CLRZDBSBZ
CLRP2.6
SSBS_END:RET
;************************延时子程序(5MS)************************
DELAY5MS:
PUSHPSW
SETBRS0
SETBRS1
MOVR1,#25;延时子程序(5MS)
DL5_PA:MOVR2,#100
DJNZR2,$
DJNZR1,DL5_PA
POPPSW
RET
TAB:DB"TIME:",00h
TAB1:DB"DATE:",00h
TAB2:DB"01234567891"
TAB3:DB0AH,06H,00H,0AH,06H,00H,0AH,03H;用于比较调整时间时的比较
TAB4:DB0AH,04H,00H,0AH,02H,00H,0AH,0AH,0AH,0AH;用于调整日期时的比较
TAB5:DB00H,03H,02H,03H,03H,03H,03H,03H,03H,03H,03H,03H,03H;用于天数十位比较
TAB6:DB00H,02H,09H,02H,01H,02H,01H,02H,02H,01H,02H,01H,02H;用于天数个位比较
D. 基于单片机电子钟的国内外研究现状
二十一世纪是数字化技术高速发展的时代,而单片机在数字化高速发展的时代扮演着极为重要的角色。基于单片机操控的电子钟的开发与研究在信息化时代的今天亦是当务之急,因为它应用在学校、机关、企业、部队等单位礼堂、训练场地、教学室、公共场地等场合,可以说遍及人们生活的每一个角落。所以说电子万年历的开发是国家之所需,社会之所需,人民之所需。由于社会对信息交换不断提高的要求及高新技术的逐步发展,促使电子万年历发展并且投入市场得到广泛应用。随着科技的快速发展,时间的流逝,从观太阳、摆钟到现在电子钟,人类不断研究,不断创新纪录。它可以对年、月、日、时、分、秒进行计时,还具有闰年补偿等多种功能,而且DS1302的使用寿命长,误差小。对于数字电子万年历采用直观的数字显示,可以同时显示年、月、日、周日、时、分、秒和温度等信息,还具有时间校准等功能。该电路采用STC89C52单片机作为核心,功耗小,能在5V的低压工作,电压可选用4.5~5.5V电压供电。
E. 用单片机设计一个数字时钟
#include <REG51.H>#include <intrins.h> #define uint unsigned int#define uchar unsigned charsbit DS1302_CLK = P1^7; //实时时钟时钟线引脚 sbit DS1302_IO = P1^6; //实时时钟数据线引脚 sbit DS1302_RST = P1^5; //实时时钟复位线引脚sbit wireless_1 = P3^0;sbit wireless_2 = P3^1;sbit wireless_3 = P3^2;sbit wireless_4 = P3^3; //无线控制sbit ACC0 = ACC^0;sbit ACC7 = ACC^7;char hide_sec,hide_min,hide_hour,hide_day,hide_week,hide_month,hide_year; //秒,分,时到日,月,年位闪的计数sbit Set = P2^0; //模式切换键sbit Up = P2^1; //加法按钮sbit Down = P2^2; //减法按钮sbit out = P2^3; //立刻跳出调整模式按钮sbit DQ = P1^0; //温度传送数据IO口char done,count,temp,flag,up_flag,down_flag;uchar temp_value; //温度值uchar TempBuffer[5],week_value[2]; void show_time(); //液晶显示程序/***********1602液晶显示部分子程序****************///Port Definitions**********************************************************sbit LcdRs = P2^5;sbit LcdRw = P2^6;sbit LcdEn = P2^7;sfr DBPort = 0x80; //P0=0x80,P1=0x90,P2=0xA0,P3=0xB0.数据端口 //内部等待函数**************************************************************************unsigned char LCD_Wait(void){ LcdRs=0; LcdRw=1; _nop_(); LcdEn=1; _nop_(); LcdEn=0; return DBPort; }//向LCD写入命令或数据************************************************************#define LCD_COMMAND 0 // Command#define LCD_DATA 1 // Data#define LCD_CLEAR_SCREEN 0x01 // 清屏#define LCD_HOMING 0x02 // 光标返回原点void LCD_Write(bit style, unsigned char input){ LcdEn=0; LcdRs=style; LcdRw=0; _nop_(); DBPort=input; _nop_();//注意顺序 LcdEn=1; _nop_();//注意顺序 LcdEn=0; _nop_(); LCD_Wait(); } //设置显示模式************************************************************#define LCD_SHOW 0x04 //显示开#define LCD_HIDE 0x00 //显示关 #define LCD_CURSOR 0x02 //显示光标#define LCD_NO_CURSOR 0x00 //无光标 #define LCD_FLASH 0x01 //光标闪动#define LCD_NO_FLASH 0x00 //光标不闪动 void LCD_SetDisplay(unsigned char DisplayMode){ LCD_Write(LCD_COMMAND, 0x08|DisplayMode); } //设置输入模式************************************************************#define LCD_AC_UP 0x02#define LCD_AC_DOWN 0x00 // default #define LCD_MOVE 0x01 // 画面可平移#define LCD_NO_MOVE 0x00 //default void LCD_SetInput(unsigned char InputMode){ LCD_Write(LCD_COMMAND, 0x04|InputMode);} //初始化LCD************************************void LCD_Initial(){ LcdEn=0; LCD_Write(LCD_COMMAND,0x38); //8位数据端口,2行显示,5*7点阵 LCD_SetDisplay(LCD_SHOW|LCD_NO_CURSOR); //开启显示, 无光标 LCD_Write(LCD_COMMAND,LCD_CLEAR_SCREEN); //清屏 LCD_SetInput(LCD_AC_UP|LCD_NO_MOVE); //AC递增, 画面不动} //液晶字符输入的位置************************void GotoXY(unsigned char x, unsigned char y){ if(y==0) LCD_Write(LCD_COMMAND,0x80|x); if(y==1) LCD_Write(LCD_COMMAND,0x80|(x-0x40));} //将字符输出到液晶显示void Print(unsigned char *str){ while(*str!='\0') { LCD_Write(LCD_DATA,*str); str++; }} /***********DS1302时钟部分子程序******************/typedef struct __SYSTEMTIME__{ unsigned char Second; unsigned char Minute; unsigned char Hour; unsigned char Week; unsigned char Day; unsigned char Month; unsigned char Year; unsigned char DateString[11]; unsigned char TimeString[9];}SYSTEMTIME; //定义的时间类型SYSTEMTIME CurrentTime; #define AM(X) X#define PM(X) (X+12) // 转成24小时制#define DS1302_SECOND 0x80 //时钟芯片的寄存器位置,存放时间#define DS1302_MINUTE 0x82#define DS1302_HOUR 0x84 #define DS1302_WEEK 0x8A#define DS1302_DAY 0x86#define DS1302_MONTH 0x88#define DS1302_YEAR 0x8C void DS1302InputByte(unsigned char d) //实时时钟写入一字节(内部函数){ unsigned char i; ACC = d; for(i=8; i>0; i--) { DS1302_IO = ACC0; //相当于汇编中的 RRC DS1302_CLK = 1; DS1302_CLK = 0; ACC = ACC >> 1; } } unsigned char DS1302OutputByte(void) //实时时钟读取一字节(内部函数){ unsigned char i; for(i=8; i>0; i--) { ACC = ACC >>1; //相当于汇编中的 RRC ACC7 = DS1302_IO; DS1302_CLK = 1; DS1302_CLK = 0; } return(ACC); } void Write1302(unsigned char ucAddr, unsigned char ucDa) //ucAddr: DS1302地址, ucData: 要写的数据{ DS1302_RST = 0; DS1302_CLK = 0; DS1302_RST = 1; DS1302InputByte(ucAddr); // 地址,命令 DS1302InputByte(ucDa); // 写1Byte数据 DS1302_CLK = 1; DS1302_RST = 0;} unsigned char Read1302(unsigned char ucAddr) //读取DS1302某地址的数据{ unsigned char ucData; DS1302_RST = 0; DS1302_CLK = 0; DS1302_RST = 1; DS1302InputByte(ucAddr|0x01); // 地址,命令 ucData = DS1302OutputByte(); // 读1Byte数据 DS1302_CLK = 1; DS1302_RST = 0; return(ucData);} void DS1302_GetTime(SYSTEMTIME *Time) //获取时钟芯片的时钟数据到自定义的结构型数组{ unsigned char ReadValue; ReadValue = Read1302(DS1302_SECOND); Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = Read1302(DS1302_MINUTE); Time->Minute = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = Read1302(DS1302_HOUR); Time->Hour = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = Read1302(DS1302_DAY); Time->Day = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = Read1302(DS1302_WEEK); Time->Week = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = Read1302(DS1302_MONTH); Time->Month = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = Read1302(DS1302_YEAR); Time->Year = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); } void DateToStr(SYSTEMTIME *Time) //将时间年,月,日,星期数据转换成液晶显示字符串,放到数组里DateString[]{ if(hide_year<2) //这里的if,else语句都是判断位闪烁,<2显示数据,>2就不显示,输出字符串为 2007/07/22 { Time->DateString[0] = '2'; Time->DateString[1] = '0'; Time->DateString[2] = Time->Year/10 + '0'; Time->DateString[3] = Time->Year%10 + '0'; } else { Time->DateString[0] = ' '; Time->DateString[1] = ' '; Time->DateString[2] = ' '; Time->DateString[3] = ' '; } Time->DateString[4] = '/'; if(hide_month<2) { Time->DateString[5] = Time->Month/10 + '0'; Time->DateString[6] = Time->Month%10 + '0'; } else { Time->DateString[5] = ' '; Time->DateString[6] = ' '; } Time->DateString[7] = '/'; if(hide_day<2) { Time->DateString[8] = Time->Day/10 + '0'; Time->DateString[9] = Time->Day%10 + '0'; } else { Time->DateString[8] = ' '; Time->DateString[9] = ' '; } if(hide_week<2) { week_value[0] = Time->Week%10 + '0'; //星期的数据另外放到 week_value[]数组里,跟年,月,日的分开存放,因为等一下要在最后显示 } else { week_value[0] = ' '; } week_value[1] = '\0'; Time->DateString[10] = '\0'; //字符串末尾加 '\0' ,判断结束字符} void TimeToStr(SYSTEMTIME *Time) //将时,分,秒数据转换成液晶显示字符放到数组 TimeString[];{ if(hide_hour<2) { Time->TimeString[0] = Time->Hour/10 + '0'; Time->TimeString[1] = Time->Hour%10 + '0'; } else { Time->TimeString[0] = ' '; Time->TimeString[1] = ' '; } Time->TimeString[2] = ':'; if(hide_min<2) { Time->TimeString[3] = Time->Minute/10 + '0'; Time->TimeString[4] = Time->Minute%10 + '0'; } else { Time->TimeString[3] = ' '; Time->TimeString[4] = ' '; } Time->TimeString[5] = ':'; if(hide_sec<2) { Time->TimeString[6] = Time->Second/10 + '0'; Time->TimeString[7] = Time->Second%10 + '0'; } else { Time->TimeString[6] = ' '; Time->TimeString[7] = ' '; } Time->DateString[8] = '\0';} void Initial_DS1302(void) //时钟芯片初始化{ unsigned char Second=Read1302(DS1302_SECOND); if(Second&0x80) //判断时钟芯片是否关闭 { Write1302(0x8e,0x00); //写入允许 Write1302(0x8c,0x07); //以下写入初始化时间 日期:07/07/25.星期: 3. 时间: 23:59:55 Write1302(0x88,0x07); Write1302(0x86,0x25); Write1302(0x8a,0x07); Write1302(0x84,0x23); Write1302(0x82,0x59); Write1302(0x80,0x55); Write1302(0x8e,0x80); //禁止写入 } } /***********ds18b20子程序*************************/ /***********ds18b20延迟子函数(晶振12MHz )*******/ void delay_18B20(unsigned int i){ while(i--);} /**********ds18b20初始化函数**********************/ void Init_DS18B20(void) { unsigned char x=0; DQ = 1; //DQ复位 delay_18B20(8); //稍做延时 DQ = 0; //单片机将DQ拉低 delay_18B20(80); //精确延时 大于 480us DQ = 1; //拉高总线 delay_18B20(14); x=DQ; //稍做延时后 如果x=0则初始化成功 x=1则初始化失败 delay_18B20(20);} /***********ds18b20读一个字节**************/ unsigned char ReadOneChar(void){ uchar i=0; uchar dat = 0; for (i=8;i>0;i--) { DQ = 0; // 给脉冲信号 dat>>=1; DQ = 1; // 给脉冲信号 if(DQ) dat|=0x80; delay_18B20(4); } return(dat);} /*************ds18b20写一个字节****************/ void WriteOneChar(uchar dat){ unsigned char i=0; for (i=8; i>0; i--) { DQ = 0; DQ = dat&0x01; delay_18B20(5); DQ = 1; dat>>=1; }} /**************读取ds18b20当前温度************/ void ReadTemp(void){ unsigned char a=0; unsigned char b=0; unsigned char t=0; Init_DS18B20(); WriteOneChar(0xCC); // 跳过读序号列号的操作 WriteOneChar(0x44); // 启动温度转换 delay_18B20(100); // this message is wery important Init_DS18B20(); WriteOneChar(0xCC); //跳过读序号列号的操作 WriteOneChar(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度 delay_18B20(100); a=ReadOneChar(); //读取温度值低位 b=ReadOneChar(); //读取温度值高位 temp_value=b<<4; temp_value+=(a&0xf0)>>4; }void temp_to_str() //温度数据转换成液晶字符显示{ TempBuffer[0]=temp_value/10+'0'; //十位 TempBuffer[1]=temp_value%10+'0'; //个位 TempBuffer[2]=0xdf; //温度符号 TempBuffer[3]='C'; TempBuffer[4]='\0';}void Delay1ms(unsigned int count){ unsigned int i,j; for(i=0;i<count;i++) for(j=0;j<120;j++);} /*延时子程序*/void mdelay(uint delay){ uint i; for(;delay>0;delay--) {for(i=0;i<62;i++) //1ms延时. {;} }}
F. 单片机数字时钟设计
ORG 0X00
LJMP START
ORG 0X30
#30H ; 设置堆栈
MOV P1, #0XFE ; 设置在数码管的第0位显示
MOV A, #0X01 ; A置初值为0000 0001
LOOP: MOV P0, A ; 在数码管上显示寄存器A中的二进制数
MOV R3, #0X5C ; 延时1S
ACALL EXTDELAY ; 延时观察和记录对应的显示结果
RL A ; 将A寄存器的值循环左移一位
SJMP LOOP ; 循环显示
DELAY: MOV R1, #0XC8 ; 延时20MS
DL2: MOV R2, #0X18
DL1: NOP
NOP
DJNZ R2, DL1
DJNZ R1, DL2
RET
EXTDELAY:ACALL DELAY
DJNZ R3,EXTDELAY
RET
END
ORG 0X00
SJMP START
ORG 0X30
START: MOV SP, #30H ; 设置堆栈
MOV P1, #0XFE ; 位选信号,选中第0个数码管
MOV P0, #0X6F ; 在数码管上显示"9"
LOOP: SJMP LOOP ; 循环执行程序
END
ORG 0X00
LJMP START
ORG 0X30
START: MOV SP, #30H ; 设置堆栈
MOV P1, #0XFE ; 设置在数码管的第0位显示
MOV DPTR, #TABLE ; 送TABLE地址到DPTR指针
MOV R7, #0X00 ; 标志变量,限制指针在TABLE区
LOOP: CLR A
MOVC A, @A+DPTR ; 取TABLE区的字形码
MOV P0, A ; 在数码管上显示寄存器A中的二进制数
INC R7 ; 指针控制加一
MOV R3, #0X5C ; 延时
ACALL EXTDELAY ; 延时观察和记录对应的显示结果
CJNE R7, #0X10, NEXT ; 如果TABLE中数未取到最后一个,继续取下一个
MOV R7, #0X00 ; 如果已经取过所有数,则重新开始去第一个数
MOV DPTR, #TABLE
SJMP LOOP
NEXT: INC DPTR ; 指针寄存器加1
SJMP LOOP ; 循环显示
DELAY: MOV R1, #0XC8 ; 延时20MS
DL2: MOV R2, #0X18
DL1: NOP
NOP
DJNZ R2, DL1
DJNZ R1, DL2
RET
EXTDELAY: ACALL DELAY
DJNZ R3 ,EXTDELAY
RET
TABLE: ; 相对地址
DB 0X3F,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0X07,0X7F,0X6F ;十六进制字形数据
0 1 2 3 4 5 6 7 8 9 (十六进制数)
DB 0X77,0X7C,0X39,0X5E,0X79,0X71 ; 十六进制字形数据
A B C D E F (十六进制数)
END
ORG 0X00
LJMP START
ORG 0X30
START: MOV SP, #30H
MOV A, #0XFE ; 置A寄存器为0XFE
MOV DPTR, #TABLE
MOV R7, #0X00
LOOP: MOV P1, A ; 在数码管的第0位显示
MOV R4,A ; 保存A寄存器的制
CLR A
MOVC A, @A+DPTR
MOV P0, A
MOV A, R4 ; 还原A寄存器的值
CJNE A, #0XBF, SKIP ; 如果已在第五位显示,那么下一次
MOV A, #0XFE ; 从第0位开始显示
SJMP LOOP ; 跳回执行下一次显示
SKIP: RL A ; A左移一位,下一次在数码管的下一位显示数字
INC R7
MOV R3, #0X5C
ACALL EXTDELAY
CJNE R7, #0X10, NEXT ; 如果TABLE中数未取到最后一个,继续取下一个
MOV R7, #0X00 ; 如果已经取过所有数,则重新开始去第一个数
MOV DPTR, #TABLE
SJMP LOOP
NEXT: INC DPTR ; 指针寄存器加1
SJMP LOOP ; 循环显示
DELAY: MOV R1, #0XC8 ; 延时20MS
DL2: MOV R2, #0X18
DL1: NOP
NOP
DJNZ R2, DL1
DJNZ R1, DL2
RET
EXTDELAY: ACALL DELAY
DJNZ R3, EXTDELAY
RET
TABLE: ; 相对地址
DB 0X3F,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0X07,0X7F,0X6F ;十六进制字形数据
0 1 2 3 4 5 6 7 8 9 (十六进制数)
DB 0X77,0X7C,0X39,0X5E,0X79,0X71 ; 十六进制字形数据
A B C D E F (十六进制数)
END