89c51单片机的数字钟程序?

4个数码管的

/*********************************************
ILONG编做
【注意P3口输出模拟和在{实验板}上不一样。实验板不用取反】
【目的】:用20次T0定时产生1s.进而形成 HH-mm-ss时间
【参数说明】:
40H~47H :显示管,每位暂存器,存放要显示的数码的地址。可根据地址加1,实现该位数加1;
并且低4位可以代表管子要显示的值(42H、45H除外)。
48H :要显示的位值(0~7,由译码器翻译出)
49H :每位每次刷出时要显示的时间0~256us
4A :20次定时,的次数计数器
4BH,4CH :小时十位进位刷0,时,小时两位数的暂存
4DH :调试时,要调整类型,每次INT0中断自增一次
50H~5FH :0~F 16个数的码值
60H :"-"的码值
61H :"空" 的码值
62H,63H :要闪的两位地址暂存 (好像没用着)
R0 :存放 每位暂存器 的地址,用于 刷新位时 移位
00H(位) :是否有INT0(调整)中断
01H(位) :是闪亮,还是闪空,即:闪烁时的亮暗状态
【存在问题】:
1,调整时间时,分钟位开始乱码。
2,调整时间时,必须亮的时候才能调
3,时间差:慢于实际时间
2011.10.21 ilong(crazy night)
*********************************************/
SJMP 0x0030
ORG 0x0030
MAIN:
//启动外部中断
SETB IT0
SETB IE0
SETB EX0
SETB PX0
SETB IT1
SETB IE1
SETB EX1
SETB PX1
SETB EA
CLR 00H //没有调整中断
CLR 01H //闪空
MOV 4DH,#04H //0xFC
MOV P3,#0FH
MOV 62H,46H //从分开始闪
MOV 63H,47H
//十位数********************
MOV 50H,#3FH
MOV 51H,#06H
MOV 52H,#5BH
MOV 53H,#4FH
MOV 54H,#66H
MOV 55H,#6DH
MOV 56H,#7DH
MOV 57H,#07H
MOV 58H,#7FH
MOV 59H,#6FH
MOV 5AH,#77H
MOV 5BH,#7CH
MOV 5CH,#39H
MOV 5DH,#5EH
MOV 5EH,#79H
MOV 5FH,#71H
MOV 60H,#40H
MOV 61H,#00H
//八位管的暂存 从左到右40-47
MOV 40H,#50H
MOV 41H,#50H
MOV 42H,#60H
MOV 43H,#50H
MOV 44H,#50H
MOV 45H,#60H
MOV 46H,#50H
MOV 47H,#50H
MOV 48H,#00H //扫描位暂存
MOV R0,#40H //扫描值地址
MOV 4AH,00H //20次定时 计数
LCALL TIMER_GO20 //开启并初始定时器
//主函数进程,就是扫描码管值并显示,其他为中断操作***************************************************
SCAN: //显示器扫描输出*************************************
MOV P2,48H //选择显示位(从左到右0-7)
MOV A,@R0 //获取该位的数码值 地址
MOV R1,A
MOV A,@R1 //获取该位码值
//CPL A //根据数码管是共阴、共阳 是否取反
MOV P0,A //从P0输出每位的码值,注意:该端口时下面的“清屏”一起改
LCALL DELAY //进入每位延时
MOV P0,#0FFH //清屏
INC 48H //暂存器后移
INC R0 //位后移
MOV A,48H //通过 (48H)的值+08H 判断是否到了 位尾
ADD A,#08H
JB 0D6H,RER //D6H(位)为AC(辅助进位:半进位)。为1时说明(48H)的值+08H=F,即(48H)=8,此时跳向RER
SJMP SCAN
RER: //扫描重置
CLR 0D6H //重置 AC(辅助进位:半进位)
MOV 48H,#00H
MOV R0,#40H
SJMP SCAN
//End 主函数*********************************************************************************************
DELAY: //延时,用于扫描7段管时,在每一位停留的时间.时间太短,回使不该亮的段也有些亮
MOV 49H,#25H //49H的值不可以等于FF,因为FF取反后49H为0,不会延迟了
MOV A,0FEH //用取反设置循环次数,
CPL A
MOV 49H,A
ADD_1:
INC 49H
MOV R1,49H //因为DJNZ判断完后要把判断的地址减去1,所以为了DJNZ不对49H的内容造成影响,把49H的值装到R1中去判断
DJNZ R1,ADD_1
RET
//20次定时*********************************************************************************************************
TIMER_GO20:
//用4AH 设置循环次数*********************************
MOV 4AH,#15H //20(=0x15)次定时
MOV A,4AH //用取反设置定时次数,
CPL A
MOV 4AH,A
SJMP TIMER_S
TIMER_GO5: //与TIMER_GO20类似,只是这里只让定时5次一循环。用于调整闪烁
MOV 4AH,#05H //5(=0x05)次定时
MOV A,4AH //用取反设置定时次数,
CPL A
MOV 4AH,A
TIMER_S://未重置(4AH)的调用,
//设置启动T0
MOV TMOD,#01H //设置模式:T0模式1
MOV TH0,#3CH //T0初值高8位
MOV TL0,#0AH //T0初值低8位
//CLR TF0 //未知问题
SETB ET0 //T0允许中断
SETB EA //CPU允许中断
SETB TR0 //启动T0
RET
T0_INT: //T0中断程序段
CPL P1.7 //测试端口:T0重新定时一次发生一次跳转
LCALL TIMER_S //T0重新定时,继续跑。但:不会初始化20次计数
INC 4AH //T0定时次数加1
MOV A,4AH
JNZ CY_20 //50H加到FF再加就到0了。不为0回去接着执行主程序;为0 则20次定时 溢出即:00H(位)为1
CPL P1.6 //测试端口每秒发生一次跳转
JB 00H,GO_BLINK //如果00H(位)被置1,则不再执行时间系统加1。用于调整时显示闪烁
LCALL CLOCK_GO //时间系统加1秒
LCALL TIMER_GO20 //20次重新开始
SJMP CY_20
GO_BLINK:
LCALL TIMER_GO5 //5次重新开始,
LCALL BLINK
CY_20: RET
//时间系统进位设置*******************************************************************************************
CLOCK_GO:
INC 47H //秒加1
//个位秒 进位 十位
MOV A,#5AH //主要是看"#5A"中的“A”,
SUBB A,47H
JNZ SS_OUT //如果(47H)值 低4 与A中的低4不相同,跳到“SS_OUT”,不进位
MOV 47H,#50H
INC 46H
SS_OUT:
//秒 进位 分
MOV A,#56H //
SUBB A,46H
JNZ SM_OUT //如果(46H)值 低4 与A中的低4不相同,跳到“SS_OUT”,不进位
MOV 46H,#50H
CLOCK_GO_M:INC 44H
//分调整用
SM_OUT:
//分个位 进位 分
MOV A,#5AH //
SUBB A,44H
JNZ MM_OUT //如果(44H)值 低4 与A中的低4不相同,跳到“SS_OUT”,不进位
MOV 44H,#50H
INC 43H
MM_OUT:
//分 进位 时
MOV A,#56H //
SUBB A,43H
JNZ MH_OUT //如果(43H)值 低4 与A中的低4不相同,跳到“SS_OUT”,不进位
MOV 43H,#50H
CLOCK_GO_H:INC 41H
//时调整用
MH_OUT:
//时个位 进位 时
MOV A,#5AH //
CLR CY //排除借位影响
SUBB A,41H
JNZ HH_OUT //如果(41H)值 低4 与A中的低4不相同,跳到“SS_OUT”,不进位
MOV 41H,#50H
INC 40H
HH_OUT:
//时十位置0
MOV 4BH,40H //为了不影响暂存器数据,把40H、41H转到4BH、4CH中进行 操作
MOV 4CH,41H
MOV A,4BH
SWAP A //获得小时十位数,并放到A的高4位上
ANL A,#0F0H //清0低4位
ANL 4CH,#0FH //小时个位 高4位清0
ADD A,4CH //小时的十位与个位相加(高4位来自小时的十位暂存器40H,低四位来自小时个位的寄存器41H)
SUBB A,#24H
JNZ HD_OUT //如果(46H)值 低4 与A中的低4不相同,跳到“SS_OUT”,不进位
MOV 41H,#50H //个位 清零
MOV 40H,#50H //十位清零
HD_OUT:
CG_OUT: RET
//END 时间系统进位设置*******************************************************************************************
//INTO中断程序段*********************************************************************************
INT0_INT:
SETB 00H
CPL P1.5
CPL P1.5
CLR 01H //使得在换位闪烁时不会把上位的数带给下一位,
HMS_BACK: //在每次换位时都要把,被放到暂存上的值那回去,使其显示出来
MOV A,4DH
CJNE A,#02H,BSH_S //back second OR hour _select
LCALL LIGHT_M
SJMP B_END
BSH_S: //恢复秒小时选择
JB CY,B_H
SJMP B_S
B_S: LCALL LIGHT_S //把调好的数据装回暂存器
SJMP B_END
B_H: LCALL LIGHT_H
B_END:
// CLR TR0 //定时器0,停止计时
DEC 4DH //调整类型(时、分、秒)改变
MOV A,4DH
JNZ INT0_OUT //是否恢复时钟
CLR 00H //置0,调整中断(ilong 定义)
CLR 01H
MOV 4DH,#04H //初始化调整类型

INT0_OUT:
RET
////闪烁*****************************************************************
BLINK:
CPL P1.1
CPL 01H
MOV A,4DH
CJNE A,#02H,SH_S
SJMP MM_SET
SH_S: //闪烁秒小时选择
JB CY,HH_SET
SJMP SS_SET
SS_SET: //秒钟设置****************
JB 01H,DACK_S
LIGHT_S:
MOV 46H,4EH
MOV 47H,4FH
SJMP BLINK_OUT
DACK_S:
MOV 4EH,46H
MOV 4FH,47H
MOV 46H,#61H
MOV 47H,#61H
RET
MM_SET: // 分钟设置**************
JB 01H,DACK_M
LIGHT_M:
MOV 43H,4EH
MOV 44H,4FH
SJMP BLINK_OUT
DACK_M:
MOV 4EH,43H
MOV 4FH,44H
MOV 43H,#61H
MOV 44H,#61H
RET
HH_SET: //小时设置****************
JB 01H,DACK_H
LIGHT_H:
MOV 40H,4EH
MOV 41H,4FH
SJMP BLINK_OUT
DACK_H:
MOV 4EH,40H
MOV 4FH,41H
MOV 40H,#61H
MOV 41H,#61H
BLINK_OUT:RET
//INT1中断程序段***************************************************************
INT1_INT:
//lcall HMS_BACK
//clr tr0
CPL P1.4
MOV A,4DH
CJNE A,#02H,ADDSH_S //ADD Hour OR ADD second OR _select
SJMP ADD_M
ADDSH_S: //调整秒小时选择
JB CY,ADD_H
SJMP ADD_S
ADD_S: LCALL CLOCK_GO
SJMP ADD_END
ADD_M: LCALL CLOCK_GO_M
SJMP ADD_END
ADD_H: LCALL CLOCK_GO_H
ADD_END:
// LCALL CLOCK_GO
RET
/*********************************************************************************
最后放中断保险些
*********************************************************************************/
//T0中断程序***************
ORG 000BH
LCALL T0_INT
RETI
//INT0中断*****************
ORG 0003H
LCALL INT0_INT
RETI
//INT1中断*****************
ORG 0013H
LCALL INT1_INT
RETI
END
温馨提示:答案为网友推荐,仅供参考
第1个回答  2011-10-25
#include<reg52.h>
#include<intrins.h>
#define uint unsigned int
#define uchar unsigned char
sbit up_m=P3^0; //秒按键设置
sbit up_f=P3^1; //分按键设置
sbit up_s=P3^2; //时按键设置
uchar const smg[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
uchar i,j,k;
void delay(uint cnt)
{
while(--cnt);

}
void delayms(uint a)
{
uchar i;
while(--a)
{
for(i=0;i<125;i++);
}
}
void init()
{
TMOD=0X01;
TH0=(65535-50000)/256;
TL0=(65535-50000)%256;
ET0=1;
EA=1;
TR0=1;
}
void keyboard()
{
P3=0xff;
if(up_m==0)
delayms(40);
if(up_m==0)
{
i++;
if(i>=60)
{
i=0;
}
delayms(40);
}
if(up_f==0)
delayms(40);
if(up_f==0)
{
j++;
if(j>=60)
{
j=0;
}
delayms(40);
}
if(up_s==0)
delayms(40);
if(up_s==0)
{
k++;
if(k==23)
{
k=0;
}
delayms(40);
}
}
void init_smg()
{
P2=2;
P0=0x40;
delay(5);
P2=5;
P0=0x40;
delay(5);
}
main()
{
i=0;
init();
while(1)
{ init_smg();
P2=2;
P0=0x40;
P2=5;
P0=0x40;
P0=0x00;
keyboard();
P2=7;
P0=smg[i%10];
delay(3);
P0=0X00;
P2=6;
P0=smg[i/10];
delay(3);
P0=0x00;

P2=4;
P0=smg[j%10];
delay(3);
P0=0X00;
P2=3;
P0=smg[j/10];
delay(3);
P0=0x00;

P2=1;
P0=smg[k%10];
delay(3);
P0=0X00;
P2=0;
P0=smg[k/10];
delay(3);
P0=0x00;
}
}
void time () interrupt 1
{
unsigned char a;
a++;
TH0=(65535-50000)/256;
TL0=(65535-50000)%256;
if(a==20)
{
a=0;
i++; //秒控制
if(i==60)
{
i=0;
j++;//分控制
}
if(j==60)
{
j=0;
k++;//时控制
if(k>23)
k=0;
}
}
}

这是我以前写的一个,是八位数码管的,你自己改一下就好了,思想是一样的
第2个回答  2011-10-24
可以找我代做,成品都没问题,QQ67348467
相似回答