多位数码管动态显示.docx

上传人:b****8 文档编号:9917641 上传时间:2023-02-07 格式:DOCX 页数:14 大小:92.90KB
下载 相关 举报
多位数码管动态显示.docx_第1页
第1页 / 共14页
多位数码管动态显示.docx_第2页
第2页 / 共14页
多位数码管动态显示.docx_第3页
第3页 / 共14页
多位数码管动态显示.docx_第4页
第4页 / 共14页
多位数码管动态显示.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

多位数码管动态显示.docx

《多位数码管动态显示.docx》由会员分享,可在线阅读,更多相关《多位数码管动态显示.docx(14页珍藏版)》请在冰豆网上搜索。

多位数码管动态显示.docx

多位数码管动态显示

【006】多位数码管动态显示 [51]

点击数:

2102    发布日期:

2006-4-1616:

04:

00  

【收藏】 【评论】 【打印】 【编程爱好者论坛】 【关闭】

 

实验目的:

数码管动态显示多位数字。

实验参考:

笨笨工作室实验五、多位数码动态显示。

(查看原文)

实验板:

FB51A(查看)。

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

其中P0口是段码,低电平有效。

P2口是位码,高电平有效。

P2.0口控制第1个数码管,一直到P2.7口控制第8个。

该板的段码表如下:

    

各个数码管的段码都是p0口的输出,即各个数码管输入的段码都是一样的,为了使其分别显示不同的数字,可采用动态显示的方式,即先只让最低位显示0(含点),经过一段延时,再只让次低位显示1,如此类推。

由视觉暂留,只要我们的延时时间足够短,就能够使得数码的显示看起来非常的稳定清楚。

过程如下图。

           

 

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

      org  0000h

start:

mov  a,#08h      ;0   ;段码

      mov  p0,a

      mov  p2,#01h           ;位码

      lcalldelay_1ms

      mov  a,#0abh     ;1    

      mov  p0,a

      mov  p2,#02h

      lcalldelay_1ms

      mov  a,#12h      ;2

      mov  p0,a

      mov  p2,#04h

      lcalldelay_1ms

      mov  a,#22h      ;3

      mov  p0,a

      mov  p2,#08h

      lcalldelay_1ms

      mov  a,#0a1h     ;4

      mov  p0,a

      mov  p2,#10h

      lcalldelay_1ms

      mov  a,#24h      ;5

      mov  p0,a

      mov  p2,#20h

      lcalldelay_1ms

      mov  a,#04h      ;6

      mov  p0,a

      mov  p2,#40h

      lcalldelay_1ms

;     mov  a,#0aah     ;7

;     mov  p0,a

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

      mov  p2,#80h

      lcalldelay_1ms

      ljmp start

delay_1ms:

mov r6,#2

temp:

     mov r5,#0ffh

          djnzr5,$

          djnzr6,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:

 lcallplay1       ;调用显示段码子程序

      lcallplay2       ;调用显示位码子程序

      lcalldelay_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:

db08h,0abh,12h,22h,0a1h,24h,04h,0aah ;段码表

table2:

db01h,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

#include            //包含了左移函数_crol_()

voiddelayms(unsignedcharms);//延时子程序

unsignedchardatadis_digit;  //位选通值,传送到P2口用于选通当前数码管的数值,

                               //如等于0x01时,选通P2.0口数码管

unsignedcharcodedis_code[11]={0x08,0xab,0x12,0x22,0xa1,       //0,1,2,3,4

                                0x24,0x04,0xaa,0x00,0x20,0xff};//5,6,7,8,9,off

unsignedchardatadis_buf[8]; //dis_buf显于缓冲区基地址

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

voidmain()

{

   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;               //见注释

   }

}

voiddelayms(unsignedcharms)            //延时子程序(晶振12M)

{                       

   unsignedchari;

   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(250),下载到板上就可以看到实际上数码管是由低位到高位逐位显示的。

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

#include

#include           //_crol_()用

voiddelayms(unsignedcharms);//延时子程序

unsignedchardatadis_digit;  //位选通值,传送到P2口用于选通当前数码管的数值,

                               //如等于0x01时,选通P2.0口数码管

unsignedcharcodedis_code[11]={0x08,0xab,0x12,0x22,0xa1,       //0,1,2,3,4

                                0x24,0x04,0xaa,0x00,0x20,0xff};//5,6,7,8,9,off

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

voidmain()

{

   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;

   }

}

voiddelayms(unsignedcharms) //延时子程序(晶振12M)

{                       

   unsignedchari;

   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       ……

幂次运算函数flaotpow(floatx,floaty)包含在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

#include           //包含了左移函数_crol_()

unsignedchardatadis_digit;  //位选通值,传送到P2口用于选通当前数码管的数值,

                               //如等于0x01时,选通P2.0口数码管

unsignedcharcodedis_code[11]={0x08,0xab,0x12,0x22,0xa1,       //0,1,2,3,4

                                0x24,0x04,0xaa,0x00,0x20,0xff};//5,6,7,8,9,off

unsignedchardatadis_buf[8]; //dis_buf显于缓冲区基地址

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

voidmain()

{

   P0=0xff;   //关闭所有数码管

   P2=0x00;

   TMOD=0x01; //00000001B定时计数器0工作在方式1,16位定时器/计数器

   TH0=0xFC;     

   TL0=0x17;  //预置初值FC17H=64535D,216-64535=1001us=1ms

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

   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);  //循环等待中断

}

voidtimer0()interrupt1 //定时器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口高电平显示为灰,即高阻。

引用地址:

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1