微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > MCU和单片机设计讨论 > 键盘输入大于65536的数,数码管显示数与65536的差

键盘输入大于65536的数,数码管显示数与65536的差

时间:10-02 整理:3721RD 点击:
各位大神,请教个事情,具体看下面:
1,我通过键盘(0-9)和一个确定键(sure)来输入0-99999之间的数据。
2.数据输入完毕按确定键,会存储到24C02里面,每次单片机掉电重新上电时,显示上次输入的数据。
3.我实现了0-255,256-65535的掉电存储和上电显示功能。
4.但是在显示65536-99999这个区间的数据时,每次它只能显示输入数据和65536的差值,比如说,我输入88888,上电后显示的数据233352,这是为什么?
5.起初怀疑是数据溢出,定义的num0(就是键盘输入生成的5位数)不对,就将他的属性有unsinged char改到unsigned long,但还是不行。6.主程序放到下面,请大家检查检查,提提意见。
void main()
{
        ulong num0;
        ulong num1,num2;
        uchar ge,shi,bai,qian,wan;
        uchar add_HH1,add_H1,add_L1;
    add_HH1=read_add(0);//读取24C02中第0位值
        delay0(5);
        add_H1=read_add(1);//读取24C02中第1位值
        delay0(5);
        add_L1=read_add(2);//读取24C02中第2位值
        delay0(5);
        num2=add_HH1*65536+add_H1*256+add_L1;//得到要显示的数
        wan=num2/10000;
        qian=num2%10000/1000;
        bai=num2%1000/100;
        shi=num2%100/10;
        ge=num2%10;
        while(1)
        {
                init();
                display(wan,qian,bai,shi,ge);//数码管显示函数
                keyscan();//键盘扫描函数,每按一次,就数据就多一位
                switch(cishu)
                {
                        case 1:a0=num;b0=0;c0=0;d0=0;e0=0;break;
                        case 2:b0=num;c0=0;d0=0;e0=0;break;
                        case 3:c0=num;d0=0;e0=0;break;
                        case 4:d0=num;e0=0;break;
                        case 5:e0=num;break;               
                }
                if(sure==1)        //确认键按下的时候,就生成该数据,并将该数据拆分成三个字节写到24C02中去
                {
                        switch(cishu)
                        {
                                case 1:num0=a0;break;
                                case 2:num0=a0*10+b0;break;
                                case 3:num0=a0*100+b0*10+c0;break;
                                case 4:num0=a0*1000+b0*100+c0*10+d0;break;
                                case 5:num0=a0*10000+b0*1000+c0*100+d0*10+e0;break;
                        }
                        if(num0>=65536)
                        {                                
                               
                                write_add(0,num0/65536);
                                delay0(5);
                                write_add(1,num0%65536/256);
                                delay0(5);
                                write_add(2,num0%256);
                                delay0(5);
                        }
                        else
                        {
                                write_add(0,0);
                                delay0(5);
                                write_add(1,num0/256);
                                delay0(5);
                                write_add(2,num0%256);
                                delay0(5);
                        }                       
                        cishu=0;
                }
        }
}

你这算法着实令人无语!
虽然你定义的是long型,理论上是没有溢出的,但是单片机得开多大的空间来计算你这算法啊,当单片机开的空间不够用时,就会覆盖先前的空间,使计算出错!(这已经溢出了)
解决的办法就是换一种算法,比如
num2=add_HH1*65536+add_H1*256+add_L1;//得到要显示的数
可以换算为如下:
num2=add_HH1;
num2=num2<<8;
num2 |=add_H1;
num2=num2<<8;
num2 |=add_L1;//得到要显示的数
键盘输入的数就留给小编自己想吧!发挥你的想象力!

这个程序还是有点问题的
如楼上所说,除法和取余对单片机来说效率极为底下,而程序中多为除以2的次幂
除以256可以改为 num>>8, 除以65536可以改为 num>>16,对65536取余数可以改为 num & (0xFFFF) ,类推
其次是程序,按小编的想法,就是最大显示99999,也就是0x1869F,也是3个字节,为什么要经过一堆可能出错的运算才存起来?
假设键盘采到 num0 = 99999;
如果我来存,我一定这样写,不需要判断是否大于65536:
定义一个: unsigned char  temp;
temp = (num0 >> 16 ) & 0xFF
write_add(0,temp );
delay0(5);
temp = (num0 >> 8 ) & 0xFF
write_add(1,temp );
delay0(5);
temp = num0 & 0xFF
write_add(2, temp );
delay0(5);
上电的时候取出来就可以了
        add_HH1=read_add(0);//读取24C02中第0位值
        delay0(5);
         add_H1=read_add(1);//读取24C02中第1位值
        delay0(5);
         add_L1=read_add(2);//读取24C02中第2位值
        delay0(5);
         num2= (add_HH1 << 16) | (add_H1 << 8) | add_L1;//得到要显示的数
        wan=num2/10000;
         qian=num2%10000/1000;
         bai=num2%1000/100;
         shi=num2%100/10;
         ge=num2%10;
         .....
逻辑运算的速度要比算术运算快很多很多,如果你把编译之后的文件反汇编回来你就知道差别了
直接存直接取,简单直接,效率提高

但是,问题应该不是这里,问题在于你的0地址上的数据被覆盖了,就是每次上电那里的数据都是0
所以0-65535是OK的,但是往上就不对了
不知道你其他地方有没有操作0地址,检查一下其他代码,还有init()这个函数是什么意思?怎么在主循环里面,会不会有问题?

你可以验证一下,改为write(1,x),write(2,x),write(3,x)
上电的时候read1,2,3
避开0地址看看

我之前是搞PLC的,对逻辑运算会提高效率我今天终于明白了。
地址1,2,3是我最开始写程序时候用的,也是和现在一样的情况,我当时的想法和你一样,觉得地址1写不进去,我就试了地址0,也是一样。我甚至试了地址10,地址2,和地址3,也是写不进去。
你的头像不错。

在读(add_HH1=read_add(0);//读取24C02中第0位值)时,你应该在之前做相应延时,你不可能一开机就马上读吧!
还有就是算法,你看看我这个:

  1. #include "Counter.h"         //计算器头文件
  2. #include "Keyboard.h"        //键盘控制
  3. #include "LCD1602.h"         //LCD1602显示

  4. uchar code Table_A[]="0123456789";
  5. uchar code Table_B[]="error!";

  6. struct Counter               //定义计算器变量
  7. {
  8.         uchar Flag;                //运算标志
  9.   uchar Dsp[10];             //把计算结果B存到数组
  10.   long A;                    //输入值A
  11.   long B;                    //输入值B,运算后存到B
  12.        
  13. } Count;                     //计算

  14. uchar Set_Counter_Keyboart() //计算器键盘设置
  15. {
  16.   uchar Key_Data;           
  17.        
  18.         Key_Data=Read_Keyboart_4_4();//读取按键值
  19.         switch(Key_Data)               
  20.         {
  21.    case 0xee:return '/';break; ///
  22.          case 0xde:return '*';break; //*
  23.          case 0xbe:return '-';break; //-
  24.          case 0x7e:return '+';break; //+
  25.                                                  
  26.          case 0xed:return 9;break;   //9
  27.          case 0xdd:return 6;break;   //6
  28.          case 0xbd:return 3;break;   //3
  29.          case 0x7d:return '=';break; //=
  30.                                                  
  31.          case 0xeb:return 8;break;   //8
  32.          case 0xdb:return 5;break;   //5
  33.          case 0xbb:return 2;break;   //2
  34.          case 0x7b:return 0;break;   //0
  35.                                                          
  36.          case 0xe7:return 7;break;   //7
  37.          case 0xd7:return 4;break;   //4
  38.          case 0xb7:return 1;break;   //1
  39.          case 0x77:return 'C';break; //清零
  40.                                                  
  41.    default:break;                                          
  42.   }
  43.        
  44.         return Counter_NULL;         
  45. }

  46. uchar Counter_Input()                 //输入A值和B值
  47. {
  48.   uchar Counter_Data;                 //键盘中间变量

  49.   Counter_Data=Set_Counter_Keyboart();//读取键盘值
  50.         if(Counter_Data!=Key_4_4_NULL)
  51.         {
  52.                 switch(Counter_Data)
  53.                 {
  54.       case '+':LCD_Write(1,'+');Count.Flag='+';Count.B=Count.A;Count.A=0;break;
  55.                         case '-':LCD_Write(1,'-');Count.Flag='-';Count.B=Count.A;Count.A=0;break;
  56.                         case '*':LCD_Write(1,'*');Count.Flag='*';Count.B=Count.A;Count.A=0;break;
  57.                         case '/':LCD_Write(1,'/');Count.Flag='/';Count.B=Count.A;Count.A=0;break;
  58.                         case 'C':LCD_Write(1,'C');LCD_Write(0,0x01);Count.A=Count.B=0;break;
  59.                         case '=':LCD_Write(1,'=');return '=';break;
  60.                                
  61.                         default:break;
  62.     }
  63.     if(Counter_Data>=0&&Counter_Data<10)
  64.                 {
  65.                         Count.A=Count.A*10+Counter_Data;    //把输入值累加并存到A中   
  66.                         LCD_Write(1,Table_A[Counter_Data]);
  67.     }       
  68.   }
  69.        
  70.         return Counter_NULL;
  71. }

  72. void Resolve_Data(long dat)     //分解计算结果并保存到数组中
  73. {
  74.   uchar i=0;
  75.         uchar j=0;
  76.         uchar num[10]=0;
  77.        
  78.         while(dat)
  79.         {       
  80.                 num[i]=dat%10;              //一直读取个位数,把计算结果从个位保存到数组中
  81.                 dat /=10;
  82.                 i++;
  83.   }
  84.        
  85.         while(i--)
  86.         {
  87.           Count.Dsp[j++]=num[i]+'0';  //数组从高到底保存结果
  88.   }
  89. }

  90. void Counter_Compute()          //A与B进行相应计算并显示
  91. {
  92.         uchar i=0;
  93.        
  94.         if(Counter_Input()=='=')
  95.         {
  96.          switch(Count.Flag)
  97.          {
  98.     case '+':Count.B+=Count.A;break;
  99.           case '-':Count.B-=Count.A;break;
  100.           case '*':Count.B*=Count.A;break;
  101.           case '/':Count.B/=Count.A;break;
  102.           default:break;
  103.    }  
  104.          Resolve_Data(Count.B);                    //分解计算结果并保存到数组中
  105.          if(Count.B<0) LCD_Coord_Data(0,2,Table_B);//显示错误
  106.          else LCD_Coord_Data(0,2,Count.Dsp);       //显示计算结果
  107.          Count.A=Count.B=0;                        //清零
  108.          for(i=0;i<10;i++)
  109.          {
  110.      Count.Dsp[i]=0;
  111.    }
  112.   }
  113. }

复制代码

你的read_add函数可能有问题,你贴出来瞧瞧,写可能问题不大
因为第一个读的不正确,后面两个是正确的
我猜测,第一个读取的时候置位了读信号,但是信号没有准备好就去读,结果读到异常值
当后面再去读的时候,第一的置位已经完成,对时间没有要求,能正确读到数据

read函数
uchar read()
{
        uchar i,k;
        scl=0;
        delay();
        sda=1;
        delay();
        for(i=0;i<8;i++)
        {
                scl=1;
                delay();       
                k=(k<<1)|sda;
                scl=0;
                delay();       
        }
        return k;
}

read_add函数从固定地址读取
uchar read_add(uchar address)
{
        uchar date;
        start();
        write(0xa0);
        respond();
        write(address);
        respond();
        start();
        write(0xa1);
        respond();
        date=read();
        stop();
        return date;
}

我刚刚又试了,把计算里面的a0,b0,c0,d0,e0这五个数定义成无符号的long型就可以了,但是我不知道这是为什么,应该还是和数据溢出有关的。
下面这个语句是用到a0,b0,c0,d0,e0的地方,之前定义的是无符号的char型的,num0就是键盘输入生成的数据,定义的是无符号long型。
case 5:num0=a0*10000+b0*1000+c0*100+d0*10+e0;break;
我不知道为什么,可以解释一下么?
感谢你们的提醒,我下一步要优化这个算法。

谢谢大师兄。
但是我觉得问题不是出在算法上面,我承认我的算法是有很大的问题(菜鸟就是菜鸟,呵呵),单片机还是能处理我这点运算量的,应该是数据溢出的问题。
我刚刚又试了,把计算里面的a0,b0,c0,d0,e0这五个数定义成无符号的long型就可以了,但是我不知道这是为什么,应该还是和数据溢出有关的。
下面这个语句是用到a0,b0,c0,d0,e0的地方,之前定义的是无符号的char型的,num0就是键盘输入生成的数据,定义的是无符号long型。
case 5:num0=a0*10000+b0*1000+c0*100+d0*10+e0;break;
我不知道为什么,可以解释一下么?
感谢你们的提醒,我下一步要优化这个算法,谢谢大师兄(头像)提醒

有可能是编译器的问题吧!
把case 5:num0=a0*10000+b0*1000+c0*100+d0*10+e0;break;
改为case 5:num0=(long)a0*10000+(long)b0*1000+(long)c0*100+(long)d0*10+e0;break;
a0,b0,c0,d0,e0还是原来的char型,试试行不行

因为你前面把百,十,个都定义成uchar型,我也是刚学没多久 ,感觉是那个有问题,uchar最大好像就65536,你把uchar改下

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top