键盘输入大于65536的数,数码管显示数与65536的差
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位值)时,你应该在之前做相应延时,你不可能一开机就马上读吧!
还有就是算法,你看看我这个:
- #include "Counter.h" //计算器头文件
- #include "Keyboard.h" //键盘控制
- #include "LCD1602.h" //LCD1602显示
- uchar code Table_A[]="0123456789";
- uchar code Table_B[]="error!";
- struct Counter //定义计算器变量
- {
- uchar Flag; //运算标志
- uchar Dsp[10]; //把计算结果B存到数组
- long A; //输入值A
- long B; //输入值B,运算后存到B
-
- } Count; //计算
- uchar Set_Counter_Keyboart() //计算器键盘设置
- {
- uchar Key_Data;
-
- Key_Data=Read_Keyboart_4_4();//读取按键值
- switch(Key_Data)
- {
- case 0xee:return '/';break; ///
- case 0xde:return '*';break; //*
- case 0xbe:return '-';break; //-
- case 0x7e:return '+';break; //+
-
- case 0xed:return 9;break; //9
- case 0xdd:return 6;break; //6
- case 0xbd:return 3;break; //3
- case 0x7d:return '=';break; //=
-
- case 0xeb:return 8;break; //8
- case 0xdb:return 5;break; //5
- case 0xbb:return 2;break; //2
- case 0x7b:return 0;break; //0
-
- case 0xe7:return 7;break; //7
- case 0xd7:return 4;break; //4
- case 0xb7:return 1;break; //1
- case 0x77:return 'C';break; //清零
-
- default:break;
- }
-
- return Counter_NULL;
- }
- uchar Counter_Input() //输入A值和B值
- {
- uchar Counter_Data; //键盘中间变量
-
- Counter_Data=Set_Counter_Keyboart();//读取键盘值
- if(Counter_Data!=Key_4_4_NULL)
- {
- switch(Counter_Data)
- {
- case '+':LCD_Write(1,'+');Count.Flag='+';Count.B=Count.A;Count.A=0;break;
- case '-':LCD_Write(1,'-');Count.Flag='-';Count.B=Count.A;Count.A=0;break;
- case '*':LCD_Write(1,'*');Count.Flag='*';Count.B=Count.A;Count.A=0;break;
- case '/':LCD_Write(1,'/');Count.Flag='/';Count.B=Count.A;Count.A=0;break;
- case 'C':LCD_Write(1,'C');LCD_Write(0,0x01);Count.A=Count.B=0;break;
- case '=':LCD_Write(1,'=');return '=';break;
-
- default:break;
- }
- if(Counter_Data>=0&&Counter_Data<10)
- {
- Count.A=Count.A*10+Counter_Data; //把输入值累加并存到A中
- LCD_Write(1,Table_A[Counter_Data]);
- }
- }
-
- return Counter_NULL;
- }
- void Resolve_Data(long dat) //分解计算结果并保存到数组中
- {
- uchar i=0;
- uchar j=0;
- uchar num[10]=0;
-
- while(dat)
- {
- num[i]=dat%10; //一直读取个位数,把计算结果从个位保存到数组中
- dat /=10;
- i++;
- }
-
- while(i--)
- {
- Count.Dsp[j++]=num[i]+'0'; //数组从高到底保存结果
- }
- }
- void Counter_Compute() //A与B进行相应计算并显示
- {
- uchar i=0;
-
- if(Counter_Input()=='=')
- {
- switch(Count.Flag)
- {
- case '+':Count.B+=Count.A;break;
- case '-':Count.B-=Count.A;break;
- case '*':Count.B*=Count.A;break;
- case '/':Count.B/=Count.A;break;
- default:break;
- }
- Resolve_Data(Count.B); //分解计算结果并保存到数组中
- if(Count.B<0) LCD_Coord_Data(0,2,Table_B);//显示错误
- else LCD_Coord_Data(0,2,Count.Dsp); //显示计算结果
- Count.A=Count.B=0; //清零
- for(i=0;i<10;i++)
- {
- Count.Dsp[i]=0;
- }
- }
- }
你的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改下