找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 866|回复: 34
收起左侧

51单片机四位数码管计时器代码有严重的重影?

  [复制链接]
ID:1100331 发表于 2023-11-19 11:41 | 显示全部楼层 |阅读模式
遇到的问题是:
  • 仿真结果运行异常,数码管不能正常显示。 {4DF00D81-3762-4d1c-9500-B839E8A17688}.png
  • 烧录到单片机上之后,显示结果与仿真也不一致。如图: 1700364901528.jpg


其实昨天写代码,仿真结果是正常的,但是有个很严重的问题:烧录到单片机之后:如果四位数码管全部显示,那么会有严重的重影,每一小段数码管几乎都在亮,我尝试调高延时,结果就是有很严重的闪烁,人眼看着很不舒服。 如果只显示两位,即秒的部分,那么则不会出现上述问题,所以今天我尝试重新修改代码,但是却连仿真都不正常了,希望各位大神不吝赐教,谢谢。

单片机源程序如下:
  1. #include "reg51.h"
  2. #include <intrins.h>
  3. #define uchar unsigned char
  4. #define uint unsigned int
  5. #define DIGIT_O P0//设置P0位数码管数据输出端

  6. //定义时分秒变量
  7. uchar timer,seconds,minutes,hours;        
  8. uchar timer,seconds,minutes,hours = 0;

  9. //共阳极数字码表
  10. uchar code segCode[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

  11. //定时器中断服务程序
  12. void timer0_ISR() interrupt 1 {
  13.     TH0 = 0xee;   // 设置定时初值,一次中断为5ms
  14.     TL0 = 0x00;
  15.         timer++;
  16.         if(timer==20){                  //到达1s
  17.             seconds++;
  18.             if (seconds == 60) {
  19.                 seconds = 0;
  20.                 minutes++;
  21.         if (minutes == 60) {
  22.             minutes = 0;
  23.             hours++;
  24.         if (hours == 24) {
  25.                 hours = 0;
  26.                     }
  27.                 }
  28.             }
  29.         }        
  30. }
  31. void delay(uint x)   //延时xms
  32. {
  33.         uint i,j;
  34.         for(i=x;i>0;i--)
  35.                 for(j=114;j>0;j--);
  36. }

  37. //初始化硬件
  38. void init()
  39. {
  40.     TMOD = 0x01;  // 定时器0工作在模式1(16位定时器)
  41.     TH0 = 0xee;   // 设置定时初值
  42.     TL0 = 0x00;
  43.     ET0 = 1;      // 允许定时器0中断
  44.     EA = 1;       // 允许总中断
  45.     TR0 = 1;      // 启动定时器0
  46.         seconds=0;
  47.         minutes=0;
  48.         hours=0;   
  49. }
  50. // 显示时间
  51. void display_time()
  52. {
  53.         DIGIT_O=segCode[seconds/10];
  54.         P3 = 0x01;
  55.         delay(5);

  56.         DIGIT_O=segCode[seconds%10];
  57.         P3 = 0x02;
  58.         delay(5);


  59.         DIGIT_O=segCode[minutes/10];
  60.         P3 = 0x04;
  61.         delay(5);


  62.         DIGIT_O=segCode[minutes%10];
  63.         P3 = 0x08;
  64.         delay(5);

  65.         DIGIT_O=0XFF;

  66. }


  67. void main()
  68. {
  69.         init();
  70.         while(1)
  71.         {        
  72.         display_time();        
  73.    
  74.         }
  75. }
复制代码

经过修改后的仿真正常: {CD93B379-5209-420f-A14D-7EC65657409A}.png 但是烧录后数码管已然异常: IMG_20231119_184138.jpg


回复

使用道具 举报

ID:1100331 发表于 2023-11-20 18:44 | 显示全部楼层
经过两天的努力,所有的问题终于都找出来了,再次感谢所有朋友的热心帮助,你们分享的话语让我受益匪浅,现将这次数字钟实践中遇到的所有问题罗列如下:
1.没有消隐。应当遵循 消隐-段选-位选-延时的逻辑
2.位选端的正反逻辑搞错了。之前使用proteus仿真过程中,数码管位选为高电平有效,但是今天中午通过拔插位选线各种调试后,发现板子上的这块数码管是低电平有效....
3.最最最严重的问题:这块板子的P3口完全是反着来的!我是怎么发现这个问题的,详见23楼。这块板子上标的是P3.0,而实际是P3.7;标的是P3.7,实际上是P3.0....因为这个问题,我之前“以为”的P3.0-3.3一直都是输出高电平,也就是所有位选都生效....在这里痛斥学校的采购
我是因为这个问题才注册的论坛,切身感受到了朋友们热情和渊博,再次感谢
7bdc7fd7df47244d50c91aad23687754.jpg
回复

使用道具 举报

ID:332444 发表于 2023-11-19 16:09 | 显示全部楼层
每个数码管显示前都要消隐,是每个。
回复

使用道具 举报

ID:883242 发表于 2023-11-19 16:37 | 显示全部楼层
在每条
        DIGIT_O=   ;
前面加上
        P3 = 0x0;
试试。
回复

使用道具 举报

ID:1064915 发表于 2023-11-19 17:22 | 显示全部楼层
DIGIT_O=0XFF;
再复制3个
回复

使用道具 举报

ID:213173 发表于 2023-11-19 17:57 | 显示全部楼层
核心问题是动态扫描数码管没有消隐。
  1. #include "reg51.h"
  2. #include <intrins.h>
  3. #define uchar unsigned char
  4. #define uint unsigned int
  5. #define DIGIT_O P0//设置P0位数码管数据输出端

  6. //定义时分秒变量
  7. uchar timer,seconds,minutes,hours;        
  8. uchar timer,seconds,minutes,hours = 0;

  9. //共阳极数字码表
  10. uchar code segCode[]={
  11.                 0xc0,0xf9,0xa4,0xb0,
  12.                 0x99,0x92,0x82,0xf8,
  13.                 0x80,0x90,0x88,0x83,
  14.                 0xc6,0xa1,0x86,0x8e};
  15. //定时器中断服务程序
  16. void timer0_ISR() interrupt 1
  17. {
  18.         TH0 = 0xee;   // 设置定时初值,一次中断为5ms
  19.         TL0 = 0x00;
  20.         timer++;
  21.         if(timer==200) //到达1s
  22.         {            
  23.                 timer=0;
  24.                 seconds++;
  25.                 if (seconds == 60)
  26.                 {
  27.                         seconds = 0;
  28.                         minutes++;
  29.                         if (minutes == 60)
  30.                         {
  31.                                 minutes = 0;
  32.                                 hours++;
  33.                                 if (hours == 24)
  34.                                 {
  35.                                         hours = 0;
  36.                                 }
  37.                         }
  38.                 }
  39.         }        
  40. }
  41. void delay(uint x)   //延时xms
  42. {
  43.         uint i,j;
  44.         for(i=x;i>0;i--)
  45.                 for(j=114;j>0;j--);
  46. }

  47. //初始化硬件
  48. void init()
  49. {
  50.         TMOD = 0x01;  // 定时器0工作在模式1(16位定时器)
  51.         TH0 = 0xee;   // 设置定时初值
  52.         TL0 = 0x00;
  53.         ET0 = 1;      // 允许定时器0中断
  54.         EA = 1;       // 允许总中断
  55.         TR0 = 1;      // 启动定时器0
  56.         seconds=0;
  57.         minutes=0;
  58.         hours=0;   
  59. }
  60. // 显示时间
  61. void display_time()
  62. {
  63.         static uchar data i;
  64.         P3&=0xf0;//消隐
  65.         switch(i)
  66.         {
  67.                 case 0: DIGIT_O=segCode[minutes/10]; break;
  68.                 case 1: DIGIT_O=segCode[minutes%10]&0x7f; break;//加点
  69.                 case 2: DIGIT_O=segCode[seconds/10]; break;
  70.                 case 3: DIGIT_O=segCode[seconds%10]; break;
  71.         }
  72.         P3|=0x01<<i;
  73.         i=++i%4;
  74. }


  75. void main()
  76. {
  77.         init();
  78.         while(1)
  79.         {        
  80.                 display_time();        
  81.                 delay(1);
  82.         }
  83. }
复制代码
回复

使用道具 举报

ID:1100331 发表于 2023-11-19 18:26 | 显示全部楼层
xianfajushi 发表于 2023-11-19 16:09
每个数码管显示前都要消隐,是每个。

谢谢,我试了试后仿真没有问题,但是烧到板子上仍然有重影,不知道是哪的问题了现在(苦),头大
回复

使用道具 举报

ID:1100331 发表于 2023-11-19 18:28 | 显示全部楼层
Hephaestus 发表于 2023-11-19 16:37
在每条
        DIGIT_O=   ;
前面加上

谢谢,我这样试了。仿真恢复正常,但是板子仍然有重影好无奈啊
回复

使用道具 举报

ID:1100331 发表于 2023-11-19 18:44 | 显示全部楼层
谢谢大家的回复,我重新在每个数码管显示前都添加了消隐(禁用位选,输出空段码两种方式都尝试了)
仿真结果均正常,但是烧录到机子里仍然有重影
回复

使用道具 举报

ID:1100331 发表于 2023-11-19 19:12 | 显示全部楼层
为了深究原因,我尝试把秒的位选代码删去,只保留分钟。发现一个奇怪的现象:在仿真里正常,高两位数码管显示分钟,低两位不显示。但是烧录到板子后:高两位显示分钟,低两位却也有显示,并且显示的是高两位的重叠。啊啊啊头大
{16BD196F-7A3B-41e3-9680-5BD1BEDF8D9C}.png
IMG_20231119_190852.jpg
IMG_20231119_191117.jpg
回复

使用道具 举报

ID:36322 发表于 2023-11-19 19:26 | 显示全部楼层

P3 = 0x00;
DIGIT_O=segCode[seconds/10];
P3 = 0x01;
delay(5);

延时可适当加长
回复

使用道具 举报

ID:275826 发表于 2023-11-19 19:49 | 显示全部楼层
板子与仿真原理图肯定不一样才这样
回复

使用道具 举报

ID:397054 发表于 2023-11-19 19:54 | 显示全部楼层
把这行语句多看几遍,直到看出问题来:

if(timer==20){                  //到达1s
回复

使用道具 举报

ID:397054 发表于 2023-11-19 19:58 | 显示全部楼层
消隐并不重要,display_time函数中没有最后一行也不致引起很大的影响,几乎看不出来。
回复

使用道具 举报

ID:397054 发表于 2023-11-19 20:30 | 显示全部楼层
我根本就没法理解你的叙述,牛头不对马嘴的,数码管如何重影呢,它要么亮要么就不亮,总是清晰的,它就是个普通物体怎么个重影法,,,,但根据你的程序看,我知道你说的“重影”是什么意思,只是用这个词实在是太词不达意了;而且另一个现象更加不可能,【如果只显示两位,即秒的部分,那么则不会出现上述问题,】——这根本不可能,4位都都显示时,决不会4位都“重影”,左面两位不“重影”,右面的两位才“重影”,右面的是秒,而你偏偏说秒正常,这完全不可能 。。。。。
回复

使用道具 举报

ID:213173 发表于 2023-11-19 21:12 | 显示全部楼层
小小小初学者 发表于 2023-11-19 19:12
为了深究原因,我尝试把秒的位选代码删去,只保留分钟。发现一个奇怪的现象:在仿真里正常,高两位数码管显 ...

无标题.jpg
回复

使用道具 举报

ID:1100331 发表于 2023-11-19 21:41 | 显示全部楼层
鹈鹕 发表于 2023-11-19 20:30
我根本就没法理解你的叙述,牛头不对马嘴的,数码管如何重影呢,它要么亮要么就不亮,总是清晰的,它就是个 ...

你好,谢谢你的回复,首先我很抱歉我的叙述可能有些混乱,因为这东西搞得我有些焦头烂额。接下来我来回复你的质疑:
1.重影或者说残影,这个是我最原本代码确实存在的问题,因为没有消影。这个问题在大家的帮助下解决了。现在在仿真上是一切正常的。
2.我在1楼说的现象是真实的,只不过造成那个现象的代码和我在9楼说的现象所使用的代码相比,做了很多修改,两者代码不一样,现象也不一样,不是一回事。
现在的问题是,即时添加了消影,数码管仍然不能正确显示(大部分时间是8,9这种多个段接通的数字)。
我尝试在代码中删除显示分钟的,保留显示秒钟的,那么烧录到机子后,将有这样的现象:显示秒钟的两位正常显示,分钟位也显示(代码中已经删去显示分钟了),并且显示的是秒钟两位个位和十位的叠加
  1. void display_time()
  2. {

  3.         P3=0X00;
  4.         DIGIT_O=segCode[seconds/10];
  5.         P3 = 0x04;
  6.         delay(5);

  7.         P3=0X00;
  8.         DIGIT_O=segCode[seconds%10];
  9.         P3 = 0x08;
  10.         delay(5);
  11. }
复制代码
回复

使用道具 举报

ID:275826 发表于 2023-11-20 08:31 | 显示全部楼层
小小小初学者 发表于 2023-11-19 21:41
你好,谢谢你的回复,首先我很抱歉我的叙述可能有些混乱,因为这东西搞得我有些焦头烂额。接下来我来回复 ...

你这个消隐程序错了,改成P3=0xff,你应该把电路板原理图发上来才能解决下载后的问题
回复

使用道具 举报

ID:1073939 发表于 2023-11-20 09:09 | 显示全部楼层
仿真结果和实验结果应该相差不大。
这种现象表示仿真模型和实验硬件不符。
回复

使用道具 举报

ID:123289 发表于 2023-11-20 09:32 | 显示全部楼层
"扫屏显示的切换应当用最少的指令完成,在不多于8个LED的情况下:三条指令即可完成,对于STC的单片机,10us不到就完成了,例:8个LED哪个亮(位选)受P1控制,显示什么内容(段码)由P0决定。
要点:技巧如下!!!
先将哪个LED需要亮的参数事先准备好,放在:P1_OUT变量中,同样将这个LED需要显示的段码放在P0_OUT中。【这个做法最关键,不要直接切换操作P0、P1口,而是通过这两个变量来操作。原因是,无论你用多长时间对P1_OUT、P0_OUT进行操作,操作期间,显示不受影响。】
切换:
第一条指令:关断所有显示:P1=FFH              ;关闭所有显示
第二条指令:切换LED段码:P0=P0_OUT         ;这时显示已关,切换不会有残留,重影,闪烁了。
第三条指令:更新显示的LED管:P1=P1_OUT  ;点亮需要显示的LED,例如:11101111B
【这个做法是最优秀的扫描显示切换方法,用时最短,最合理,没有之二!!!】
重复一下关键点:事先处理准备好变量P1_OUT、P0_OUT的值。"
回复

使用道具 举报

ID:332444 发表于 2023-11-20 09:39 | 显示全部楼层
数码管动态驱动是最基本的,它的原则:消隐-送段值-送位值-延时,按这样的顺序执行和编写代码.
看仿真图片数码管应该是共阳,我这里有个简单的荔枝,显示固定的日期小数点分割,修改了一下,仿真和下载开发板试看.
  1. #include "reg52.h"
  2. unsigned char code SmZiFu[]={63,6,91,79,102,109,125,7,127,111,128,0};//共阴0-9.
  3. void main()
  4. {
  5.         unsigned char sy=0,my=0,ls=0;
  6.         while(1)
  7.         {
  8.                 if(++sy==0)
  9.                 {
  10.                         P0=255;P3=0;
  11.                         if(ls==0||ls==8)ls=1;else ls*=2;//控制4位数码管显示
  12.                         P0=255-(SmZiFu[ls==1?2
  13.                         :ls==2?0
  14.                         :ls==4?2
  15.                         :ls==8?3
  16.                         :ls==16?1
  17.                         :ls==32?1
  18.                         :ls==64?0
  19.                         :ls==128?7
  20.                         :11]+(ls==8||ls==32?SmZiFu[10]:0));
  21.                         P3=ls;
  22.                 }
  23.         }
  24. }
复制代码



回复

使用道具 举报

ID:272119 发表于 2023-11-20 10:10 | 显示全部楼层
应该很好调试的,我没有仔细看楼主的程序只看了图片,如果不是消隐反向的话,建议楼主先不要忙那么多位显示,首先调试最后一位显示1->9正常后辐射4位就简单的多,如果连一位都不能正常显示那仔细磨驱动代码吧.
回复

使用道具 举报

ID:332444 发表于 2023-11-20 10:39 | 显示全部楼层
忘记打括号了,加的要括号,或者删除+(ls==8||ls==32?SmZiFu[10]:0)加小数点,另外6位的话删除最后2位即64和128,修改条件 if(ls==0||ls==32)
回复

使用道具 举报

ID:1100331 发表于 2023-11-20 13:26 | 显示全部楼层
谢谢各位的回复,学业繁忙,我等晚上或者明天再一一回复。不过目前我越来越感觉问题不是代码,而是板子。因为我只保留输出秒个位的代码后,在调试中,拔掉了四个位选线。接下来按道理,其中只有一个位选线能让对应的数码管闪亮,但实际情况是:每条位选线都能让数码管闪亮
这可能意味着位选线4个端总是0xff!!(即让数码管生效)
我换了我两个舍友的板子重新烧,结果是一样的,感觉同时焊错的可能性比较小....

回复

使用道具 举报

ID:1100331 发表于 2023-11-20 13:30 | 显示全部楼层
这个是老师发的原理图,单片机型号是STC 89C51RC

DIY_MCU_51_REV1.0(2015.11.06)原理图.pdf

1.08 MB, 下载次数: 3

回复

使用道具 举报

ID:1073939 发表于 2023-11-20 14:00 | 显示全部楼层
小小小初学者 发表于 2023-11-20 13:26
谢谢各位的回复,学业繁忙,我等晚上或者明天再一一回复。不过目前我越来越感觉问题不是代码,而是板子。因 ...

把板子的原理图发出来。
回复

使用道具 举报

ID:57657 发表于 2023-11-20 14:18 | 显示全部楼层
51单片机8位并行IO口一组,段码和位码不是并行发送的,所以需要消隐,16位以上不需要,新的位码送出去了,段码还是旧的,所以就出现影子了。
回复

使用道具 举报

ID:34149 发表于 2023-11-20 15:17 | 显示全部楼层
你的位驱动是I0口直接驱动吗?
没有原理图,确实挺难帮你分析的
回复

使用道具 举报

ID:397054 发表于 2023-11-20 15:53 | 显示全部楼层
你这个程序实际上是两个错误,第一个是我12楼说的那个,第二个我昨天晚上没看出来。第一个错误会导致这个计时器快10倍,我以为你所说的“重影”实际是走的快引起来显示8的原因,但早上起来我想想即使快10倍也不至于引起“重影”现象,即充其量秒的个位会因为变化快看起来像8,其他的就不会了,秒的10位肯定看得清楚因为快10倍也就是1秒一变,分钟就更加不会了。中午吃完饭没事就把你的程序复制下来稍加修改适合我的板子,上电一看果然4个8字,但凭经验看明显的不是因为跳变快引起的,这跟我前一段时间碰到的一个故障非常像,因为有这个经验仔细看了一下你的有关显示的代码,看出了问题所在,跟我的那个故障的原因虽然不完全一样,但现象确实会很像——是你把显示掩码(显示掩码,很多书上称为“位码”)的正负逻辑弄颠倒了,你的显示掩码电路肯定是负逻辑,一般的都是,而你把它当正逻辑来处理了,即display_time()函数中P3=,,,那4行右面是正逻辑01、02、04、08,这样该亮的不亮不该亮的3个同时亮,这么个扫描循环当然就乱七八糟了,所以正确的数值应该是这4个数的反码才对,或者你把他们的反码替代他们比如FE替代01等或者懒得算的话就在每个数字前面加个非号“~”即可。

之所以两个数码管时正常,那是假象,是因为只有两个数码管,它仍然遵循该亮的不亮其它都亮的规则,但这时的“其它”只有1个了,所以看上去就正常了,但这时,两位是颠倒的,即秒的10位显示的是个位数值,而个位显示的是十位。

第一个错误也要改否则的话根本就谈不上准确,且错误还不止那一句,虽然都是同一个错误。
回复

使用道具 举报

ID:1100331 发表于 2023-11-20 18:46 | 显示全部楼层
tyrl800 发表于 2023-11-19 19:49
板子与仿真原理图肯定不一样才这样

谢谢你的回复,我已经解决了这个问题。板子与仿真原理图不一致确实是问题之一
回复

使用道具 举报

ID:1100331 发表于 2023-11-20 18:48 | 显示全部楼层
tyrl800 发表于 2023-11-20 08:31
你这个消隐程序错了,改成P3=0xff,你应该把电路板原理图发上来才能解决下载后的问题

谢谢你的回复,消隐程序正反逻辑颠倒确实是问题之一
回复

使用道具 举报

ID:1100331 发表于 2023-11-20 18:51 | 显示全部楼层
ydatou 发表于 2023-11-20 09:09
仿真结果和实验结果应该相差不大。
这种现象表示仿真模型和实验硬件不符。

问题已经解决了,您说得真对,确实是硬件有一些问题没注意到或者是没法注意到(哭笑)
回复

使用道具 举报

ID:1100331 发表于 2023-11-20 18:54 | 显示全部楼层
yzwzfyz 发表于 2023-11-20 09:32
"扫屏显示的切换应当用最少的指令完成,在不多于8个LED的情况下:三条指令即可完成,对于STC的单片机,10us ...

谢谢你的分享。你讲的这个技巧看起来很惊艳,我会下来仔细琢磨并实践的
回复

使用道具 举报

ID:1100331 发表于 2023-11-21 00:06 | 显示全部楼层
鹈鹕 发表于 2023-11-20 15:53
你这个程序实际上是两个错误,第一个是我12楼说的那个,第二个我昨天晚上没看出来。第一个错误会导致这个计 ...

谢谢你,目前所有的问题已经解决。确实有位选码(显示掩码)逻辑颠倒的问题。详情见我最新的置顶楼。
另外,我的中断函数里除了”timer==20“这一句有错误,也忘了令timer=0;所以后来无论怎么改timer都不影响速度(因为都是从0到255再到0)(哭笑)
回复

使用道具 举报

ID:332444 发表于 2023-11-21 10:46 | 显示全部楼层
借助这帖就说说阻塞与查询延时的转换,把代码if(++sy==0)修改while(++sy)就是阻塞延时,阻塞延时放前后都可以.
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表