51单片机简易计算器(中断,矩阵按键,数码管显示)【1】

在上一篇简绍了几个重要的部分,下面先说一下计算器程序的

大体思路


/这个计算器没有等于键,按下加号或减号相当于等号。/

  • 初始化中断,设置三个变量先用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 正负控制。

  • 9
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值