数码管动态扫描中的delay 函数有什么作用?

作者&投稿:哀盾 (若有异议请与网页底部的电邮联系)
数码管动态扫描中的delay 函数有什么作用?~

该实验用到实验板的资源电路图如下:




其中P0口是段码,低电平有效。P2口是位码,高电平有效。P2.0口控制第1个数码管,一直到P2.7口控制第8个。该板的段码表如下:



各个数码管的段码都是p0口的输出,即各个数码管输入的段码都是一样的, 为了使其分别显示不同的数字, 可采用动态显示的方式,即先只让最低位显示0(含点),经过一段延时,再只让次低位显示1,如此类推。由视觉暂留,只要我们的延时时间足够短,就能够使得数码的显示看起来非常的稳定清楚。过程如下图。




采用上述方法思路编写如下:

org 0000h

start: mov a,#08h ;0 ;段码
mov p0,a
mov p2,#01h ;位码
lcall delay_1ms

mov a,#0abh ;1
mov p0,a
mov p2,#02h
lcall delay_1ms

mov a,#12h ;2
mov p0,a
mov p2,#04h
lcall delay_1ms

mov a,#22h ;3
mov p0,a
mov p2,#08h
lcall delay_1ms

mov a,#0a1h ;4
mov p0,a
mov p2,#10h
lcall delay_1ms

mov a,#24h ;5
mov p0,a
mov p2,#20h
lcall delay_1ms

mov a,#04h ;6
mov p0,a
mov p2,#40h
lcall delay_1ms

; mov a,#0aah ;7
; mov p0,a
mov p0,#0aah ;感觉用这句和上面两句实现一样,可能这种习惯以后会有用吧
mov p2,#80h
lcall delay_1ms

ljmp start

delay_1ms: mov r6,#2
temp: mov r5,#0ffh
djnz r5,$
djnz r6,temp
ret
end


下载到板上得到测结果为从低到高八位分别显示0到7(含点)。

★ 上述方法逐次给P0或者P2赋值,一方面程序的复杂程度增加,另外一方面会使得程序的灵活性降低。如果要改变显示的数字,程序改动起来很麻烦。 所以要用51单片机中常用的一种方法:查表法。例如P0口输出段码时,我们可以把要显示的段码放在一个表格中,然后每次从这个表格里面取数,送到P0口即可。P2口输出位码时,可以把要用的位码放在另一个表格里,每次从此表中取数,送入P2口。这样,如果要改变显示的数字,只需要改变表格里面的数。

org 0000h

start: mov r7,#0ffh ;r7,r6查表时送入变址寄存器a (因自加1后为0,所以预置ffh)
mov r6,#0ffh
loop: lcall play1 ;调用显示段码子程序
lcall play2 ;调用显示位码子程序
lcall delay_1ms
cjne a,#80h,loop ;判断是否到了最左边的数,即第8个位码
ajmp start

play1: ;查表求段码子程序
; mov a,r7
; inc a
; mov r7,a

inc r7 ;这2句和上面三条语句实现功能相同
mov a,r7 ;a在这里做变址寄存器

mov dptr,#table1 ;表首址送dptr,dptr做基址寄存器
movc a,@a+dptr ;基址寄存器加变址寄存器寻址
mov p0,a
ret

play2: ;查表求位码子程序(原理同play1)
mov a,r6
inc a
mov r6,a
mov dptr,#table2
movc a,@a+dptr
mov p2,a
ret

table1: db 08h,0abh,12h,22h,0a1h,24h,04h,0aah ;段码表
table2: db 01h,02h,04h,08h,10h,20h,40h,80h ;位码表

delay_1ms: mov r5,#02h ;延时1ms子程序
temp: mov r4,#0ffh
djnz r4,$
djnz r5,temp
ret
end

下载到板上验证得到预想结果。



--------------------------------------------------------------------------------
C51实现如下(参考了AS的例程):

#include <reg51.h>
#include <intrins.h> // 包含了左移函数_crol_()

void delayms(unsigned char ms); // 延时子程序

unsigned char data dis_digit; // 位选通值, 传送到P2口用于选通当前数码管的数值,
// 如等于0x01时,选通P2.0口数码管

unsigned char code dis_code[11]={0x08,0xab,0x12,0x22,0xa1, // 0,1,2,3, 4
0x24,0x04,0xaa,0x00,0x20, 0xff}; // 5,6,7,8,9, off

unsigned char data dis_buf[8]; // dis_buf 显于缓冲区基地址

unsigned char data dis_index; // 显示索引, 用于标识当前显示的数码管和缓冲区的偏移量

void main()
{
P0 = 0xff; // 关闭所有数码管
P2 = 0x00;

dis_buf[0] = dis_code[0];
dis_buf[1] = dis_code[1];
dis_buf[2] = dis_code[2];
dis_buf[3] = dis_code[3];
dis_buf[4] = dis_code[4];
dis_buf[5] = dis_code[5];
dis_buf[6] = dis_code[6];
dis_buf[7] = dis_code[7];

dis_digit = 0x01; // 首先选通P2.0
dis_index = 0; // 当前偏移量为0

while(1)
{
P0 = dis_buf[dis_index]; // 段码送P0口
P2 = dis_digit; // 选能位(即位码)
delayms(1); // 延时
dis_digit = _crol_(dis_digit, 1); // 位选通左移, 下次选通下一位
dis_index++; // 下一个段码

dis_index &= 0x07; // 见注释
}

}
void delayms(unsigned char ms) // 延时子程序(晶振12M)
{
unsigned char i;
while(ms--)
{
for(i = 0; i < 120; i++);
}
}


★ 注释: 此句作用是8个数码管全部扫描完一遍之后,再回到第一个开始下一次扫描。写回一般形式:dis_index = dis_index & 0x07 。这种方法挺新,第一次见到,十六进制的07就是二进制的00000111,这样通过与操作可能控制循环了。比如dis_index 经第一次循环后值为00000001,和0x07与操作后值不变仍为0x01,第二次循环时,其值为0为0x02,与0x07后仍为0x02,一直到其值增为0x07时还是不变的,但再次循环后其值为0x80,再与0x07后就变成0x00了,这样又从初始循环了。此句可用 if (dis_index == 8) dis_index = 0 代替,效果一样。

★ 通过C51用上述方法实现时,其段码放在了数组dis_code[11]中,再通过缓冲区数组dis_buf[]将程序中要调用的值装入,这样就可以用下标(偏移量)访问了。这样看上去有些繁锁,但其思路比较清楚,结构上也很明了,具有通用性,便于扩展。

★ 另外只要把程序中的延时加长,如delayms(1000),下载到板上就可以看到实际上数码管是由低位到高位逐位显示的。



--------------------------------------------------------------------------------
若单单就实现这个功能而言,可以直接调入段码数组dis_code[11]中下标从0到7的值,而不必再设置缓冲数组dis_buf[],实现如下:

#include <reg51.h>
#include <intrins.h> //_crol_()用

void delayms(unsigned char ms); //延时子程序

unsigned char data dis_digit; //位选通值, 传送到P2口用于选通当前数码管的数值,
//如等于0x01时,选通P2.0口数码管

unsigned char code dis_code[11]={0x08,0xab,0x12,0x22,0xa1, // 0,1,2,3,4
0x24,0x04,0xaa,0x00,0x20, 0xff}; // 5,6,7,8,9,off

unsigned char data dis_index; //显示索引, 用于标识当前显示的数码管和缓冲区的偏移量

void main()
{
P0 = 0xff; // 关闭所有数码管
P2 = 0x00;

dis_index = 0; // 当前偏移量为0
dis_digit = 0x01; // 选通P2.0

while(1)
{
P0 = dis_code[dis_index]; // 段码送P0口
P2 = dis_digit; // 位码送P2口
delayms(1);

dis_digit = _crol_(dis_digit, 1); // 位选通左移, 下次选通下一位

dis_index++;
dis_index &= 0x07;
}
}
void delayms(unsigned char ms) // 延时子程序(晶振12M)
{
unsigned char i;
while(ms--)
{
for(i = 0; i < 120; i++);
}
}

★ 通本来是想通过以下方式实现一次循环的:

for (dis_index = 0; dis_index < 8; dis_index++)
{
P0 = dis_code[dis_index]; // 段码送P0口
P2 = dis_index+1; // 位码送P2口
delayms(1);
}

可得到的总是错误的结果:第0位到第2位这三位显示的是三个8,第3位显示的是7,高四位没有显示。加长延时逐位观察也没有发现错误的规律,对Keil的调试也不熟悉,先把问题留到这,待找出原因后再补上。

[2006.5.2] 找出原因啦,补上:

今天又看了一下,找到上面的错误出在哪了。当时是想用dis_index的值做为位码的,即第一位显示0时,段码为dis_code[0], 即dis_index值为0, 此时位码值为1。第二位显示1时,段码为dis_code[1],即dis_index值为1,此时位码值为2。所以就简单用了个加1运算,将P0口的偏移值与P2口的位码联系起来。但仔细想一下位码的原理,上述方法显然是错的,只要再验证一步就明白了,即当第3位显示2时,段码为dis_code[2], dis_index值为2,加1后为3,按上述方法时就将这个3作为了位码,而正确的位码应该是4 (00000100B)。所以出错。实际上这个对应关系是有的,但不是简简单单的加1,位码应该是2的dis_index次幂。即:
0--1
1--2
2--4
3--8
4--16 ……
幂次运算函数flaot pow(float x, float y)包含在math.h中, 返回值为xy (float型):

for (dis_index = 0; dis_index < 8; dis_index++)
{
P0 = dis_code[dis_index]; // 段码送P0口
P2 = (char) pow(2, dis_index); // 位码送P2口
delayms(255);
}

再次下载到板上发现仍有问题, 即延时很小的时候显示混乱,但加大延时时间(如程序中的值)可以观查到数码管是按位正确显示的。另外用这种方法产生的代码量也很大(从写入速度看,很明显)。这里仅提出了一个思路,只在此实验中适用,意义不大,到此为止。

[补充结束]



--------------------------------------------------------------------------------


AS中绐出的例程是利用定时中断做的延时,参考修改到我的板上,程序如下:

#include <reg51.h>
#include <intrins.h> // 包含了左移函数_crol_()

unsigned char data dis_digit; // 位选通值, 传送到P2口用于选通当前数码管的数值,
// 如等于0x01时,选通P2.0口数码管

unsigned char code dis_code[11]={0x08,0xab,0x12,0x22,0xa1, // 0,1,2,3,4
0x24,0x04,0xaa,0x00,0x20, 0xff}; // 5,6,7,8,9,off

unsigned char data dis_buf[8]; // dis_buf 显于缓冲区基地址

unsigned char data dis_index; // 显示索引, 用于标识当前显示的数码管和缓冲区的偏移量

void main()
{
P0 = 0xff; //关闭所有数码管
P2 = 0x00;

TMOD = 0x01; // 00000001B 定时计数器0工作在方式1,16位定时器/计数器
TH0 = 0xFC;
TL0 = 0x17; // 预置初值 FC17H=64535D, 216-64535=1001us=1ms

IE = 0x82; // 10000010B T0溢出中断允许

dis_buf[0] = dis_code[0x0];
dis_buf[1] = dis_code[0x1];
dis_buf[2] = dis_code[0x2];
dis_buf[3] = dis_code[0x3];
dis_buf[4] = dis_code[0x4];
dis_buf[5] = dis_code[0x5];
dis_buf[6] = dis_code[0x6];
dis_buf[7] = dis_code[0x7];

dis_digit = 0x01; // 选通第0位数码管
dis_index = 0; // 偏移初值为0

TR0 = 1; // 启动T0
while(1); // 循环等待中断

}

void timer0() interrupt 1 // 定时器0中断服务程序, 用于数码管的动态扫描

{
TH0 = 0xFC; // 发生中断定时/计数器重装初值
TL0 = 0x17; // 感觉此处(及上)应该是0x18,而不是17,分析如下

P2 = 0x00; // 先关闭所有数码管
P0 = dis_buf[dis_index]; // 段码送P0口
P2 = dis_digit; // 位码送P2口

dis_digit = _crol_(dis_digit,1); // 位选通值左移, 下次中断时选通下一位数码管
dis_index++;

dis_index &= 0x07; // 8个数码管全部扫描完一遍之后,再回到第一个开始下一次扫描
}

★ 定时器/计数器的输入脉冲周期与机器周期一样, 为时钟振荡频率的1/12。晶振用12M时,输入脉冲周期间隔为1us。机器周期为 1us。设T0的初值为X,计算初值的方法:本例中定时器用方式1,是16位的定时器,即最大值为216=65536,超过此值将发生溢出,引起中断,进入中断处理程序。这里要让其延时1ms,即1000us, 则有式216-X=1000,可得X=64536,换算为16进制为FC18,即初值TH0=0xFC,TL0=0x18。即定时器由64536开始计数,经1000次计数后值为65536,将发生定时中断,再进入中断处理子程序后,重新装和初值,如此循环下去。
而在上例中其装入的初值并非FC18(64536),而是FC17(64535)。我想大概认为其计数范围在0~65565的原因吧,我也想过这个问题,是用216-计数初值=中断间隔 呢,还是用(216-1)-计数初值=中断间隔呢? 随手查了几本书, 说法不一,不过用前者的较多, 我自己也认为前者比较合理, 因为在计算机中16位的二进制不能表示65536, 在各位均为1时表示的值为65535, 即65535H=1111111111111111B, 也可以说65536是溢出得到的。而何时响应中断就成了关键,拿上例来说,如设初值为64535(FC17),则计数到65535时,已经计数为1000个,即1ms,但此时并未发生溢出,因此也没有触发中断。而是在下一个计数后才发生。确切值应为1001us。若初值为64536(FC18),则恰好为所需值,所以上例中的初值应该用FC18而不是FC17。这仅仅是我自己的一点看法,至于是不是这样,还有待进一步考证。



--------------------------------------------------------------------------------
最终下载到实验板上结果:




######################################补充########################################

用Proteus仿真结果如下(某一状态的截图):




★ 该电路段码是按与板上接法对应的,即按前面的段码表次序连接。另外这个八位的仿真数码管最左端是第一位,最右端是第八位,与板上的顺序相反,所以接为了统一,该图以板为准连接。上图不加上拉电阻也可仿真出结果,只是P0口高电平显示为灰,即高阻。

该实验用到实验板的资源电路图如下:

其中P0口是段码,低电平有效。P2口是位码,高电平有效。P2.0口控制第1个数码管,一直到P2.7口控制第8个。该板的段码表如下:

各个数码管的段码都是p0口的输出,即各个数码管输入的段码都是一样的, 为了使其分别显示不同的数字, 可采用动态显示的方式,即先只让最低位显示0(含点),经过一段延时,再只让次低位显示1,如此类推。由视觉暂留,只要我们的延时时间足够短,就能够使得数码的显示看起来非常的稳定清楚。过程如下图。

采用上述方法思路编写如下:

org 0000h

start: mov a,#08h ;0 ;段码
mov p0,a
mov p2,#01h ;位码
lcall delay_1ms

mov a,#0abh ;1
mov p0,a
mov p2,#02h
lcall delay_1ms

mov a,#12h ;2
mov p0,a
mov p2,#04h
lcall delay_1ms

mov a,#22h ;3
mov p0,a
mov p2,#08h
lcall delay_1ms

mov a,#0a1h ;4
mov p0,a
mov p2,#10h
lcall delay_1ms

mov a,#24h ;5
mov p0,a
mov p2,#20h
lcall delay_1ms

mov a,#04h ;6
mov p0,a
mov p2,#40h
lcall delay_1ms

; mov a,#0aah ;7
; mov p0,a
mov p0,#0aah ;感觉用这句和上面两句实现一样,可能这种习惯以后会有用吧
mov p2,#80h
lcall delay_1ms

ljmp start

delay_1ms: mov r6,#2
temp: mov r5,#0ffh
djnz r5,$
djnz r6,temp
ret
end

下载到板上得到测结果为从低到高八位分别显示0到7(含点)。

★ 上述方法逐次给P0或者P2赋值,一方面程序的复杂程度增加,另外一方面会使得程序的灵活性降低。如果要改变显示的数字,程序改动起来很麻烦。 所以要用51单片机中常用的一种方法:查表法。例如P0口输出段码时,我们可以把要显示的段码放在一个表格中,然后每次从这个表格里面取数,送到P0口即可。P2口输出位码时,可以把要用的位码放在另一个表格里,每次从此表中取数,送入P2口。这样,如果要改变显示的数字,只需要改变表格里面的数。

org 0000h

start: mov r7,#0ffh ;r7,r6查表时送入变址寄存器a (因自加1后为0,所以预置ffh)
mov r6,#0ffh
loop: lcall play1 ;调用显示段码子程序
lcall play2 ;调用显示位码子程序
lcall delay_1ms
cjne a,#80h,loop ;判断是否到了最左边的数,即第8个位码
ajmp start

play1: ;查表求段码子程序
; mov a,r7
; inc a
; mov r7,a

inc r7 ;这2句和上面三条语句实现功能相同
mov a,r7 ;a在这里做变址寄存器

mov dptr,#table1 ;表首址送dptr,dptr做基址寄存器
movc a,@a+dptr ;基址寄存器加变址寄存器寻址
mov p0,a
ret

play2: ;查表求位码子程序(原理同play1)
mov a,r6
inc a
mov r6,a
mov dptr,#table2
movc a,@a+dptr
mov p2,a
ret

table1: db 08h,0abh,12h,22h,0a1h,24h,04h,0aah ;段码表
table2: db 01h,02h,04h,08h,10h,20h,40h,80h ;位码表

delay_1ms: mov r5,#02h ;延时1ms子程序
temp: mov r4,#0ffh
djnz r4,$
djnz r5,temp
ret
end

下载到板上验证得到预想结果。

--------------------------------------------------------------------------------
C51实现如下(参考了AS的例程):

#include <reg51.h>
#include <intrins.h> // 包含了左移函数_crol_()

void delayms(unsigned char ms); // 延时子程序

unsigned char data dis_digit; // 位选通值, 传送到P2口用于选通当前数码管的数值,
// 如等于0x01时,选通P2.0口数码管

unsigned char code dis_code[11]={0x08,0xab,0x12,0x22,0xa1, // 0,1,2,3, 4
0x24,0x04,0xaa,0x00,0x20, 0xff}; // 5,6,7,8,9, off

unsigned char data dis_buf[8]; // dis_buf 显于缓冲区基地址

unsigned char data dis_index; // 显示索引, 用于标识当前显示的数码管和缓冲区的偏移量

void main()
{
P0 = 0xff; // 关闭所有数码管
P2 = 0x00;

dis_buf[0] = dis_code[0];
dis_buf[1] = dis_code[1];
dis_buf[2] = dis_code[2];
dis_buf[3] = dis_code[3];
dis_buf[4] = dis_code[4];
dis_buf[5] = dis_code[5];
dis_buf[6] = dis_code[6];
dis_buf[7] = dis_code[7];

dis_digit = 0x01; // 首先选通P2.0
dis_index = 0; // 当前偏移量为0

while(1)
{
P0 = dis_buf[dis_index]; // 段码送P0口
P2 = dis_digit; // 选能位(即位码)
delayms(1); // 延时
dis_digit = _crol_(dis_digit, 1); // 位选通左移, 下次选通下一位
dis_index++; // 下一个段码

dis_index &= 0x07; // 见注释
}

}
void delayms(unsigned char ms) // 延时子程序(晶振12M)
{
unsigned char i;
while(ms--)
{
for(i = 0; i < 120; i++);
}
}

★ 注释: 此句作用是8个数码管全部扫描完一遍之后,再回到第一个开始下一次扫描。写回一般形式:dis_index = dis_index & 0x07 。这种方法挺新,第一次见到,十六进制的07就是二进制的00000111,这样通过与操作可能控制循环了。比如dis_index 经第一次循环后值为00000001,和0x07与操作后值不变仍为0x01,第二次循环时,其值为0为0x02,与0x07后仍为0x02,一直到其值增为0x07时还是不变的,但再次循环后其值为0x80,再与0x07后就变成0x00了,这样又从初始循环了。此句可用 if (dis_index == 8) dis_index = 0 代替,效果一样。

★ 通过C51用上述方法实现时,其段码放在了数组dis_code[11]中,再通过缓冲区数组dis_buf[]将程序中要调用的值装入,这样就可以用下标(偏移量)访问了。这样看上去有些繁锁,但其思路比较清楚,结构上也很明了,具有通用性,便于扩展。

★ 另外只要把程序中的延时加长,如delayms(1000),下载到板上就可以看到实际上数码管是由低位到高位逐位显示的。

--------------------------------------------------------------------------------
若单单就实现这个功能而言,可以直接调入段码数组dis_code[11]中下标从0到7的值,而不必再设置缓冲数组dis_buf[],实现如下:

#include <reg51.h>
#include <intrins.h> //_crol_()用

void delayms(unsigned char ms); //延时子程序

unsigned char data dis_digit; //位选通值, 传送到P2口用于选通当前数码管的数值,
//如等于0x01时,选通P2.0口数码管

unsigned char code dis_code[11]={0x08,0xab,0x12,0x22,0xa1, // 0,1,2,3,4
0x24,0x04,0xaa,0x00,0x20, 0xff}; // 5,6,7,8,9,off

unsigned char data dis_index; //显示索引, 用于标识当前显示的数码管和缓冲区的偏移量

void main()
{
P0 = 0xff; // 关闭所有数码管
P2 = 0x00;

dis_index = 0; // 当前偏移量为0
dis_digit = 0x01; // 选通P2.0

while(1)
{
P0 = dis_code[dis_index]; // 段码送P0口
P2 = dis_digit; // 位码送P2口
delayms(1);

dis_digit = _crol_(dis_digit, 1); // 位选通左移, 下次选通下一位

dis_index++;
dis_index &= 0x07;
}
}
void delayms(unsigned char ms) // 延时子程序(晶振12M)
{
unsigned char i;
while(ms--)
{
for(i = 0; i < 120; i++);
}
}

★ 通本来是想通过以下方式实现一次循环的:

for (dis_index = 0; dis_index < 8; dis_index++)
{
P0 = dis_code[dis_index]; // 段码送P0口
P2 = dis_index+1; // 位码送P2口
delayms(1);
}

可得到的总是错误的结果:第0位到第2位这三位显示的是三个8,第3位显示的是7,高四位没有显示。加长延时逐位观察也没有发现错误的规律,对Keil的调试也不熟悉,先把问题留到这,待找出原因后再补上。

[2006.5.2] 找出原因啦,补上:

今天又看了一下,找到上面的错误出在哪了。当时是想用dis_index的值做为位码的,即第一位显示0时,段码为dis_code[0], 即dis_index值为0, 此时位码值为1。第二位显示1时,段码为dis_code[1],即dis_index值为1,此时位码值为2。所以就简单用了个加1运算,将P0口的偏移值与P2口的位码联系起来。但仔细想一下位码的原理,上述方法显然是错的,只要再验证一步就明白了,即当第3位显示2时,段码为dis_code[2], dis_index值为2,加1后为3,按上述方法时就将这个3作为了位码,而正确的位码应该是4 (00000100B)。所以出错。实际上这个对应关系是有的,但不是简简单单的加1,位码应该是2的dis_index次幂。即:
0--1
1--2
2--4
3--8
4--16 ……
幂次运算函数flaot pow(float x, float y)包含在math.h中, 返回值为xy (float型):

for (dis_index = 0; dis_index < 8; dis_index++)
{
P0 = dis_code[dis_index]; // 段码送P0口
P2 = (char) pow(2, dis_index); // 位码送P2口
delayms(255);
}

再次下载到板上发现仍有问题, 即延时很小的时候显示混乱,但加大延时时间(如程序中的值)可以观查到数码管是按位正确显示的。另外用这种方法产生的代码量也很大(从写入速度看,很明显)。这里仅提出了一个思路,只在此实验中适用,意义不大,到此为止。

[补充结束]

--------------------------------------------------------------------------------

AS中绐出的例程是利用定时中断做的延时,参考修改到我的板上,程序如下:

#include <reg51.h>
#include <intrins.h> // 包含了左移函数_crol_()

unsigned char data dis_digit; // 位选通值, 传送到P2口用于选通当前数码管的数值,
// 如等于0x01时,选通P2.0口数码管

unsigned char code dis_code[11]={0x08,0xab,0x12,0x22,0xa1, // 0,1,2,3,4
0x24,0x04,0xaa,0x00,0x20, 0xff}; // 5,6,7,8,9,off

unsigned char data dis_buf[8]; // dis_buf 显于缓冲区基地址

unsigned char data dis_index; // 显示索引, 用于标识当前显示的数码管和缓冲区的偏移量

void main()
{
P0 = 0xff; //关闭所有数码管
P2 = 0x00;

TMOD = 0x01; // 00000001B 定时计数器0工作在方式1,16位定时器/计数器
TH0 = 0xFC;
TL0 = 0x17; // 预置初值 FC17H=64535D, 216-64535=1001us=1ms

IE = 0x82; // 10000010B T0溢出中断允许

dis_buf[0] = dis_code[0x0];
dis_buf[1] = dis_code[0x1];
dis_buf[2] = dis_code[0x2];
dis_buf[3] = dis_code[0x3];
dis_buf[4] = dis_code[0x4];
dis_buf[5] = dis_code[0x5];
dis_buf[6] = dis_code[0x6];
dis_buf[7] = dis_code[0x7];

dis_digit = 0x01; // 选通第0位数码管
dis_index = 0; // 偏移初值为0

TR0 = 1; // 启动T0
while(1); // 循环等待中断

}

void timer0() interrupt 1 // 定时器0中断服务程序, 用于数码管的动态扫描

{
TH0 = 0xFC; // 发生中断定时/计数器重装初值
TL0 = 0x17; // 感觉此处(及上)应该是0x18,而不是17,分析如下

P2 = 0x00; // 先关闭所有数码管
P0 = dis_buf[dis_index]; // 段码送P0口
P2 = dis_digit; // 位码送P2口

dis_digit = _crol_(dis_digit,1); // 位选通值左移, 下次中断时选通下一位数码管
dis_index++;

dis_index &= 0x07; // 8个数码管全部扫描完一遍之后,再回到第一个开始下一次扫描
}

★ 定时器/计数器的输入脉冲周期与机器周期一样, 为时钟振荡频率的1/12。晶振用12M时,输入脉冲周期间隔为1us。机器周期为 1us。设T0的初值为X,计算初值的方法:本例中定时器用方式1,是16位的定时器,即最大值为216=65536,超过此值将发生溢出,引起中断,进入中断处理程序。这里要让其延时1ms,即1000us, 则有式216-X=1000,可得X=64536,换算为16进制为FC18,即初值TH0=0xFC,TL0=0x18。即定时器由64536开始计数,经1000次计数后值为65536,将发生定时中断,再进入中断处理子程序后,重新装和初值,如此循环下去。
而在上例中其装入的初值并非FC18(64536),而是FC17(64535)。我想大概认为其计数范围在0~65565的原因吧,我也想过这个问题,是用216-计数初值=中断间隔 呢,还是用(216-1)-计数初值=中断间隔呢? 随手查了几本书, 说法不一,不过用前者的较多, 我自己也认为前者比较合理, 因为在计算机中16位的二进制不能表示65536, 在各位均为1时表示的值为65535, 即65535H=1111111111111111B, 也可以说65536是溢出得到的。而何时响应中断就成了关键,拿上例来说,如设初值为64535(FC17),则计数到65535时,已经计数为1000个,即1ms,但此时并未发生溢出,因此也没有触发中断。而是在下一个计数后才发生。确切值应为1001us。若初值为64536(FC18),则恰好为所需值,所以上例中的初值应该用FC18而不是FC17。这仅仅是我自己的一点看法,至于是不是这样,还有待进一步考证。

--------------------------------------------------------------------------------
最终下载到实验板上结果:

######################################补充########################################

用Proteus仿真结果如下(某一状态的截图):

★ 该电路段码是按与板上接法对应的,即按前面的段码表次序连接。另外这个八位的仿真数码管最左端是第一位,最右端是第八位,与板上的顺序相反,所以接为了统一,该图以板为准连接。上图不加上拉电阻也可仿真出结果,只是P0口高电平显示为灰,即高阻。

时间延迟的作用,其实有些程序中可以直接省略,这是因为延时会导致数码管扫屏频率降低,延时稍长便会导致人眼可识别的闪动。

用于控制每个数码管的发光时间。

延时


如何实现LED数码管的动态扫描显示?
步骤一:布局设计 连接P2口至一个高效的译码器,它如同魔术师的手指,通过精准的位码转换,为每个数码管带来动态的生命力。关键在于采用扫描显示技术,每秒一次的脉冲,如同时间的沙漏,稳定而有序。核心策略:扫描原理 扫描时,要确保位码从左到右依次点亮,就像电影中的逐帧动画,每一次移动都带来新的...

基于单片机的数码管动态扫描显示问题
P0=~table[num1];\/\/显示第一个数码管 P2=0x01;\/\/0001 0000 delay(8000);P0=~table[num2];\/\/显示第二个数码管 P2=0x02;\/\/0010 0000 delay(8000);P0=~table[num3];\/\/显示第三个数码管 P2=0x04;\/\/0100 0000 delay(8000);P0=~table[num4];\/\/显示第四个数码管 P2=0x08;\/\/100...

51单片机在动态扫描点亮数码管的时候,出现如果扫描的时间过短的话,会...
重影主要没做好消隐 动态显示的步骤是:开位、送段码(这2个顺序有时要对调)、延时1~5ms、关位,再下一位。。。这样就不会有重影了。

试编写程序:8位数码管动态显示数字AbcdEFgH
这是参考程序,把dofly_DuanMa[]里面的值改成a、b、c、d、e、f、g、h的对应段码值就行,然后位选信号看下你的连接电路是怎么接的。希望采纳,有疑问请追问。include<reg52.h> define DataPort P0 \/\/定义数据端口 程序中遇到DataPort 则用P0 替换 sbit LATCH1=P2^2;\/\/定义锁存使能端口 段...

51单片机动态扫描24个数码管,亮度不足,如何解决?用了138和573._百度知 ...
是138在做动态扫描吧,我没怎么用138,但有一点是肯定的,就是一般情况下应该是138的电流驱动能力有限导致你所说的问题。如果你多用几个573,每个数码管对应一个573来装段码,然后再将数码管的公共极直接接地或电源(看你的数码管是共阳还是共阴来决定接地还是接电源)。这样能够解决,但电路非常复杂。...

数码管的段选和位选是什么意思???
数码管的位选和段选有一定的差异,位选总的来说又是选中它的位置,确定这个位置是否联通,而段选就是选择这个位置,是否点亮因为他是关系到最终显示结果的。顿选是显示它应该显示什么样的数字。未选,总的来说就是选择你要联通哪个数码管儿,联通了之后你才能去进行段选的相应工作,所以通俗1点就是...

数码管动态显示的时候不该亮的地方也亮了,比该亮的地方暗一点,是怎么...
解释:当你显示第一个数码管,这时段码和位码都是开启的,当显示完第一个数码管,由于段码连在一起,没有关闭段码,这时开启第二位位码,在交替的瞬间第二个数码管上就显示第一个的数字,于是就出现第一位的暗影,假如第一位是2,第二位是1,这样显示1的数字不该亮的部分出现了2的暗亮,如果多个数码管扫描的快,...

proteus仿真郭天祥电路,实现动态扫描数码管并输出稳定的123456,为什么...
数码管显示哪用写那么复杂,主程序代码写几行即可。至于Proteus仿真数码管,有可能是仿真不出来的。

四位共阴数码管动态扫描显示编程
define led_wof 1 \/\/数码管位选无效电平 define led_num 4 \/\/数码管位数 \/*引脚定义*\/ define led_d P0 \/\/定义数码管段选GPIO sbit led_w0=P3^2; \/\/位选1 sbit led_w1=P3^3; \/\/位选2 sbit led_w2=P3^4; \/\/位选3 n sbit led_w3=P3^5; \/\/位选4 \/*段码数组*\/...

51单片机 C语言编程 1位数码管动态扫描显示变化数据程序求教,先行谢过...
这是一段可以8位数码管显示的较为通用程序,你现在只有一位数码管,所以改为1时可行,如果是多位的话,你改1就有问题出来了。

郸城县17729382540: 数码管动态扫描中的delay 函数有什么作用? -
宾柿通用: 该实验用到实验板的资源电路图如下:其中P0口是段码,低电平有效.P2口是位码,高电平有效.P2.0口控制第1个数码管,一直到P2.7口控制第8个.该板的段码表如下:各个数码管的段码都是p0口的输出,即各个数码管输入的段码都是一...

郸城县17729382540: 51单片机动态数码管扫描 -
宾柿通用: 问题在 P0=tab[(x%100)]; 改成 P0=tab[(x%10)];

郸城县17729382540: 单片机的一段关于数码管显示的函数问题,中间少了一段delay的定义,我不知道该如何定义,求大神指导 -
宾柿通用: 把这个延时函数加在主函数之前,就可以了. void delay(uchar z) {uchar x, y;for(x=z;x>0;x--)for(y=115;y>0;y--); }

郸城县17729382540: 单片机数码管动态扫描 -
宾柿通用: 你的延时函数时间太短了 下面说工作流程 第一个管子的位选打开,选中第一个管,关位选,段选打开,送数据,关段选 延时delay(5) (注意这里,人的眼睛有视觉暂留,大约0.1-0.4秒,如果你延时很短,这两个数码管分先后,第一个亮,然后第二个亮,循环往复,但是你的眼睛分辨不出来,就感觉它俩是一直亮,你可以把延时加的长点,慢慢试试,你就知道其中的含义了) 第二个管子的位选打开,选中第二个管,关位选,段选打开,送数据,关段选

郸城县17729382540: 有关数码管的动态显示求助,大家帮帮忙
宾柿通用: #include &lt;reg51.h&gt; #include &lt;intrins.h&gt; void delay(); //函数声名 //此表为 LED 的字模, 共阴数码管 0-9 - unsigned char code Disp_Tab[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40}; //段码控制 //此表为8个数码管位...

郸城县17729382540: 单片机共阴极两位数码管动态显示15秒倒计时 -
宾柿通用: 没有具体的原理图,所以就从原理上说明一下吧,首先应该先根据原理图推出每个数码管每个数字对应的8位驱动信号类型,既然是共阴极,那么如果给数码管对应的引脚置1,就会让该位发光,这样就可以推出从0到9的数码管驱动序列,如果是...

郸城县17729382540: 关于51单片机60秒倒计时的c语言程序,delay和dispiay部分分别什么意思啊,具体点 -
宾柿通用: delay(int t)这个是延时子程序,如果采用20M晶振的话,调用一次延时t毫秒. display()是数码管动态扫描显示子程序,位控位为P3.0、P3.1,段码控制为P1.扫描间隔时间为5毫秒左右.

郸城县17729382540: 我在学AVR单片机在遇到数码管动态显示实验时,里面要有个Delay延时,为什么要延时啊 -
宾柿通用: 动态显示就是利用人的视觉暂留的假象,你延时长了也就是闪烁频率低了,低到一定程度,你的眼睛就能看到闪烁了

郸城县17729382540: 单片机C语言两个数码管按键计数程序 -
宾柿通用: 12345678910111213141516171819202122232425262728293031323334353637 #include<reg52.h>#define uchar unsigned char#define uint unsigned int uchar code table[]= { 0x3f,0x06,0x5b,0x4f,0x66, 0x6d,0x7d,0x07,0x7f,0x6f,}; sbit key1=P3^...

郸城县17729382540: 怎么用四位数码管显示一个不断更新的数字,想用不断扫描的方式.求C语言. -
宾柿通用: void diaplay(uint N) { uchar n1,n2,n3,n4; n1=N/1000; n2=N/100%10; n3=N/10%10; n4=N%10; P1_0=0; //P1_0 P1_1 P1_2 P1_3分别为四位的位选,P0为断选, P0=table[n1]; //table为数码管八段编码的数组,0到9编码 delay(); //自己要加的延时函数 P1_0=1; P1_1=0; P0=table[n2]; delay(); P1_1=1; P1_2=0; P0=table[n3]; delay(); P1_2=1; P1_3=0; P0=table[n4]; delay(); P1_3=1; }

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网