问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

什么是数码管动态显示

发布网友 发布时间:2022-04-20 15:01

我来回答

1个回答

热心网友 时间:2023-07-05 15:49

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

  其中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口高电平显示为灰,即高阻。

参考资料:http://www.programfan.com/blog/article.asp?id=12535

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
华硕笔记本电脑触摸板怎么开笔记本电脑触摸板怎么开启和关闭_百度知 ... 陕西职务侵占案立案准则 结婚后我的恋情维系了十年,怎么做到的? 玉米仁子饭产自哪里 中国期货交易所的交易品种有哪些? 历史要怎么读,有啥诀窍 高中历史诀窍 年终会活动策划方案 深度解析:第一财经回放,探索财经新风向 逆水寒手游庄园怎么邀请好友同住 什么是LED数码管静态扫描和动态扫描.简述LED数码管... LED 动态数码管显示? 单片机 汇编 LED数码管动态显示 1,led数码管静态显示和动态显示有什么不同 采用动态方式控制4位LED数码管显示时,对4位数码管... 简述LED数码管动态扫描的原理及其实现方式 单片机驱动LED数码管有哪几种显示方法 4位共阳极led数码管动态显示驱动原理? 请教51汇编之LED数码管动态显示方法 在单片机应用系统中。LED数码管显示电路通常有什么... 数码管动态显示工作原理 LED的静态显示方式与动态显示方式有何区别? LED的静态显示方式与动态显示方式有何区别 简述LED数码管动态,扫描的原理及其实现方式? 什么是LED数码显示中的静态、动态显示技术?各自特... 印度总统是谁? 是谁发明的火狐浏览器? 火狐浏览器好用吗、 火狐浏览器无法使用 火狐浏览器无法进入网页 为什么数码管一般采用动态显示 用AT89C51单片机控制两位8段LED数码管采用动态显示... LED数码管有哪些种类 LED数码管的驱动方式 印度总统 苹果6美版有铆钉版么 波司登X高缇耶设计师联名款哪款最好看? 有人用过春缇舍氧元素去过疤痕吗? 王晶拍戏时怒骂张敏,被暴打后缝了15针,为啥张敏... vivo手机怎样关掉分屏 张敏的成功离不开三位男人,除了周星驰外,另两位... 向华胜爱了张敏9年,为她与导演王晶对立,他后来怎... vivo x21分屏微信锁怎么去掉? 张敏的简介 vivo+s6分屏密码锁怎么解 如何评价张敏? vivo怎么退出分屏 张敏现在怎么样了? vivox50分屏模式怎么关闭 张敏有什么作品?