在上一篇简绍了几个重要的部分,下面先说一下计算器程序的
大体思路
:
/这个计算器没有等于键,按下加号或减号相当于等号。/
- 初始化中断,设置三个变量先用AB表示,值全部为零。 进入输入数字子程序,等待数字输入 数字输入并显示,直到有运算符按下,B为输出值。
- 判断运算符
- 如果是加加号,A=A+B,并让下一个输入的数乘1,显示A,回到数字输入。
- 如果是减号,A=A+B,并让下一个输入的数乘-1,显示A,回到数字输入。
- 如果是乘号,进入乘循环A=A*B,显示A,直到按下加减号。
- 如果是除号,进入除循环A=A/B,显示A,直到按下加减号。
_
_
理论上,这是一个闭环的循环,可以进行无限次计算,占用内存不会扩大。
相关变量的设置
这是最糟糕的。。。自己贪图省事,设置了一大堆变量。。。
这确实有点乱,一些主要的变量都放在这里,后面子程序还有一堆临时变量。
大家不用看这段,只是说明后面都用到了。。。
#include "reg51.h"
long double S;
long double T;
long double U;
long int Z;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;//控制数码管的三位数
sbit ZhengFu=P3^3;//输入正负号的按键,默认输入正数,按下输入负数
int i;
char code DENG_CODE[22]=
{0x3F,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7F,0x6f,
0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF};
//数组,前10个0-9,后10个有小数点下标
#define GPIO_DENG P0//宏定义数码管共阴极(阳极)
#define GPIO_ANJIAN P1//宏定义按键
int h;
long double sz;
unsigned char jia;
unsigned char jian;
unsigned char cheng;
unsigned char chu;
unsigned char dian;
unsigned char KeyValue;
long double A;
unsigned char zhengfu=1;
sbit led=P2^0;//输入负号时的中断指示灯
long double V;
void Delay5ms() 延时函数
{
unsigned char a,b,c;
for(c=1;c>0;c--)
for(b=8;b>0;b--)
for(a=50;a>0;a--);
}
void Delay10ms()//延时函数
{
unsigned char a,b,c;
for(c=1;c>0;c--)
for(b=38;b>0;b--)
for(a=130;a>0;a--);
}
矩阵按键扫描
void KeyDown()//检测按下的是哪个数
{
GPIO_ANJIAN=0x0f;//之前我先宏定义了GPIO_ANJIAN是八个引脚,现在电位00001111
if(GPIO_ANJIAN!=0x0f)//有按键按下
{
Delay10ms();//延迟防抖动
if(GPIO_ANJIAN!=0x0f)//检测行
{
GPIO_ANJIAN=0X0F;
switch(GPIO_ANJIAN)//选择哪一行
{
case(0X07): KeyValue=0;break;
case(0X0b): KeyValue=1;break;
case(0X0d): KeyValue=2;break;
case(0X0e): KeyValue=3;break;
}
GPIO_ANJIAN=0XF0;//反转电位,检测列
Delay10ms();//延时防抖动
switch(GPIO_ANJIAN)//选择哪一列
{
case(0X70): KeyValue=KeyValue;break;
case(0Xb0): KeyValue=KeyValue+4;break;
case(0Xd0): KeyValue=KeyValue+8;break;
case(0Xe0): KeyValue=KeyValue+12;break;
}
while((h<500)&&(GPIO_ANJIAN!=0xf0)) //直到按键抬起,程序才向下进行。
{
Delay10ms();
h++;
}
h=0;
}
}
switch(KeyValue)//让按键和其所对应的数值,运算符相匹配
{
case(0):sz=1;break;//s1
case(1):sz=2;break;//s2
case(2):sz=3;break;//s3
case(3):jia=1;sz=0;break;//加.s4
case(4):sz=4;break;//s5
case(5):sz=5;break;
case(6):sz=6;break;
case(7):jian=1;sz=0;break;//减
case(8):sz=7;break;
case(9):sz=8;break;
case(10):sz=9;break;
case(11):cheng=1;sz=0;break;//乘
//case(12):sz=0;break;
case(13):sz=0;break;
case(14):dian=1;sz=0;break;//小数点
case(15):chu=1;sz=0;break;//除,s16
}
}
插入图片。。。。
数码管动态显示
这个最费脑子了。。。它能实现小数的显示,并且保证在八位数之内整数显示的完整性
还能显示正负。(我自己都佩服我自己了 )思路是:
先判断正负
如果是正数进入下面程序;如果是负数,在最左位打上符号,再让它变为正数。
处理数字
先让小数变为整数,并记住它是几位数。
将该出现小数的那一位把数替换为带小数点的数并显示
循环上面的程序,直到按下按键。
void xianshi()
{
long int F;
long int G;
int l=1;
int X;
Z=10000000;//这里设置一个最大值
if(S>0)//数大于零时
{
G=S;
while((S<Z)&&(l<8))
{S=S*10;l++;}//求出小数点应该点在八位数的哪一位。
while((GPIO_ANJIAN==0xf0)&&(ZhengFu==1))//直到有按键按下
{
F=S;//强制类型转换,因为只能整数取余
switch(l)//选择显示有小数点的那一位
{
case(1):
LSA=0;LSB=0;LSC=0;GPIO_DENG=0x00;break;//数码管消隐
case(2):
LSA=1;LSB=0;LSC=0;break;
case(3):
LSA=0;LSB=1;LSC=0;break;
case(4):
LSA=1;LSB=1;LSC=0;break;
case(5):
LSA=0;LSB=0;LSC=1;break;
case(6):
LSA=1;LSB=0;LSC=1;break;
case(7):
LSA=0;LSB=1;LSC=1;break;
case(8):
LSA=1;LSB=1;LSC=1;break;
}
X=G/1%10+10;//取余,获取当前数字的个位数,即带有小数点的那一位
GPIO_DENG=DENG_CODE[X]; //显示该带点数字
Delay5ms();
for(i=1;i<9;i++)//一位一位的显示数字
{
switch(i)
{
case(1):
LSA=0;LSB=0;LSC=0;GPIO_DENG=0x00;break;
case(2):
LSA=1;LSB=0;LSC=0;break;
case(3):
LSA=0;LSB=1;LSC=0;break;
case(4):
LSA=1;LSB=1;LSC=0;break;
case(5):
LSA=0;LSB=0;LSC=1;break;
case(6):
LSA=1;LSB=0;LSC=1;break;
case(7):
LSA=0;LSB=1;LSC=1;break;
case(8):
LSA=1;LSB=1;LSC=1;break;
}
X=F/1%10;
F=F/10; //从最低位开始
GPIO_DENG=DENG_CODE[X]; //显示对应数字
Delay5ms();
GPIO_DENG=0x00; //消隐
}
}
}
if(S<0)//数小于零时,除了最高位显示负号,最高位是7位外,其他步骤同上
{
S=-S;
G=S;
while((S<Z/10)&&(l<7))
{S=S*10;l++;}
while((GPIO_ANJIAN==0xf0)&&(ZhengFu==1))
{
LSA=1;LSB=1;LSC=1;
GPIO_DENG=0X40;
Delay5ms();
GPIO_DENG=0X00;
F=S;
switch(l)
{
case(1):
LSA=0;LSB=0;LSC=0;GPIO_DENG=0x00;break;
case(2):
LSA=1;LSB=0;LSC=0;break;
case(3):
LSA=0;LSB=1;LSC=0;break;
case(4):
LSA=1;LSB=1;LSC=0;break;
case(5):
LSA=0;LSB=0;LSC=1;break;
case(6):
LSA=1;LSB=0;LSC=1;break;
case(7):
LSA=0;LSB=1;LSC=1;break;
case(8):
LSA=1;LSB=1;LSC=1;break;
}
X=G/1%10+10;
GPIO_DENG=DENG_CODE[X];
Delay5ms();
for(i=1;i<8;i++)
{
switch(i)
{
case(1):
LSA=0;LSB=0;LSC=0;GPIO_DENG=0x00;break;
case(2):
LSA=1;LSB=0;LSC=0;break;
case(3):
LSA=0;LSB=1;LSC=0;break;
case(4):
LSA=1;LSB=1;LSC=0;break;
case(5):
LSA=0;LSB=0;LSC=1;break;
case(6):
LSA=1;LSB=0;LSC=1;break;
case(7):
LSA=0;LSB=1;LSC=1;break;
case(8):
LSA=1;LSB=1;LSC=1;break;
}
X=F/1%10;
F=F/ 10;
GPIO_DENG=DENG_CODE[X];
Delay5ms();
GPIO_DENG=0x00;
}
}
}
if(S==0)//等于零时,直接显示零
{
while((GPIO_ANJIAN==0xf0)&&(ZhengFu==1))
{
LSA=1;LSB=1;LSC=1;
GPIO_DENG=0X3f;
Delay5ms();
}
}
}
中断控制输入数字正负
void Int1Init()
{
IT1=1;//触发方式下降沿
EX1=1;//打开中断INT1允许
EA=1;//打开总中断
}
void Int1() interrupt 2 //外部中断1
{
Delay10ms();
if(ZhengFu==0)
{
zhengfu=-zhengfu;//正负标志改变
led=~led;//LED灯反转
}
}
数字的输入
具体思路:
持续循环以下函数,直到有运算符被按下。
等待按下数字键或小数点键
判断是否按过下小数点
若没按下,之前数乘十加现在的数
若按下,现在的数除10的N次方(从第一次按下小数点到现在。)加原来的数
按下运算符键后判断正负标志,输出数字。
void shu()
{
int Q=0;//累计小数点位数的变量
int R=1;
long int w;
A=0;
while((jia==jian)&&(cheng==chu))
{
while(GPIO_ANJIAN==0xf0)//等待按键按下
{
w++;
w--;
}
KeyDown();//按键按下
if((jia==jian)&&(cheng==chu))
{
switch(dian)//判断是否按下过小数点键
{
case(0):
A=A*10+sz;
S=A;
xianshi();//数码管显示
break;
case(1):
Q++;
while(R<Q)//得到数应在小数点后的位数
{
sz=sz/10;
R++;
}
R=1;
A=A+sz;
S=A;
xianshi();
break;
}
}
}
if(zhengfu==-1)//判断正负
{
A=-A;
}
zhengfu=1;//正负复原
led=1;
dian=0;小数点复原
}
数字计算
大体思路已在文章最前面提到。
void suan()//
{
if(jia==1)//加法
{
while((h<500)&&(GPIO_ANJIAN!=0xf0))//等待按键抬起
{Delay10ms();h++;}
h=0;
T=T+A;
jia=0;
S=T;
zhengfu=1;
xianshi();
}
if(jian==1)//减法
{
while((h<500)&&(GPIO_ANJIAN!=0xf0))
{Delay10ms();h++;}
h=0;
T=T+A;
zhengfu=-1;
jian=0;
S=T;
xianshi();
}
if((chu==1)||(cheng==1))//乘除循环
{
while((h<500)&&(GPIO_ANJIAN!=0xf0))
{Delay10ms();h++;}
h=0;
while(jia==jian)//再按下加减号之前,一直循环
{
if(cheng==1)//乘法
{
V=A;
cheng=0;
shu();//等待下一个数字
A=A*V;
}
if(chu==1)//除法
{
V=A;
chu=0;
shu();
A=V/A;
}
}
T=T+A;//把之前的数和乘除中断里的数加起来
}
}
主函数&总结
void main(void)//
{
GPIO_ANJIAN=0xf0;
while(1)//大的顺序循环
{
Int1Init();
shu();
suan();
}
}
总结:一开始不熟悉C语言,按照面向对象的方式去了。。。。。
后来开始适应,写C语言要有整体思路也要时刻想着下一步具体是什么。
应先把数码管显示程序写出来,如果最后写它,结果即便程序没语法错误也不能运行起来,想看哪出错也没法看,先把数码管显示写出来与每一部分结合能直观的看出效果,查找错误。
S1到S11三成三是1到9
S14 0
S15 点
左侧一列 加减乘除
k4 正负控制。