最后我附上了问题,如果有同学知道希望可以解答,谢谢
一、实验目的:
让六个数码管动态显示数字,计算秒数
二、实验分析:
a.因为要显示秒数,所以我们首先应该定义一个数组,用来存放0-9数字在数码管上对应的 十六进制数
b.3/8译码器的使能设置
c.定时器的理解。首先应该知道定时器与TMOD和和TCON有关。对于TMOD值得我们特别注意的是TMOD的Gate位(门控位)和C/T位。Gate=0时,仅由运行控制位TR1/TR0来启动定时器运行。而当Gate=1时,仅由运行控制位TR1/TR0和外中断引脚(INT0和INT1)上的高电平共同来启动定时器运行。C/T=0为定时模式,C/T=1为计数模式。我这次用到的是定时器1的方式一,为了不影响TMOD上的其他位,我这次用了逻辑运算:TMOD=TMOD|0x10;MOD=TMOD&0x0DF。对于定时方式一实际上是有一个16位数(TH和TL组成,各占8位)进行累加。上接着看TCON,我们主要用到的TF(定时溢出标志)和TR(定时器运行控制位)。上面说的16位数溢出时,TF=1。(特别需要注意的是:当执行溢出中断时TF位可由硬件清零,当不执行溢出中断时TF位一定要由软件清零)。而对于TR位,当TR=1时开始计时,TR=0,停止计时。因为方式一最长定时是100多毫秒,所以可以每1ms进行一次中断,中断1000次就是1s。
d.定时器的初始化:
因为方式一是16位,所以用公式:(65536-X)*12/11.0592M=0.001,可以得到x=FC66H
所以我们令TH1=0xFC,TL1=66
e.中断的初始化。我们主要用到中断请求寄存器IE。为了能够响应定时器1中断,我们需要另总中断使能位EA=1,定时器1中断使能位ET1=1
f.中断服务程序。这里特别注意我们需要重新设置TH1=0xFC,TL1=66,因为如果不设置,那么TH1=0x00,TL1=0x00,这样下次进入中断的时间久不是1ms了。同时我们在这里每3ms进行一次刷新一个数码管。这样在18ms就可以将全部数码管刷新一个。(因为人视线残留为20ms,而20/6=3.33,s所以每3ms刷新一次,这样就不会占用太多cpu!!)。
g. 刷新函数
需要指出的是在用C和汇编还是有一些方面是不一样的,在进行汇编编写是遇到的一个
比较棘手的问题就是六位数的存放。因为C中有32数,但是汇编没有,最后我用到的是用R0--R5分别存放数码管显示的数字。
三、实验代码:
1.C语言实现:
#include <reg52.h>
typedef unsigned char uint8;
typedef unsigned int uint16;
typedef unsigned long uint32;
//共阳数码管0-9
uint8 number[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
uint8 a[6];
uint32 sec=0; //用于记录秒数,因为六个数码管已经超过16位可以表示最大范围,所以用32 位
uint16 count=0;//用于记录中断次数,因为我设置每次中断时1ms,所以count=1000时就是一秒
sbit ENLED1 = P1^4;
sbit ENLED2= P1^3;
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
//首先3/8译码器使能
void en()
{
ENLED1=0;
ENLED2=1;
}
//定时器初始化
void init_timer()
{
TMOD=TMOD|0x10;
TMOD=TMOD&0x0DF;
TH1=0x0FC;
TL1=0x66;
TR1=1;
}
//中断初始化
void init_interrupr()
{
EA=1;//总中断使能位
ET1=1;//定时器1中断使能位
}
void display()
{
static uint8 j = 0;
switch(j)
{
case 0: ADDR0 = 0;ADDR1 = 0;ADDR2 = 0;j++;P0 = number[a[0]];break;
case 1: ADDR0 = 1;ADDR1 = 0;ADDR2 = 0;j++;P0 = number[a[1]];break;
case 2: ADDR0 = 0;ADDR1 = 1;ADDR2 = 0;j++;P0 = number[a[2]];break;
case 3: ADDR0 = 1;ADDR1 = 1;ADDR2 = 0;j++;P0 = number[a[3]];break;
case 4: ADDR0 = 0;ADDR1 = 0;ADDR2 = 1;j++;P0 = number[a[4]];break;
case 5: ADDR0 = 1;ADDR1 = 0;ADDR2 = 1;j=0;P0 = number[a[5]];break;
default: break;
}
}
void ov_interrupt() interrupt 3
{
count++;
TF1=0;// 这里因为我们进行了中断,所以硬件会自动清零,所以不一定要加上这一句
TH1=0x0FC;
TL1=0x66;
if(count==1000)//说明是一秒
{
sec++;
count=0;
a[0] = sec%10;
a[1] = sec/10%10;
a[2] = sec/100%10;
a[3] = sec/1000%10;
a[4] = sec/10000%10;
a[5] = sec/100000%10;
}
if(count%3==0)
display();//进行刷新,这里我们可以看出,我们是3ms进行一次刷新,这样就不会占用太多cpu了
}
void main()
{
en(); //首先3/8译码器使能
init_timer();//定时器初始化
init_interrupr();//中断初始化
while(1); //让程序一直处于运行中
}
2.汇编语言实现:
org 00H
ljmp start
org 1BH //定时器1中断入口地址
ljmp ov_interrupt
org 30H
start:
mov R6,#00//用于记录产生几次中断,如果(R6)=100,说明为1s
mov R0,#00//R0-R5用于存储数码管上数字
mov R1,#00
mov R2,#00
mov R3,#00
mov R4,#00
mov R5,#00
mov dptr,#numb
lcall en
lcall init_interrupt
lcall init_timer
here: sjmp here //好比是一个while(1)循环
;首先3/8译码器使能
en:
clr P1.4
setb P1.3
ret
//中断初始化
init_interrupt:
setb EA
setb ET1
ret
//定时器初始化
init_timer:
/*orl TMOD,#0x10
anl TMOD,#0x0DF*///这两技能说明工作在T1的方式1 且计时有TR1启动
mov TMOD,#10H
mov TH1,#0xDC
mov TL1,#0x00//一个10ms的定时初值设置
setb TR1 //让定时器1开始工作
ret
delay:
mov 20H,#3 ;一个机器周期
Dd2:
mov 30H,#10 ;一个机器周期
Dd1:
mov 40H,#3 ;一个机器周期
djnz 40H,$ ;2个机器周期
djnz 30H,Dd1 ;2个机器周期
djnz 20H,Dd2 ;个机器周期
Ret
//刷新函数
display:
D0:
mov A,R0
movc A,@A+dptr
clr P1.0
clr P1.1
clr P1.2
mov P0,A
lcall delay
D1:
mov A,R1
movc A,@A+dptr
setb P1.0
clr P1.1
clr P1.2
mov P0,A
lcall delay
D2:
mov A,R2
movc A,@A+dptr
clr P1.0
setb P1.1
clr P1.2
mov P0,A
lcall delay
D3:
mov A,R3
movc A,@A+dptr
setb P1.0
setb P1.1
clr P1.2
mov P0,A
lcall delay
D4:
mov A,R4
movc A,@A+dptr
clr P1.0
clr P1.1
setb P1.2
mov P0,A
lcall delay
D5:
mov A,R5
movc A,@A+dptr
setb P1.0
clr P1.1
setb P1.2
mov P0,A
lcall delay
ret
//TF1=1中断服务程序
ov_interrupt:
push DPH
push DPL
push psw
mov TH1,0xDC//一个0.01s的定时初值设置
mov TL1,0x00
inc R6 //中断计数器加1,当(R6)=100,就是1s
clr TF1// 这里因为我们进行了中断,所以硬件会自动清零,所以不一定要加上这一句
cjne R6,#100,return //判断中断计数器R6,如果不为100则转移到here
mov R6,#00H
led0:
inc R0//数码管0数字加1
cjne R0,#10,return //判断(R0),如果不为10,转移到display,
//否则为10就变为0,然后转移到led1
mov R0,#00H
led1:
inc R1
cjne R1,#10,return
mov R1,#00H
led2:
inc R2
cjne R2,#10,return
mov R2,#00H
led3:
inc R3
cjne R4,#10,return
mov R4,#00H
led4:
inc R4
cjne R4,#10,return
mov R4,#00H
led5:
inc R5
cjne R5,#10,return
mov R5,#00H
//如果执行这一句说明超过了999999
return:
pop psw
pop DPL
pop DPH
dis:
mov A,R6//当R6为偶数,进行display,也就是每两次中断(20ms)进行一次刷新
anl A,#01H
cjne A,#0,quit
call display
quit:
reti
//0-9
numb:DB 0C0H,0F9H,0A4H,0B0H,99H,92H,82H,0F8H,80H,90H
end
实验结果:
和自己实验前的分析结果一样。
四、实验问题:
1.定时问题:我的目的是为了得到1s,但是按照我的方法得到的只是一个非常接近于1s的数字,不知道有没有什么办法可以精确的得到1s
2.在进行汇编编程的时候,我用6个寄存器表示一个六位数,但是总是感觉这样消耗的内存大了,并且一个8位是只用了0-9,好像不是很节约内存,不知道有没有好的办法,可以解决这个问题。
3.不知道为什么第一个数码管比其他数码管亮很多