51单片机(AT89S52)设计简单计算器(4位加减乘除)

(一)前言

  本文是简单计算器的设计。使用的是AT89S52芯片,C语言。
  目的设计一个简易计算器,以能够进行4位BCD码的加减乘除整数运算,且能够在溢出范围时做出错提醒。用8位数码管动态显示,实现人机交互功能。开机时显示“0”,数字左移显示。计算器功能通过软件实现,以C语言对89S52单片机进行编程以实现所需要的计算器的设计。
  本文使用JY2E00编译环境,当然也可以用keil4等。C语言编写程序后通过JY-E2300C仿真器连接硬件,选择AT89S52单片机和8155,输入采用两个2*8的矩阵键盘。显示用6位8段共阴极LED动态显示

(二)设计目的

  (1) 8位数码管显示,开机时显示“0”,数字左移显示;
  (2) 4位BCD码加减乘除,整数运算;
  (3) 运算超范围出错提示
  (4)2*8矩阵键盘,其中10个数字键,4个加减乘除运算功能键,1个等于键,1个清零键

(三)硬件及其电路

  主要硬件设备:AT89S52单片机、8155、数码管、矩阵键盘
  其中AT89S52单片机的电路网上有很多,只需要正常提供时钟,搭建复位电路即可

(1)时钟电路

  单片机内部有用于构成振荡器的高增益反相放大器,引脚XTAL1和XTAL2分别是此放大器的输入端和输出端。采用内部方式时钟电路,即外接晶体以及电容C1,C2构成并联谐振电路,接在放大器的反馈回路中,内部振荡器产生自激振荡。
时钟电路

(2)复位电路

  使用上电复位电路,即上电瞬间,RST端的电位与Vcc相同,随着电容逐步充电而充电电流减小,RST电位逐渐下降。上电复位所需的最短时间是振荡器建起时间加上两个机器周期,在这段时间内RST端口的电平需要维持高于施密特触发器的下阈值。
复位电路

(3)数码管显示电路

  本次项目需要显示数据量少,故采用LED数码管进行显示,经济且实用。数码管显示有静态显示和动态显示两种方法。
  为了减少端口的使用,故选择静态显示,并采用共阴极接法电路,并通过74LS240和8155相连且受单片机控制。
  数码管显示电路中,SW3、SW4红色拨码开关打在“ON”位置,数码管代码端和公共端与 8155PA、 PB 口相连。如果 SW3、 SW4 红色拨码开关打在相反位置,即“ OFF”位置,数码管电路与 8155 断开,数码管代码端和公共端对外开放。
数码管显示电路

(4)8155扩展I/O口电路

  8155位可编程并行I/O接口芯片,具有两个8位和一个6位I/O口,以及256字节RAM和一个14位计数器。本次课设只用到了三个扩展I/O口,即将单片机的P0口与8155的输入相连,PA,PC输出给矩阵键盘电路,PB口控制数码管位选口。
8155扩展I/O口电路

(5)矩阵键盘电路

  键盘可分为两类:编码键盘和非编码键盘。编码键盘是较多按键和专用驱动芯片的组合,当按下某个按键时能够处理按键抖动、连击等问题,直接输出按键的编码,无需系统软件干预。当系统功能比较复杂,按键数量很多时,采用编码键盘可以简化软件设计。但在需要按键数不多时,为了降低成本和简化电路通常采用非编码键盘。非编码键盘的接口电路有设计者根据需要自行决定,按键信息通过接口软件来获取。
  本项目需要的16个按键,用两个28即可,即由8155的PC口和PA口采用两个28矩阵式键盘。
矩阵键盘电路

(四)系统程序设计

(1)主程序思路

  首先定义需要用到的数码管对应显示的16进制编码、键盘编码和各个需要用到的数组和参量。进入主程序,首先对com8155控制字进行写入,再执行清除命令以清除输入,这两步为系统初始化。然后进入while中,首先进行数码管显示和输入数值计算,然后判断是否输入超过四位,如超过则跳入报错的子程序中,数码管显示ERROR;如未溢出,则继续进行,执行键盘扫描程序。
  进入switch判断,对应不同的keycode分别进行四则运算和清零操作。完成后回到while重复执行。
  下面是主函数及一些子函数定义声明和变量定义。

#include<reg51.h>
#include<absacc.h>
#include<intrins.h>
#define com8155 XBYTE[0xff20]
#define pa8155 XBYTE[0xff21]
#define pb8155 XBYTE[0xff22]
#define pc8155 XBYTE[0xff23]
unsigned char Led_code[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x79,0xf7,0x00,0x40,0x76,0x38};
unsigned int Led_store[]={0,0,0,0,0,0};
unsigned int Key_code[]={7,4,8,5,9,6,10,11,1,0,2,15,3,14,12,13,0};
long int Inputnum[]={0,0,0,0,0};
long int Outputnum;
int a,i,temp1,temp2,temp3,temp4;
int keynum=0;
int keycode=16;
void Led_display(void);
void keyscan(void);
void if_keyin(void);
void clear_input(void);
void add_compute(void);
void sub_compute(void);
void mul_compute(void);
void dev_compute(void);
void if_error(void);
void delay(int xms);
void out_high_display(void);
void out_low_display(void);
void input_B(void);

void main()
{
com8155=0x43;
clear_input();
while(1)
{
 Led_display();  //显示
 Inputnum[0]=Led_store[2]+Led_store[3]*10+Led_store[4]*100+Led_store[5]*1000; //输入值计算
 if(keynum>4) if_error();
 keyscan(); //键盘输入扫描
switch(keycode)
  {
    case 11: clear_input(); break;
    case 6:  add_compute(); break;
    case 7:  sub_compute(); break;
    case 14: mul_compute(); break;
    case 15: dev_compute(); break;
    default: break;
  }
 }
}

(2)LED动态显示子程序流程图

  LED动态显示程序执行流程如下:设置参量并赋初值,进入for循环以实现6位数码管的动态位选,进入switch中进行对应数码管的段选显示,延时继续循环进行。

void Led_display()
{
int temp,j;
  temp=0x01;
  j=0;
for(i=0;i<6;i++)
{
  temp=~temp;
  pa8155=temp;
 switch(Led_store[j])
{
 case 0:pb8155=~Led_code[0];break;
 case 1:pb8155=~Led_code[1];break;
 case 2:pb8155=~Led_code[2];break;
 case 3:pb8155=~Led_code[3];break;
 case 4:pb8155=~Led_code[4];break;
 case 5:pb8155=~Led_code[5];break;
 case 6:pb8155=~Led_code[6];break;
 case 7:pb8155=~Led_code[7];break;
 case 8:pb8155=~Led_code[8];break;
 case 9:pb8155=~Led_code[9];break;
 case 10:pb8155=~Led_code[10];break;
 case 11:pb8155=~Led_code[11];break;
 case 12:pb8155=~Led_code[12];break;
 case 13:pb8155=~Led_code[13];break;
 case 14:pb8155=~Led_code[14];break;
 case 15:pb8155=~Led_code[15];break;
 default: break;
}
j++;
temp=~temp;
temp=_crol_(temp,1);
delay(2);
pb8155=0xff;
 }
}

(3)键盘扫描子程序流程图

  首先判断键盘中有无按键按下。即由PA口输出0x00,再读取PC口状态,若PC低四位不全为1,则有键按下,继续进行消抖程序,即当发现有键按下时,延时一段时间后再判断键盘的状态,若仍有键保持按下状态,则断定有键按下,否则认为是抖动。然后进行扫描求键号,即从PA口输出0xfe并左移,扫描PC口状态读入,进行键号求解并存入Led_store[0]中。然后再等待闭合键的释放,若释放后则认为完成一次按键输入捕获,此时将按键输入存入Led_store数组中,记录keynum++即代表取入一个BCD码。

void keyscan()
{
 int line=0;
 int temp;
 while(a==1)
 {
  do
  {   Led_display();
      if_keyin();
      delay(5);
  }while(a);
	delay(6);//12ms
    if_keyin();
 }
	 pa8155=0xfe;
	 ACC=pa8155;
    do{
        temp=pc8155;
        temp=temp&0x0f;
	 if(temp==0x0e)
	 {keycode=line;
	  break;};
	 if(temp==0x0d)
	 {keycode=8+line;
	 break;};
	 pa8155=pa8155<<1;
	 line++;
      }while(ACC^0);
	Led_store[0]=Key_code[keycode];
    while(a==0)
    {if_keyin();
     Led_display();
    }
    Led_store[5]=Led_store[4];
    Led_store[4]=Led_store[3];
    Led_store[3]=Led_store[2];
    Led_store[2]=Led_store[0];
    Led_store[1]=0;
    Led_store[0]=Led_store[0];
    keynum++;
}

void delay(int xms)
{
 int x,y;
 for(x=xms;x>0;x--)
 for(y=110;y>0;y--);
}

void if_keyin()
{
    int b;
	a=1;
	pa8155=0x00;
	b=pc8155&0x0f;
	if(b!=0x0f) a=0;
}

(4)溢出报错子程序

  定义无符号整数数组temp存入“ERROR”的段选码,并循环存入Led_store数组中,该程序在程序溢出范围时执行,将显示ERROR在高5位数码管,最低位不显示。

void if_error()
{
    unsigned int temp[] ={10,11,11,0,11,12};
    int i;
    keynum=0;
    for(i=5;i>=0;i--)
    Led_store[i]=temp[5-i];
}

(5)清零子程序

  循环清零Led_store数组,并清零keynum,该程序在按下清零键时执行,将显示000000,即可重新输入。

void clear_input()
{
    int i;
    keynum=0;
    for(i=0;i<6;i++)
    Led_store[i]=0;
}

(6)运算数值输入计算子程序

  该子程序在按下四个功能运算键后执行,计算输入的第二个值,如加数、减数、乘数和除数。具体流程如下:keynum清零,并将Inputnum[0]存入Inputnum[1]中,执行清零子程序清零Inputnum[0],进入while循环,输入按键捕获并运算Inputnum[0]的值,当有等于按键按下时,退出循环,并将Inputnum[1]存入Inputnum[2],Inputnum[0]存入Inputnum[1],此时第一个值在Inputnum[2]中,第二个值在Inputnum[1]中。如果keynum>4,即第二次输入超过四位,则执行溢出报错程序并退出循环。

void input_B()
{
     keynum=0;
     Inputnum[1]=Inputnum[0];
     clear_input();
    while(1)
   {
     if(keycode==13) break;
     Inputnum[0]=Led_store[2]+Led_store[3]*10+Led_store[4]*100+Led_store[5]*1000;
     if(keynum>4)
     {if_error();
       break;}
     keyscan();
   }
     Inputnum[2]=Inputnum[1];
     Inputnum[1]=Inputnum[0];
}

(7)加法计算子程序

  加法计算子程序在按下加法按键后执行,具体流程如下:执行第二个值输入计算子程序,计算求和结果并存入Inputnum[3]中。然后判断当按键输入不是清零时,进入while循环,首先进行按键扫描捕获子程序,并清零keynum,顺序执行先显示高四位子程序,再执行显示低四位子程序,清零temp,最后执行输入清零子程序。这其中只要不按下清零按键,按其他任何键均重复显示运算结果,按下清零键则退出此次运算。

void add_compute()
{
      input_B();
      Inputnum[3]=Inputnum[2]+Inputnum[1];
while(keycode!=11)
{
     keyscan();
     keynum=0;
     while(temp1<100 && keycode!=11)
     {
      Led_store[0]=12;
      out_high_display();
      temp1++;
      }
     while(temp1<150 && keycode!=11)
     {
      Led_store[0]=12;
      out_low_display();
      temp1++;
     }
     temp1=0;
}
     clear_input();
}

(8)减法计算子程序

  减法计算子程序在按下加法按键后执行
  具体流程如下:先定义一个标志位用于记录结果的正负号,然后执行第二个值输入计算子程序,进入计算判断中,如果被减数大于减数,则计算Inputnum[2]-Inputnum[1]并存入Inputnum[3],记录标志位为12,即为LED不显示的段选码;如果被减数小于减数,则计算Inputnum[1]-Inputnum[2]并存入Inputnum[3],记录标志位为13,即为LED显示“-”的段选码。然后判断当按键输入不是清零时,进入while循环,首先进行按键扫描捕获子程序,并清零keynum,顺序执行先显示高四位子程序,再执行显示低四位子程序,清零temp,最后执行输入清零子程序。这其中只要不按下清零按键,按其他任何键均重复显示运算结果,按下清零键则退出此次运算。

void sub_compute()
{
     int sign;
     input_B();
      if(Inputnum[2]>Inputnum[1])
        {
        Inputnum[3]=Inputnum[2]-Inputnum[1];
        sign=12;
        }
      else
       {
        Inputnum[3]=Inputnum[1]-Inputnum[2];
        sign=13;
       }
while(keycode!=11)
{
     keyscan();
     keynum=0;
     while(temp2<100 && keycode!=11)
     {
      Led_store[0]=sign;
      out_high_display();
      temp2++;
      }
     while(temp2<150 && keycode!=11)
     {
      Led_store[0]=sign;
      out_low_display();
      temp2++;
     }
     temp2=0;
}
     clear_input();
}

(9)乘法计算子程序

  乘法计算子程序在按下乘法按键后执行,具体流程如下:执行第二个值输入计算子程序,计算求乘积结果并存入Inputnum[3]中。然后判断当按键输入不是清零时,进入while循环,首先进行按键扫描捕获子程序,并清零keynum,顺序执行先显示高四位子程序,再执行显示低四位子程序,清零temp,最后执行输入清零子程序。这其中只要不按下清零按键,按其他任何键均重复显示运算结果,按下清零键则退出此次运算。

void mul_compute()
{
     input_B();
     Inputnum[3]=Inputnum[2]*Inputnum[1];
while(keycode!=11)
{
     keyscan();
     keynum=0;
     while(temp3<100 && keycode!=11)
     {
      Led_store[0]=12;
      out_high_display();
      temp3++;
      }
     while(temp3<150 && keycode!=11)
     {
      Led_store[0]=12;
      out_low_display();
      temp3++;
     }
     temp3=0;
}
     clear_input();
}

(10)除法计算子程序

  除法计算子程序在按下乘法按键后执行,具体流程如下:执行第二个值输入计算子程序,然后进行判断除数是否为零,如果除数为零则执行溢出报错子程序;如果不为零则继续执行。然后判断当按键输入不是清零时,进入while循环,首先进行按键扫描捕获子程序,并清零keynum
  然后计算Inputnum[3]=Inputnum[2]/Inputnum[1]*10000,并顺序执行先显示高四位子程序,即Inputnum[3]中存入的是商,且位于高四位;
  再计算Inputnum[3]=Inputnum[2]%Inputnum[1],即Inputnum[3]中存入的是余数,再执行显示低四位子程序。然后清零temp,最后执行输入清零子程序。这其中只要不按下清零按键,按其他任何键均重复显示运算结果,按下清零键则退出此次运算。

void dev_compute()    //高位为商,低位为余
{
     input_B();
     if(Inputnum[1]==0) if_error();
while(keycode!=11)
{
     keyscan();
     keynum=0;
     while(temp4<100 && keycode!=11)
     {
      Led_store[0]=12;
      Inputnum[3]=Inputnum[2]/Inputnum[1]*10000;
      out_high_display();
      temp4++;
      }
     while(temp4<200 && keycode!=11)
     {
      Led_store[0]=12;
      Inputnum[3]=Inputnum[2]%Inputnum[1];
      out_low_display();
      temp4++;
     }
     temp4=0;
}
     clear_input();
}

(11)显示子程序

  因为本项目硬件只利用了六位数码管,而4位BCD码的运算结果有8位,故分开显示高四位和低四位。看看代码即可理解。当然硬件资源够的情况下直接显示全部结果最佳。

void out_high_display()
{
    Outputnum=Inputnum[3]/10000;
    Led_store[5]=Outputnum/1000;
    Led_store[4]=(Outputnum%1000)/100;
    Led_store[3]=(Outputnum%100)/10;
    Led_store[2]=Outputnum%10;
    Led_store[1]=14;
    Led_display();
}

void out_low_display()
{
    Outputnum=Inputnum[3]%10000;
    Led_store[5]=Outputnum/1000;
    Led_store[4]=(Outputnum%1000)/100;
    Led_store[3]=(Outputnum%1000%100)/10;
    Led_store[2]=Outputnum%1000%100%10;
    Led_store[1]=15;
    Led_display();
}

(五) 结束语

  本文仅仅针对该项目作一个思路解析与程序设计,读者们可以从中借鉴一些,想着大家都是搞课设而来的。
  本套方案设计还有很多不足之处,比如不能多次运算和有优先级的运算,这些在其他博主都会有讲解,需要的大家去看看,我这个设计比较简单需求不多。
  谢谢大家,请不要转载,仅供学习参考,非常欢迎一起交流。

  • 37
    点赞
  • 264
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TaiBai-let

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值