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