大神,求助啊
时间:10-02
整理:3721RD
点击:
rt,今年做的毕设,函数计算器在单片机上的实现,然后用1602作输出显示,要求第一行显示输入的等式,第二行显示结果,为什么我的等式就是显示不了呢?求高手赐教!下面附上我的源代码
/*******************************************************************************************
该程序是一个键盘查询方式的计算器,先从键盘读取一个数据,再读入运算符,读入运算符的同时要判别当前运算符与前一个运算符的优先级别,如果当前的运算符的优先级别高于前面的运算符,则将其前面的浮点数据编码成标准的浮点数表示方式(即用四个字节表示一个浮点数,最高位为符号位,接下来的八位它的阶码,剩下的23位表示浮点数的尾数),如此编码后的数据便于存储处理,将该编码后的数据存储到定义在8155的RAM中的数组存储单元中,同时将上一个运算符存储。如果当前的运算符优先级低,则在数据存储单元中取出上一个数据,解码后进行计算,将计算后的数据再存储。整个程序不断循环,反复执行
*******************************************************************************************/
#include<stdio.h>
#include<math.h>
#include<reg52.h>
#define LCM_Data P1
#define Busy 0x80 //用于检测LCM状态字中的Busy标识
sbit LCM_RW=P3^3; //定义引脚
sbit LCM_RS=P3^5;
sbit LCM_E=P3^4;
xdata unsigned char OUTBIT _at_ 0x7f01; //OUTBIT的地址
xdata unsigned char OUTSEG _at_ 0x7f02; //OUTSEG的地址
xdata unsigned char IN _at_ 0x7f03; //键盘反馈信号输入端的地址
xdata unsigned char stack[100] _at_ 0x7e00; //数据存储器,占用8155的RAM
#define LCDLen 16 //液晶一行显示字符的最大个数
#define point 0x0a //小数点键
#define sign 0x0b //正负号键
#define E 0x0c //指数操作键
#define back 0x0d //退格键
#define zero 0x0e // 清零键
#define change 0x0f //换屏键
#define ADD 0x10 //加号键
#define SUB 0x11 //减号键
#define MUL 0x12 //乘号键
#define div 0x13 //除号键
#define ln_exp 0x14 //对数和exp(x)键
#define sqrt_double 0x15//开放和平方键
#define sin_x 0x16 //正弦和二的x次方键
#define Left 0x17 //左括号键
#define right 0x18 //右括号键
#define EQU 0x19 //等号键
#define shift 0x1a //多功能键
/*****************************************************************************/
unsigned char data LCDBUF[LCDLen]; //液晶缓冲区
bit E_flag=0; //指数操作标志
bit point_flag=0; //小数点标志
bit shift_flag=0; //多功能键标志
bit sign_flag=0; //正负标志键
unsigned char pointer; //pointer为指针 m是计算符号寄存器
float result=0.0;
/*******************************************************************************************将输入的运算符编码子程序
*******************************************************************************************/
unsigned char panop1(unsigned char op)
{switch(op)
{ case ADD:return 0x01;break; //1表示加号
case SUB:return 0x02;break; //2表示减号
case MUL:return 0x04;break; //3表示乘号
case div:return 0x08;break; //4表示除号
default :return 0;break;
}
}
/******************************************************************************************将寄存器中记录的运算符解码子程序
*******************************************************************************************/
unsigned char panop(unsigned char op)
{ unsigned char go;
if(op>=0x80) go=Left;
if(op<0x80){op=op&0x0f;
switch(op)
{ case 0x08: go=div;break;
case 0x04: go=MUL;break;
case 0x02: go=SUB;break;
case 0x01: go=ADD;break;
default : go=0;break;
}
}
return go;
}
/*******************************************************************************************计算子程序
*******************************************************************************************/
float calculate(float x,unsigned char op,float y)
{ switch(op)
{case ADD: return (x+y); break;
case SUB: return (x-y);break;
case MUL: return (x*y);break;
case div: if(y!=0) return (x/y);
else return 0.0;
case 0 : return y; break;
default :return y; break;
}
}
/*******************************************************************************************延时子程序
*******************************************************************************************/
void Delay(unsigned char CNT)
{ unsigned char i;
while(CNT--!=0)
for(i=100;i!=0;i--);
}
/*******************************************************************************************
读数据
*******************************************************************************************/
unsigned char ReadDataLCM(void)
{
LCM_RS = 1;
LCM_RW = 1;
LCM_E = 0;
LCM_E = 0;
LCM_E = 1;
return(LCM_Data);
}
/*******************************************************************************************读状态
*******************************************************************************************/
unsigned char ReadStatusLCM(void)
{
LCM_Data = 0xFF;
LCM_RS = 0;
LCM_RW = 1;
LCM_E = 0;
LCM_E = 0;
LCM_E = 1;
while (LCM_Data & Busy); //检测忙信号
return(LCM_Data);
}
/******************************************************************************************写数据
*******************************************************************************************/
void WriteDataLCM(unsigned char WDLCM)
{
ReadStatusLCM(); //检测忙
LCM_Data = WDLCM;
LCM_RS = 1;
LCM_RW = 0;
LCM_E = 0; //若晶振速度太高可以在这后加小的延时
LCM_E = 0; //延时
LCM_E = 1;
}
/******************************************************************************************写指令
*******************************************************************************************/
void WriteCommandLCM(unsigned char WCLCM,BuysC) //BuysC为0时忽略忙检测
{
if (BuysC) ReadStatusLCM(); //根据需要检测忙
LCM_Data = WCLCM;
LCM_RS = 0;
LCM_RW = 0;
LCM_E = 0;
LCM_E = 0;
LCM_E = 1;
}
/*******************************************************************************************LCM初始化
*******************************************************************************************/
void LCMInit(void)
{
LCM_Data = 0;
WriteCommandLCM(0x38,0); //三次显示模式设置,不检测忙信号
Delay(52);
WriteCommandLCM(0x38,0);
Delay(52);
WriteCommandLCM(0x38,0);
Delay(52);
WriteCommandLCM(0x38,1); //显示模式设置,开始要求每次检测忙信号
WriteCommandLCM(0x08,1); //关闭显示
WriteCommandLCM(0x01,1); //显示清屏
WriteCommandLCM(0x06,1); // 显示光标移动设置
WriteCommandLCM(0x0C,1); // 显示开及光标设置
}
/******************************************************************************************按指定位置显示一个字符
******************************************************************************************/
void DisplayOneChar(unsigned char Y, unsigned char X, unsigned char DData)
{
Y &= 0x1;
X &= 0xF; //限制X不能大于15,Y不能大于1
if (Y) X |= 0x40; //当要显示第二行时地址码 0x40;
X |= 0x80; // 算出指令码
WriteCommandLCM(X, 0); //这里不检测忙信号,发送地址码
WriteDataLCM(DData);
}
/******************************************************************************************按指定位置显示一串字符
*******************************************************************************************/
void DisplayListChar(unsigned char Y, unsigned char X, unsigned char *DData)
{
unsigned char ListLength;
ListLength = 0;
Y &= 0x1;
X &= 0xF; //限制X不能大于15,Y不能大于1
while (DData[ListLength]>0x20) //若到达字串尾则退出
{
if (X <= 0xF) //X坐标应小于0xF
{
DisplayOneChar(Y, X, DData[ListLength]); //显示单个字符
ListLength++;
X++;
}
}
}
/*******************************************************************************************液晶显示子程序
*******************************************************************************************/
void Displayled(unsigned char *LCDBUF)
{
LCMInit(); //LCM初始化
Delay(52); //延时片刻(可不要)
DisplayListChar(0,0,LCDBUF);
ReadDataLCM();//测试用句无意义
}
/********************************************************************************************键盘测试子程序
*******************************************************************************************/
unsigned char Testkey()
{
OUTBIT=0; //拉低键盘接口线的电平
return(~IN & 0x0f); //判断是否有健按下 有健按下返回值不为零
}
/********************************************************************************************键扫描子程序
*******************************************************************************************/
unsigned char Getkey()
{
unsigned char pos;
unsigned char i,m;
unsigned char k;
i=0;
pos=0x01;
do{
OUTBIT=~pos; //键盘的列线,供扫描时调用
pos <<= 1; //逐根线扫描
k=~IN & 0x0f; //k不为零时有健按下
}while((i++ != 7)&&(k==0));
if(k!=0){ i-=1;
if(k&1) i+=00; //扫描第一行
else if(k&2) i+=8; //扫描第二行
else if(k&4) i+=0x10; //扫描第三行
else if(k&8) i+=0x18; //扫描第四行
m=i;
do Delay(52); //延时看是否是人为的按键
while(Testkey());
return(m); //返回键值
}
else return(0xff); //当再检测时没有检测到可以认为是机械抖动
}
/********************************************************************************************结果显示子程序
*******************************************************************************************/
void Displayresult(float result)
{
int z;
unsigned char i;
unsigned long m;
signed int j=0;
if(result>=0)
{
for(z=0;z<16;z++) //初始化数据显示缓冲区
{
LCDBUF[z]=0xfe;//对应于1602CGRAM
}
LCDBUF[0]=0xfe;
} //0xff显示为正
else
{
LCDBUF[0]=0x2D; //0x2D显示一个-号
result=-result;
}
while(result>=10){result=result/10;j++;} //将浮点数变成标准的科学计数方式
while((result<1)&&(result!=0)){result=result*10;j--;} //将浮点数变成标准的科学计数方式
m=result*1000000;
i=8;
while(m!=0)
{
LCDBUF[i]=m%10+0x30;
m=m/10;
if(i!=3){i=i-1;}
else {i=i-2;}
}
LCDBUF[2]=0x2E;
LCDBUF[10]=0x45; //E的显示
if(j<0)
{
LCDBUF[11]=0x2D; //负指数的符号显示
j=-j;
}
else
{
LCDBUF[11]=0xfe;
} //正号不显示,默认
LCDBUF[12]=j/10+0x30;
LCDBUF[13]=j%10+0x30;
if(result==0.0) //当浮点数为零时的处理
{
for(i=0;i<16;i++)
{
LCDBUF[i]=0xfe;
}
LCDBUF[13]=0x30; //对应于'0'
}
}
/********************************************************************************************读数据译码子程序
*******************************************************************************************/
float popopd() //读数据子程序
{ unsigned long y=0;
unsigned long z,high;
float x,k;
unsigned char u=0;
unsigned char i,j;
bit s;
for(i=0;i<4;i++) //从数据组存储器中连续读出四个字节的数据 一个浮点数
{ pointer--;
u=stack[pointer];
y=y*256+u;
}
if(y>=0x80000000) s=1; //判断符号 最高位为1则为负为0则为正
else s=0;
j=((2*y)/(65536*256)); //j为该浮点数的阶码
z=(((2*y)%(65536*256))/2); //z为该浮点数的尾数
z=z*2;
k=1.0;
x=1.0;
for(i=1;i<=23;i++){ k=k*2.0; //将存储的二进制浮点数尾数变成标准的形式以便计算
high=(z&0x800000);
if(high==0x800000) {x=(x+1/k);}
z=z*2;
}
if(j>=127) {for(i=1;i<=j-127;i++) x=x*2.0; } //阶码返回到浮点数的处理
else
{for(i=1;i<=127-j;i++) x=(x/2.0);}
if(s==1){ x=x*(-1.0); } //还原浮点数的符号
return x; //返回转换后的浮点数
}
/********************************************************************************************编码存储数据子程序
*******************************************************************************************/
void pushreadkey(float x) //将键盘读出的数据放到数据组中 即压栈
{ unsigned long y=0;
unsigned char i,j,k=0;
if(x<0) {y=0x100; x=fabs(x); } //x小于零则浮点数的符号位为1 //变为正数以便统一计算
else y=0;
if(x==0.0){y=0;} //浮点数为正则符号位为0
else if(x<1.0){ //浮点数小于一时则将其乘以2直道大于一为止,同时记下乘了多少次 k
while(x<=1.0){
x=x*2.0;
k=k+1;
}
y=y+127-k; //计算阶码
}
else if(x<2.0){ y=y+127; } //x在一到二之间正好是标准计数的形式
else if(x>=2.0){
while(x>=2.0){ x=x/2.0; //浮点数大于二要将其变为标准形式 并计算除了多少次k
k=k+1;
}
y=y+127+k; //计算阶码
}
if(x>=1.0) x=x-1; //浮点数尾数部分的1是默认的不必存储,所以减去
for(i=1;i<=23;i++){ //将浮点数x变为标准的二进制方式y
x=x*2;
if(x>=1){ x=x-1; //y左移1位且判断最低位是1还是0
y=y*2+1;}
else y=y*2;
}
for(i=0;i<4;i++){ j=(unsigned char)(y%256);//存储这个标准的二进制浮点数到数据组存储器 占用四个字节
y=y/256;
stack[pointer]=j;
pointer++;
} //低字节存在前面的数组单元
}
/********************************************************************************************主函数
*******************************************************************************************/
void main()
{ float last_opd,now_opd;
unsigned char i,j,z,key,last_key,last_op,y,m; //j为小数点位置 y为LCD一行所能显示的字符个数减一
long int In,shu;
int n=0,t=1; //n为浮点数输入指数 t为连续输入的左括号个数计数
pointer=0; //数据指针初始化
m=0; //运算符寄存器初始化
In=0; //浮点数底数初始化
i=0; //有效数位数初始化
j=0; //小数点位置记录初始化
last_op=0; //运算符初始化
last_opd=0;now_opd=0; //操作数初始化
LCMInit(); //LCM初始化
for(z=0;z<16;z++) //初始化数据显示缓冲区
{LCDBUF[z]=0xfe;}
LCDBUF[15]=0x30;
while(1){ y=15;
Displayled(LCDBUF);
while(!Testkey()) //检测是否有键按下
Delay(52); //延时去抖动
while(!Testkey())
Delay(100);
key=Getkey(); //读键值
if((key>=0x0f)&&(key<=0x1a)) //当键为change到EQU之间的操作符号时
{
for(z=0;z<16;z++){LCDBUF[z]=0xfe;}//清理数据显示缓冲区(清空显示)
//显示当前输入的运算符(在最右边即15)
if(key==0x10){LCDBUF[15]=0x2B;}
if(key==0x11){LCDBUF[15]=0x2D;}
if(key==0x12){LCDBUF[15]=0x2A;}
if(key==0x13){LCDBUF[15]=0x2F;}
if(key==0x17){LCDBUF[15]=0x28;}
if(key==0x18){LCDBUF[15]=0x29;}
if(key!=Left){t=1;} //当输入不为左括号时 清除左括号标志
if(key==0x1a)//多功能键shift
{
shift_flag=1;LCDBUF[0]=0x53;LCDBUF[1]=0x68;LCDBUF[2]=0x69;
LCDBUF[3]=0x66;LCDBUF[4]=0x74;
} //多功能键就设立多功能标志键,LCDBUF分别改为SHIFT显示出来
if(last_key<=change)//考虑到上一个操作符为单操作符时要保留上面的结果now_opd
{ //例如,7+sqrt(2.1)-3,输入sqrt后会紧接着输入减号,此时的数据没有变化,为前面的计算结果
now_opd=In; //将键盘输入的码源转换为想要输入的浮点数
n=n-j; //指数计算
if(n>0){for(z=0;z<n;z++) //将输入的浮点数的指数反馈到浮点数上
{now_opd=now_opd*10;}
}
if(n<0)
{
n=-n; //将输入的浮点数的指数反馈到浮点数上
for(z=0;z<n;z++)
{now_opd=now_opd/10.0;}
}
j=0; //小数点位置记录清零
i=0; //有效数位数记录清零
E_flag=0; //指数操作标志清零
point_flag=0; //小数点标志位清零
sign_flag=0; //正负号复位
n=0; //指数清零
In=0; //底数清零
}
if((key>=0x14)&&(key<=0x16)) //当按键为单操作键时
{
if(last_key==Left){;} //无效输入(在左括号后马上输入运算符无效)
switch(key)
{ case ln_exp : if(shift_flag==1) //判多功能键确定是求自然对数还是求e的x方
{now_opd=log(now_opd); //自然对数处理
shift_flag=0; //多功能标志复位
}
else{now_opd=exp(now_opd);} break; //exp处理
case sqrt_double : if((shift_flag==1)&&(now_opd>=0)) //判多功能键确定是求开放还是平方
{now_opd=sqrt(now_opd); //开放处理
shift_flag=0; } //多功能标志复位
else
now_opd=now_opd*now_opd; break; //平方处理
case sin_x : if(shift_flag==1) //判多功能键确定是求正弦还是2的x方
{now_opd=sin(now_opd); //正弦值计算
shift_flag=0;} //多功能标志复位
else{now_opd=pow(2.0,now_opd);} //2的x次方处理
break;
}
result=now_opd;
}
if((key>=0x10)&&(key<=0x13)) //当按键为双操作符号时(+ - * /)的讨论
{ if((last_key>=ADD)&&(last_key<=div)){} //连续输入两个双操作数运算符时后输入的无效
else if(last_key==Left){} //无效输入(在左括号后马上输入运算符无效)
else{
if((key==ADD)||(key==SUB)) //当输入为加或减时
{ if((last_op==0x17)||(last_op==0))
//如果上一个运算符是左括号则应将上一个运算符和数据存储
{stack[pointer]=m; //存储运算符
pointer++; //指针前移
pushreadkey(now_opd); //存储数据
}
if((last_op>=0x10/*ADD*/)&&(last_op<=0x13/*div*/))
//如果上一个运算符是+_* /中的一个则计算
{ last_opd=popopd(); //取出前一个操作数据
now_opd=calculate(last_opd,last_op,now_opd); //计算
pushreadkey(now_opd); //存储数据
}
last_op=key; //记录输入的运算符
m=panop1(last_op); //运算符寄存器作相应的记录
}
if((key==MUL)||(key==div)) //如果输入的是* /号
{ if((last_op==0x17/*Left*/)||(last_op==ADD)||(last_op==SUB)||(last_op==0) )
//如果前一个运算符的运算级低则存储
{ stack[pointer]=m; //存储运算符记录
pointer++; //指针上移
pushreadkey(now_opd); //存储当前数据
}
if((last_op==MUL)||(last_op==div)) //如果前一个运算级高则计算
{last_opd=popopd(); //取出上一个操作数
now_opd=calculate(last_opd,last_op,now_opd); //计算
pushreadkey(now_opd); //存储数据
}
last_op=key; //记录输入的运算符
m=panop1(last_op); //运算符寄存器作相应的记录
}
}
}
if(key==Left) //输入左括号时
{ if(last_key==Left){t++;} //连续输入左括号时t显示连续输入的左括号的个数
for(z=0;z<16;z++){LCDBUF[z]=0xfe;}
LCDBUF[0]='('; //显示左括号
LCDBUF[1]=t+0x30; //显示左括号的个数
if(m>=0x80){m=m+0x10;} //已经由左括号时,记录加一个
else {m=m+0x80;} //前面没有左括号时 记录左括号
last_op=Left; //记录上一个运算符
}
if(key==right) //输入右括号时
{while(last_op!=Left) //一直计算直道消除一个与之匹配的左括号为止
{ last_opd=popopd(); //取出上一个操作数
pointer--; //指针减一对准运算符
m=stack[pointer]; //取出运算符记录
now_opd=calculate(last_opd,last_op,now_opd); //计算
last_op=panop(m); //判断上一个运算符
}
if ((m&0xf0)>0x80) //当前面有连续的几个左括号时
{ m=m-0x10; //消除一个左括号
last_op=Left; //返回上一个运算符
}
else //只有一个左括号时
{ m=m&0x0f; //消除左括号
last_op=panop(m); //返回上一个运算符
}
Displayresult(now_opd); //显示当前这个括号中的计算结果
}
if(key==EQU) //当输入的为等号时则直接计算输出结果
{
while(last_op!=0) //当还有数据没有处理时则计算
{ last_opd=popopd(); //取出上一个操作数
pointer--; //指针减一个,对准运算符
m=stack[pointer]; //取出运算符记录
now_opd=calculate(last_opd,last_op,now_opd);//计算
last_op=panop(m); //通过运算符寄存器m判断上一个运算符以便计算
}
result=now_opd; //结果赋给result
Displayresult(result); //显示结果
Displayled(LCDBUF);
}
}
if((key>=0)&&(key<=0x0e)) //当按键为数字处理键时
{ for(z=0;z<16;z++) //清理显示缓冲区 以免处理时显示混乱
{LCDBUF[z]=0xfe;}
if(key==sign){sign_flag=1;
if(E_flag==0){LCDBUF[0]=0x2D;}
if(E_flag==1){LCDBUF[10]=0x2D;}} //负号的显示 设立正负号标志
if(key==E){E_flag=1;LCDBUF[9]=0x45; } //E
if(key==zero)
{last_opd=now_opd=0; //数据寄存器清零 (清零键)
m=0; //运算符记录寄存器清零
last_op=0;/*op=0;*/ //上一个运算符清零
result=0; //结果清零
for(z=0;z<100;z++)
{stack[z]=0;} //清理数据存储区
//清理所有的标志位,相当于复位
i=0;n=0;In=0; //输入数据寄存器清零
j=0; //小数点位置记录清零
t=1; //左括号连续输入个数复位
pointer=0; //取数据指针复位
E_flag=0; //指数操作标志清零
shift_flag=0; //多功能标志位清零
point_flag=0; //小数点标志为清零
sign_flag=0; //正负标志位清零
for(z=0;z<6;z++) //清理显示缓冲区
{LCDBUF[z]=0xfe;}
Displayled(LCDBUF);
}
if(key==point){point_flag=1;} //设立小数点标志号
if(key==back){ if(E_flag==1) {n=n/10;i--;} //退格键处理 在指数操作时指数退一格
if(E_flag==0) {In=In/10; //底数操作时,底数退一格
if(i>0){i--;} //输入的有效数据位数减少一个
if(j>0){j--;} } //输入的小数点的位置后移一位
for(z=0;z<16;z++) //清理显示缓冲区
{LCDBUF[z]=0xfe;}
if(E_flag==1){LCDBUF[9]=0x45; //指数操作标志
if(sign_flag==1)LCDBUF[10]=0x2D; //
}
if((last_key==Left)||(last_key==back))
{
if(m>0x80)
{
m=m-0x10;t--;LCDBUF[0]=0x28;LCDBUF[1]=t+0x30;
if(t==0){m=m&0x0f;last_op=panop(m);}} } //如果多输入了左括号,可以通过此键消除多余的左括号
}
if((key>=0)&&(key<=9)) //纯数字处理
{
if(E_flag==1) //指数输入界面
{
if(i<2)
{
n=abs(n); //考虑到浮点数的取值范围,指数最多只能输入两位
n=n*10+key; //指数计算
i++;
if(sign_flag==1){n=-abs(n);} //数据取正值以便显示处理
}
}
else if(E_flag==0) //底数部分输入
{ if(i<7)
{
In=labs(In); //可以输入7位有效数据
In=In*10+key; i++; //i记录已经输入的有效数的位数
if((In==0)&&(point_flag==0)) //连续输入几个零,且不是有效数据时,输入的零无效
{i=i-1;} //连续输入几个零且不是有效数则,有效数的个数不计数
if(point_flag==1){j++;} //输入的小数点的位置记录
if(sign_flag==1){In=-labs(In);}
}
}
}
if(E_flag==0)
{
shu=In; //底数屏操作显示
if(shu<0)
{ LCDBUF[0]=0x2D; //底数屏负号的显示在最高位
shu=labs(shu); //数据取正,以便显示处理
}
LCDBUF[y--]=(shu%10)+0x30; //输入的最低位数据处理
shu=shu/10;
if(i>j)
//该数的底数为大于1的浮点数,则前面连续输入几个零时无效,不显示 i为有效数。 { // j为小数点位置
while((y>=1)&&(shu!=0))
{ LCDBUF[y--]=(shu%10)+0x30; //输入的底数据大于一的浮点数
shu=shu/10;
}
if((j>0)&&(j<=6)) //显示小数点 随着输入的位数的变化,小数点的位置也随着变化
{
LCDBUF[ 9-j]=LCDBUF[10-j];
LCDBUF[10-j]=LCDBUF[11-j];
LCDBUF[11-j]=LCDBUF[12-j];
LCDBUF[12-j]=LCDBUF[13-j];
LCDBUF[13-j]=LCDBUF[14-j];
LCDBUF[14-j]=LCDBUF[15-j];
LCDBUF[15-j]=0x2E;
}
Displayled(LCDBUF);
}
else if(i=j) //此种情况是处理底数是小于1的浮点数 如0.0002
{
if(j<=6)
{
for(z=0;z<j;z++)
/*******************************************************************************************
该程序是一个键盘查询方式的计算器,先从键盘读取一个数据,再读入运算符,读入运算符的同时要判别当前运算符与前一个运算符的优先级别,如果当前的运算符的优先级别高于前面的运算符,则将其前面的浮点数据编码成标准的浮点数表示方式(即用四个字节表示一个浮点数,最高位为符号位,接下来的八位它的阶码,剩下的23位表示浮点数的尾数),如此编码后的数据便于存储处理,将该编码后的数据存储到定义在8155的RAM中的数组存储单元中,同时将上一个运算符存储。如果当前的运算符优先级低,则在数据存储单元中取出上一个数据,解码后进行计算,将计算后的数据再存储。整个程序不断循环,反复执行
*******************************************************************************************/
#include<stdio.h>
#include<math.h>
#include<reg52.h>
#define LCM_Data P1
#define Busy 0x80 //用于检测LCM状态字中的Busy标识
sbit LCM_RW=P3^3; //定义引脚
sbit LCM_RS=P3^5;
sbit LCM_E=P3^4;
xdata unsigned char OUTBIT _at_ 0x7f01; //OUTBIT的地址
xdata unsigned char OUTSEG _at_ 0x7f02; //OUTSEG的地址
xdata unsigned char IN _at_ 0x7f03; //键盘反馈信号输入端的地址
xdata unsigned char stack[100] _at_ 0x7e00; //数据存储器,占用8155的RAM
#define LCDLen 16 //液晶一行显示字符的最大个数
#define point 0x0a //小数点键
#define sign 0x0b //正负号键
#define E 0x0c //指数操作键
#define back 0x0d //退格键
#define zero 0x0e // 清零键
#define change 0x0f //换屏键
#define ADD 0x10 //加号键
#define SUB 0x11 //减号键
#define MUL 0x12 //乘号键
#define div 0x13 //除号键
#define ln_exp 0x14 //对数和exp(x)键
#define sqrt_double 0x15//开放和平方键
#define sin_x 0x16 //正弦和二的x次方键
#define Left 0x17 //左括号键
#define right 0x18 //右括号键
#define EQU 0x19 //等号键
#define shift 0x1a //多功能键
/*****************************************************************************/
unsigned char data LCDBUF[LCDLen]; //液晶缓冲区
bit E_flag=0; //指数操作标志
bit point_flag=0; //小数点标志
bit shift_flag=0; //多功能键标志
bit sign_flag=0; //正负标志键
unsigned char pointer; //pointer为指针 m是计算符号寄存器
float result=0.0;
/*******************************************************************************************将输入的运算符编码子程序
*******************************************************************************************/
unsigned char panop1(unsigned char op)
{switch(op)
{ case ADD:return 0x01;break; //1表示加号
case SUB:return 0x02;break; //2表示减号
case MUL:return 0x04;break; //3表示乘号
case div:return 0x08;break; //4表示除号
default :return 0;break;
}
}
/******************************************************************************************将寄存器中记录的运算符解码子程序
*******************************************************************************************/
unsigned char panop(unsigned char op)
{ unsigned char go;
if(op>=0x80) go=Left;
if(op<0x80){op=op&0x0f;
switch(op)
{ case 0x08: go=div;break;
case 0x04: go=MUL;break;
case 0x02: go=SUB;break;
case 0x01: go=ADD;break;
default : go=0;break;
}
}
return go;
}
/*******************************************************************************************计算子程序
*******************************************************************************************/
float calculate(float x,unsigned char op,float y)
{ switch(op)
{case ADD: return (x+y); break;
case SUB: return (x-y);break;
case MUL: return (x*y);break;
case div: if(y!=0) return (x/y);
else return 0.0;
case 0 : return y; break;
default :return y; break;
}
}
/*******************************************************************************************延时子程序
*******************************************************************************************/
void Delay(unsigned char CNT)
{ unsigned char i;
while(CNT--!=0)
for(i=100;i!=0;i--);
}
/*******************************************************************************************
读数据
*******************************************************************************************/
unsigned char ReadDataLCM(void)
{
LCM_RS = 1;
LCM_RW = 1;
LCM_E = 0;
LCM_E = 0;
LCM_E = 1;
return(LCM_Data);
}
/*******************************************************************************************读状态
*******************************************************************************************/
unsigned char ReadStatusLCM(void)
{
LCM_Data = 0xFF;
LCM_RS = 0;
LCM_RW = 1;
LCM_E = 0;
LCM_E = 0;
LCM_E = 1;
while (LCM_Data & Busy); //检测忙信号
return(LCM_Data);
}
/******************************************************************************************写数据
*******************************************************************************************/
void WriteDataLCM(unsigned char WDLCM)
{
ReadStatusLCM(); //检测忙
LCM_Data = WDLCM;
LCM_RS = 1;
LCM_RW = 0;
LCM_E = 0; //若晶振速度太高可以在这后加小的延时
LCM_E = 0; //延时
LCM_E = 1;
}
/******************************************************************************************写指令
*******************************************************************************************/
void WriteCommandLCM(unsigned char WCLCM,BuysC) //BuysC为0时忽略忙检测
{
if (BuysC) ReadStatusLCM(); //根据需要检测忙
LCM_Data = WCLCM;
LCM_RS = 0;
LCM_RW = 0;
LCM_E = 0;
LCM_E = 0;
LCM_E = 1;
}
/*******************************************************************************************LCM初始化
*******************************************************************************************/
void LCMInit(void)
{
LCM_Data = 0;
WriteCommandLCM(0x38,0); //三次显示模式设置,不检测忙信号
Delay(52);
WriteCommandLCM(0x38,0);
Delay(52);
WriteCommandLCM(0x38,0);
Delay(52);
WriteCommandLCM(0x38,1); //显示模式设置,开始要求每次检测忙信号
WriteCommandLCM(0x08,1); //关闭显示
WriteCommandLCM(0x01,1); //显示清屏
WriteCommandLCM(0x06,1); // 显示光标移动设置
WriteCommandLCM(0x0C,1); // 显示开及光标设置
}
/******************************************************************************************按指定位置显示一个字符
******************************************************************************************/
void DisplayOneChar(unsigned char Y, unsigned char X, unsigned char DData)
{
Y &= 0x1;
X &= 0xF; //限制X不能大于15,Y不能大于1
if (Y) X |= 0x40; //当要显示第二行时地址码 0x40;
X |= 0x80; // 算出指令码
WriteCommandLCM(X, 0); //这里不检测忙信号,发送地址码
WriteDataLCM(DData);
}
/******************************************************************************************按指定位置显示一串字符
*******************************************************************************************/
void DisplayListChar(unsigned char Y, unsigned char X, unsigned char *DData)
{
unsigned char ListLength;
ListLength = 0;
Y &= 0x1;
X &= 0xF; //限制X不能大于15,Y不能大于1
while (DData[ListLength]>0x20) //若到达字串尾则退出
{
if (X <= 0xF) //X坐标应小于0xF
{
DisplayOneChar(Y, X, DData[ListLength]); //显示单个字符
ListLength++;
X++;
}
}
}
/*******************************************************************************************液晶显示子程序
*******************************************************************************************/
void Displayled(unsigned char *LCDBUF)
{
LCMInit(); //LCM初始化
Delay(52); //延时片刻(可不要)
DisplayListChar(0,0,LCDBUF);
ReadDataLCM();//测试用句无意义
}
/********************************************************************************************键盘测试子程序
*******************************************************************************************/
unsigned char Testkey()
{
OUTBIT=0; //拉低键盘接口线的电平
return(~IN & 0x0f); //判断是否有健按下 有健按下返回值不为零
}
/********************************************************************************************键扫描子程序
*******************************************************************************************/
unsigned char Getkey()
{
unsigned char pos;
unsigned char i,m;
unsigned char k;
i=0;
pos=0x01;
do{
OUTBIT=~pos; //键盘的列线,供扫描时调用
pos <<= 1; //逐根线扫描
k=~IN & 0x0f; //k不为零时有健按下
}while((i++ != 7)&&(k==0));
if(k!=0){ i-=1;
if(k&1) i+=00; //扫描第一行
else if(k&2) i+=8; //扫描第二行
else if(k&4) i+=0x10; //扫描第三行
else if(k&8) i+=0x18; //扫描第四行
m=i;
do Delay(52); //延时看是否是人为的按键
while(Testkey());
return(m); //返回键值
}
else return(0xff); //当再检测时没有检测到可以认为是机械抖动
}
/********************************************************************************************结果显示子程序
*******************************************************************************************/
void Displayresult(float result)
{
int z;
unsigned char i;
unsigned long m;
signed int j=0;
if(result>=0)
{
for(z=0;z<16;z++) //初始化数据显示缓冲区
{
LCDBUF[z]=0xfe;//对应于1602CGRAM
}
LCDBUF[0]=0xfe;
} //0xff显示为正
else
{
LCDBUF[0]=0x2D; //0x2D显示一个-号
result=-result;
}
while(result>=10){result=result/10;j++;} //将浮点数变成标准的科学计数方式
while((result<1)&&(result!=0)){result=result*10;j--;} //将浮点数变成标准的科学计数方式
m=result*1000000;
i=8;
while(m!=0)
{
LCDBUF[i]=m%10+0x30;
m=m/10;
if(i!=3){i=i-1;}
else {i=i-2;}
}
LCDBUF[2]=0x2E;
LCDBUF[10]=0x45; //E的显示
if(j<0)
{
LCDBUF[11]=0x2D; //负指数的符号显示
j=-j;
}
else
{
LCDBUF[11]=0xfe;
} //正号不显示,默认
LCDBUF[12]=j/10+0x30;
LCDBUF[13]=j%10+0x30;
if(result==0.0) //当浮点数为零时的处理
{
for(i=0;i<16;i++)
{
LCDBUF[i]=0xfe;
}
LCDBUF[13]=0x30; //对应于'0'
}
}
/********************************************************************************************读数据译码子程序
*******************************************************************************************/
float popopd() //读数据子程序
{ unsigned long y=0;
unsigned long z,high;
float x,k;
unsigned char u=0;
unsigned char i,j;
bit s;
for(i=0;i<4;i++) //从数据组存储器中连续读出四个字节的数据 一个浮点数
{ pointer--;
u=stack[pointer];
y=y*256+u;
}
if(y>=0x80000000) s=1; //判断符号 最高位为1则为负为0则为正
else s=0;
j=((2*y)/(65536*256)); //j为该浮点数的阶码
z=(((2*y)%(65536*256))/2); //z为该浮点数的尾数
z=z*2;
k=1.0;
x=1.0;
for(i=1;i<=23;i++){ k=k*2.0; //将存储的二进制浮点数尾数变成标准的形式以便计算
high=(z&0x800000);
if(high==0x800000) {x=(x+1/k);}
z=z*2;
}
if(j>=127) {for(i=1;i<=j-127;i++) x=x*2.0; } //阶码返回到浮点数的处理
else
{for(i=1;i<=127-j;i++) x=(x/2.0);}
if(s==1){ x=x*(-1.0); } //还原浮点数的符号
return x; //返回转换后的浮点数
}
/********************************************************************************************编码存储数据子程序
*******************************************************************************************/
void pushreadkey(float x) //将键盘读出的数据放到数据组中 即压栈
{ unsigned long y=0;
unsigned char i,j,k=0;
if(x<0) {y=0x100; x=fabs(x); } //x小于零则浮点数的符号位为1 //变为正数以便统一计算
else y=0;
if(x==0.0){y=0;} //浮点数为正则符号位为0
else if(x<1.0){ //浮点数小于一时则将其乘以2直道大于一为止,同时记下乘了多少次 k
while(x<=1.0){
x=x*2.0;
k=k+1;
}
y=y+127-k; //计算阶码
}
else if(x<2.0){ y=y+127; } //x在一到二之间正好是标准计数的形式
else if(x>=2.0){
while(x>=2.0){ x=x/2.0; //浮点数大于二要将其变为标准形式 并计算除了多少次k
k=k+1;
}
y=y+127+k; //计算阶码
}
if(x>=1.0) x=x-1; //浮点数尾数部分的1是默认的不必存储,所以减去
for(i=1;i<=23;i++){ //将浮点数x变为标准的二进制方式y
x=x*2;
if(x>=1){ x=x-1; //y左移1位且判断最低位是1还是0
y=y*2+1;}
else y=y*2;
}
for(i=0;i<4;i++){ j=(unsigned char)(y%256);//存储这个标准的二进制浮点数到数据组存储器 占用四个字节
y=y/256;
stack[pointer]=j;
pointer++;
} //低字节存在前面的数组单元
}
/********************************************************************************************主函数
*******************************************************************************************/
void main()
{ float last_opd,now_opd;
unsigned char i,j,z,key,last_key,last_op,y,m; //j为小数点位置 y为LCD一行所能显示的字符个数减一
long int In,shu;
int n=0,t=1; //n为浮点数输入指数 t为连续输入的左括号个数计数
pointer=0; //数据指针初始化
m=0; //运算符寄存器初始化
In=0; //浮点数底数初始化
i=0; //有效数位数初始化
j=0; //小数点位置记录初始化
last_op=0; //运算符初始化
last_opd=0;now_opd=0; //操作数初始化
LCMInit(); //LCM初始化
for(z=0;z<16;z++) //初始化数据显示缓冲区
{LCDBUF[z]=0xfe;}
LCDBUF[15]=0x30;
while(1){ y=15;
Displayled(LCDBUF);
while(!Testkey()) //检测是否有键按下
Delay(52); //延时去抖动
while(!Testkey())
Delay(100);
key=Getkey(); //读键值
if((key>=0x0f)&&(key<=0x1a)) //当键为change到EQU之间的操作符号时
{
for(z=0;z<16;z++){LCDBUF[z]=0xfe;}//清理数据显示缓冲区(清空显示)
//显示当前输入的运算符(在最右边即15)
if(key==0x10){LCDBUF[15]=0x2B;}
if(key==0x11){LCDBUF[15]=0x2D;}
if(key==0x12){LCDBUF[15]=0x2A;}
if(key==0x13){LCDBUF[15]=0x2F;}
if(key==0x17){LCDBUF[15]=0x28;}
if(key==0x18){LCDBUF[15]=0x29;}
if(key!=Left){t=1;} //当输入不为左括号时 清除左括号标志
if(key==0x1a)//多功能键shift
{
shift_flag=1;LCDBUF[0]=0x53;LCDBUF[1]=0x68;LCDBUF[2]=0x69;
LCDBUF[3]=0x66;LCDBUF[4]=0x74;
} //多功能键就设立多功能标志键,LCDBUF分别改为SHIFT显示出来
if(last_key<=change)//考虑到上一个操作符为单操作符时要保留上面的结果now_opd
{ //例如,7+sqrt(2.1)-3,输入sqrt后会紧接着输入减号,此时的数据没有变化,为前面的计算结果
now_opd=In; //将键盘输入的码源转换为想要输入的浮点数
n=n-j; //指数计算
if(n>0){for(z=0;z<n;z++) //将输入的浮点数的指数反馈到浮点数上
{now_opd=now_opd*10;}
}
if(n<0)
{
n=-n; //将输入的浮点数的指数反馈到浮点数上
for(z=0;z<n;z++)
{now_opd=now_opd/10.0;}
}
j=0; //小数点位置记录清零
i=0; //有效数位数记录清零
E_flag=0; //指数操作标志清零
point_flag=0; //小数点标志位清零
sign_flag=0; //正负号复位
n=0; //指数清零
In=0; //底数清零
}
if((key>=0x14)&&(key<=0x16)) //当按键为单操作键时
{
if(last_key==Left){;} //无效输入(在左括号后马上输入运算符无效)
switch(key)
{ case ln_exp : if(shift_flag==1) //判多功能键确定是求自然对数还是求e的x方
{now_opd=log(now_opd); //自然对数处理
shift_flag=0; //多功能标志复位
}
else{now_opd=exp(now_opd);} break; //exp处理
case sqrt_double : if((shift_flag==1)&&(now_opd>=0)) //判多功能键确定是求开放还是平方
{now_opd=sqrt(now_opd); //开放处理
shift_flag=0; } //多功能标志复位
else
now_opd=now_opd*now_opd; break; //平方处理
case sin_x : if(shift_flag==1) //判多功能键确定是求正弦还是2的x方
{now_opd=sin(now_opd); //正弦值计算
shift_flag=0;} //多功能标志复位
else{now_opd=pow(2.0,now_opd);} //2的x次方处理
break;
}
result=now_opd;
}
if((key>=0x10)&&(key<=0x13)) //当按键为双操作符号时(+ - * /)的讨论
{ if((last_key>=ADD)&&(last_key<=div)){} //连续输入两个双操作数运算符时后输入的无效
else if(last_key==Left){} //无效输入(在左括号后马上输入运算符无效)
else{
if((key==ADD)||(key==SUB)) //当输入为加或减时
{ if((last_op==0x17)||(last_op==0))
//如果上一个运算符是左括号则应将上一个运算符和数据存储
{stack[pointer]=m; //存储运算符
pointer++; //指针前移
pushreadkey(now_opd); //存储数据
}
if((last_op>=0x10/*ADD*/)&&(last_op<=0x13/*div*/))
//如果上一个运算符是+_* /中的一个则计算
{ last_opd=popopd(); //取出前一个操作数据
now_opd=calculate(last_opd,last_op,now_opd); //计算
pushreadkey(now_opd); //存储数据
}
last_op=key; //记录输入的运算符
m=panop1(last_op); //运算符寄存器作相应的记录
}
if((key==MUL)||(key==div)) //如果输入的是* /号
{ if((last_op==0x17/*Left*/)||(last_op==ADD)||(last_op==SUB)||(last_op==0) )
//如果前一个运算符的运算级低则存储
{ stack[pointer]=m; //存储运算符记录
pointer++; //指针上移
pushreadkey(now_opd); //存储当前数据
}
if((last_op==MUL)||(last_op==div)) //如果前一个运算级高则计算
{last_opd=popopd(); //取出上一个操作数
now_opd=calculate(last_opd,last_op,now_opd); //计算
pushreadkey(now_opd); //存储数据
}
last_op=key; //记录输入的运算符
m=panop1(last_op); //运算符寄存器作相应的记录
}
}
}
if(key==Left) //输入左括号时
{ if(last_key==Left){t++;} //连续输入左括号时t显示连续输入的左括号的个数
for(z=0;z<16;z++){LCDBUF[z]=0xfe;}
LCDBUF[0]='('; //显示左括号
LCDBUF[1]=t+0x30; //显示左括号的个数
if(m>=0x80){m=m+0x10;} //已经由左括号时,记录加一个
else {m=m+0x80;} //前面没有左括号时 记录左括号
last_op=Left; //记录上一个运算符
}
if(key==right) //输入右括号时
{while(last_op!=Left) //一直计算直道消除一个与之匹配的左括号为止
{ last_opd=popopd(); //取出上一个操作数
pointer--; //指针减一对准运算符
m=stack[pointer]; //取出运算符记录
now_opd=calculate(last_opd,last_op,now_opd); //计算
last_op=panop(m); //判断上一个运算符
}
if ((m&0xf0)>0x80) //当前面有连续的几个左括号时
{ m=m-0x10; //消除一个左括号
last_op=Left; //返回上一个运算符
}
else //只有一个左括号时
{ m=m&0x0f; //消除左括号
last_op=panop(m); //返回上一个运算符
}
Displayresult(now_opd); //显示当前这个括号中的计算结果
}
if(key==EQU) //当输入的为等号时则直接计算输出结果
{
while(last_op!=0) //当还有数据没有处理时则计算
{ last_opd=popopd(); //取出上一个操作数
pointer--; //指针减一个,对准运算符
m=stack[pointer]; //取出运算符记录
now_opd=calculate(last_opd,last_op,now_opd);//计算
last_op=panop(m); //通过运算符寄存器m判断上一个运算符以便计算
}
result=now_opd; //结果赋给result
Displayresult(result); //显示结果
Displayled(LCDBUF);
}
}
if((key>=0)&&(key<=0x0e)) //当按键为数字处理键时
{ for(z=0;z<16;z++) //清理显示缓冲区 以免处理时显示混乱
{LCDBUF[z]=0xfe;}
if(key==sign){sign_flag=1;
if(E_flag==0){LCDBUF[0]=0x2D;}
if(E_flag==1){LCDBUF[10]=0x2D;}} //负号的显示 设立正负号标志
if(key==E){E_flag=1;LCDBUF[9]=0x45; } //E
if(key==zero)
{last_opd=now_opd=0; //数据寄存器清零 (清零键)
m=0; //运算符记录寄存器清零
last_op=0;/*op=0;*/ //上一个运算符清零
result=0; //结果清零
for(z=0;z<100;z++)
{stack[z]=0;} //清理数据存储区
//清理所有的标志位,相当于复位
i=0;n=0;In=0; //输入数据寄存器清零
j=0; //小数点位置记录清零
t=1; //左括号连续输入个数复位
pointer=0; //取数据指针复位
E_flag=0; //指数操作标志清零
shift_flag=0; //多功能标志位清零
point_flag=0; //小数点标志为清零
sign_flag=0; //正负标志位清零
for(z=0;z<6;z++) //清理显示缓冲区
{LCDBUF[z]=0xfe;}
Displayled(LCDBUF);
}
if(key==point){point_flag=1;} //设立小数点标志号
if(key==back){ if(E_flag==1) {n=n/10;i--;} //退格键处理 在指数操作时指数退一格
if(E_flag==0) {In=In/10; //底数操作时,底数退一格
if(i>0){i--;} //输入的有效数据位数减少一个
if(j>0){j--;} } //输入的小数点的位置后移一位
for(z=0;z<16;z++) //清理显示缓冲区
{LCDBUF[z]=0xfe;}
if(E_flag==1){LCDBUF[9]=0x45; //指数操作标志
if(sign_flag==1)LCDBUF[10]=0x2D; //
}
if((last_key==Left)||(last_key==back))
{
if(m>0x80)
{
m=m-0x10;t--;LCDBUF[0]=0x28;LCDBUF[1]=t+0x30;
if(t==0){m=m&0x0f;last_op=panop(m);}} } //如果多输入了左括号,可以通过此键消除多余的左括号
}
if((key>=0)&&(key<=9)) //纯数字处理
{
if(E_flag==1) //指数输入界面
{
if(i<2)
{
n=abs(n); //考虑到浮点数的取值范围,指数最多只能输入两位
n=n*10+key; //指数计算
i++;
if(sign_flag==1){n=-abs(n);} //数据取正值以便显示处理
}
}
else if(E_flag==0) //底数部分输入
{ if(i<7)
{
In=labs(In); //可以输入7位有效数据
In=In*10+key; i++; //i记录已经输入的有效数的位数
if((In==0)&&(point_flag==0)) //连续输入几个零,且不是有效数据时,输入的零无效
{i=i-1;} //连续输入几个零且不是有效数则,有效数的个数不计数
if(point_flag==1){j++;} //输入的小数点的位置记录
if(sign_flag==1){In=-labs(In);}
}
}
}
if(E_flag==0)
{
shu=In; //底数屏操作显示
if(shu<0)
{ LCDBUF[0]=0x2D; //底数屏负号的显示在最高位
shu=labs(shu); //数据取正,以便显示处理
}
LCDBUF[y--]=(shu%10)+0x30; //输入的最低位数据处理
shu=shu/10;
if(i>j)
//该数的底数为大于1的浮点数,则前面连续输入几个零时无效,不显示 i为有效数。 { // j为小数点位置
while((y>=1)&&(shu!=0))
{ LCDBUF[y--]=(shu%10)+0x30; //输入的底数据大于一的浮点数
shu=shu/10;
}
if((j>0)&&(j<=6)) //显示小数点 随着输入的位数的变化,小数点的位置也随着变化
{
LCDBUF[ 9-j]=LCDBUF[10-j];
LCDBUF[10-j]=LCDBUF[11-j];
LCDBUF[11-j]=LCDBUF[12-j];
LCDBUF[12-j]=LCDBUF[13-j];
LCDBUF[13-j]=LCDBUF[14-j];
LCDBUF[14-j]=LCDBUF[15-j];
LCDBUF[15-j]=0x2E;
}
Displayled(LCDBUF);
}
else if(i=j) //此种情况是处理底数是小于1的浮点数 如0.0002
{
if(j<=6)
{
for(z=0;z<j;z++)