单片机交流
直播中

h1654155275.5614

7年用户 731经验值
私信 关注
[问答]

如何利用STC89C52单片机做出一个简易的计算器?

如何利用STC89C52单片机做出一个简易的计算器?

回帖(1)

李亮涟

2021-9-28 14:51:45
基于51单片机的简易计算器

经历了一番学习,成功做出了一个简易的计算器,并且在程序开头加了一个小密码。我用的单片机是STC89C52,单片机类型不同,电路图也是不一样的。
一.设计思路

1.1LCD1602的电路图






该芯片引脚,

***it RS = P3^5; //液晶引脚定义
***it RW = P3^6;
***it EN = P3^4;
#
1.2主函数思路

主函数的思路图:





1.3主函数程序


main()
{
uchar i,num,sign,s;
uchar temp[16];
bit firstflag;
float a=0,b=0;
Lcd_Init();
DelayMs(10);
LcdWrite_cmd(0x01);
LcdWrite_string(5,0,"WELCOM !");
LcdWrite_string(3,1,"LI HUA HUI");
for(s=0;s<25;s++)
DelayMs(200);
LcdWrite_cmd(0x01);
mima();
DelayMs(200);
LcdWrite_string(0,1,"                ");//清除该行
  LcdWrite_string(0,1,"Right Open!>>>>");//密码正确显示的信息
for(s=0;s<25;s++)
DelayMs(200);
LcdWrite_cmd(0x01);
while(1)
{
num=KeyPro();
if(num!=0xff)
{
  if(i==0)
  LcdWrite_cmd(0x01);
   if(('+'==num) || ('-'==num) || ('*'==num) || ('/'==num) || ('='==num) || (i==16))
    {
    i=0;
    LcdWrite_char(0,1, num);//应该输入字符,而不是字符串
    if(firstflag==0)
    {
     sscanf(temp,"%f",&a);
     firstflag = 1;
    }
    else
    {
     sscanf(temp,"%f",&b);
    }
    for(s=0;s<16;s++)
     temp=0;
    if(num!='=')
     sign=num;
    else
    {
     switch(sign)
     {
      case '+': a=a+b;  break;
      case '-': a=a-b;  break;
      case '*': a=a*b;  break;
      case '/': a=a/b;  break;
      default:          break;
     }
     sprintf(temp,"%g",a);
     LcdWrite_string(4,1,temp);
     firstflag = 0;
     num=0;a=0;b=0;sign=0;
     for(s=0;s<16;s++)
     {
      temp=0;
     }
    }
   
    }
   else if(i<16)
   {
    if((1==i) && (temp[0]=='0'))
    {
     if(num == '.' )
     {
      temp[1]='.';
      LcdWrite_char(1,0,num);
      i++;
     }
     else
     {
      temp[0]=num;
      LcdWrite_char(0,0,num);//
      
     }
    }
    else
        {
         temp=num;
         LcdWrite_char(i,0,num);//输出数据
         i++;   //输入数值累加
        }     
   }
  }
}
}
1.3 初始化


void LCD_Busy()//判忙函数
{
uchar busy;
P0=0Xff;
RS=0;
RW=1;
do
{
  EN=1;
  busy=P0;
  EN=0;
}
while(busy & 0x80);
}
void LcdWrite_cmd(uchar cmd)
{
Lcd_Busy();
RS=0;
RW=0;
P0=cmd;
EN=1;
EN=0;
}
void LcdWrite_date(uchar date)
{
Lcd_Busy();
RS=1;
RW=0;
EN=1;
P0=date;
EN=0;
}
void LcdWrite_char(uchar x,uchar y,uchar date)//写入字符
{
Lcd_Busy();
if(y == 0)//=' 就是赋值‘==’ 就是判断数值是否相等
{
  LcdWrite_cmd(0x80 + x);
}
else
{
  LcdWrite_cmd(0xC0 + x);
}
LcdWrite_date(date);
}

void LcdWrite_string(uchar x,uchar y,uchar *s)//写入字符串
{
while(*s)
{
  LcdWrite_char(x,y,*s);
  s++;
  x++;
}
}
void DelayMs(int z)// 1ms延时函数
{
int x,y;
for (x=z;x>0;x--)
   for(y=110;y>0;y--);
}
void Lcd_Init()//初始化
{
LcdWrite_cmd(0x38);
DelayMs(5);
LcdWrite_cmd(0x06);
DelayMs(5);
LcdWrite_cmd(0x0C);
LcdWrite_cmd(0x01);
}



1.4 键盘扫描

本文采用的是线反转法

uchar KeyScan()
{
uchar value_h,value_l;
P3=0Xf0;
if((P3&0xf0)!=0xf0)//判断是否有按键按下
{
  DelayMs(5);
  if((P3&0xf0)!=0xf0);//判断按键是否有效
  {
   value_l=P3 & 0Xf0;
   P3=value_l | 0x0f;
   value_h=P3 & 0x0f;
    while( (P3 & 0x0f) != 0x0f );//松手检测
    return(value_l+value_h);
  }
  
}
}
uchar  KeyPro()
{
switch( KeyScan() )
{
   //第一行键值码
  case 0xee: return '1';  break;
  case 0xde: return '2';  break;
  case 0xbe: return '3';  break;
  case 0x7e: return '+';  break;
  
  //第二行键值码
  case 0xed: return '4';  break;
  case 0xdd: return '5';  break;
  case 0xbd: return '6';  break;
  case 0x7d: return '-';  break;
  //第三行键值码
  case 0xeb: return '7';  break;
  case 0xdb: return '8';  break;
  case 0xbb: return '9';   break;
  case 0x7b: return '*';   break;
  //第四行键值码
  case 0xe7: return '0';   break;
  case 0xd7: return '.';   break;
  case 0xb7: return '=';   break;
  case 0x77: return '/';   break;
  default:return 0xff;break;
}
}
1.5 密码程序

unsigned char code password[]={'2','2','3','3','4','4','5','5'};
void mima()
{
unsigned char num,i,j;                  
unsigned char passwordtemp[16];        //最大输入16个
unsigned char inputtimes;              //密码输入错误次数
unsigned char passwordlength,PLEN;     //输入密码长度,实际密码长度
bit Flag;
PLEN=sizeof(password) / sizeof(password[1]);//  //数组占内存总空间,除以单个元素占内存空间大小
Lcd_Init();         //初始化液晶屏
DelayMs(10);        //延时用于稳定,可以去掉
LcdWrite_cmd(0x01); ;        //清屏
LcdWrite_string(0,0,"  Welcome! ");    //写入第一行信息,主循环中不再更改此信息,所以在while之前写入
LcdWrite_string(0,1,"Input password!");    //写入第二行信息,提示输入密码
while (1)         //主循环
{
  num=KeyPro();  //扫描键盘
  if(num!=0xff)  //如果扫描是按键有效值则进行处理
    {
      if(i==0)    //输入是第一个字符的时候需要把改行清空,方便观看密码
    LcdWrite_string(0,1,"                ");//清除该行
    if(i<16)
    {
         passwordtemp=num;
         LcdWrite_char(i,1,'*');//输入的密码用"*"代替
    i++;
     }
  //i++;   //输入数值累加
  if(('='==num)|| (i==16))//输入按键值15或者密码输入到最大值16,表示输入结束,需要进行比对
   {
        passwordlength=i-1;  //计算输入密码长度
     i=0;  //计数器复位
     if(passwordlength==PLEN)//长度相等则比较,否则直接输出错误
      {
        Flag=1;//先把比较位置1
        for(j=0;j            Flag=Flag&&(passwordtemp[j]==password[j]);//比较输入值和已有密码
          }
          if(Flag==1)break;
        if(Flag)//如果比较全部相同,标志位置1
           {
          inputtimes=0;//输入正确则次数清零,重新计数
          Flag=0;
                //清除正确标志
         }
     else
      {
    LcdWrite_string(0,1,"                ");//清除该行
    LcdWrite_string(0,1,"Wrong! Retry!");//密码错误,提示重新输入
    inputtimes++;//连续输入错误,则次数累加
    if(inputtimes==3)
       {
        LcdWrite_string(0,1,"                ");//清除该行
        LcdWrite_string(0,1,"Wrong 3 times!");//密码错误,提示重新输入
        while(1);//停止该位置,重启电源后才能输入,实际实用中则需要等到一定时间后才能再次输入。
        }
       }
      }
     }
    }
   }
二.成果显示

开机显示,要求输入密码





密码输入,密码比较简单,是12345678




密码输入成功,如果输入错误3次,就不能再输入,只能关机再开机










进入计算界面,并且输入计算的数字





LCD只有两行,第一行输入显示运算的两个数字,功能键显示在第二行的第一个位子,结果从第二行的第二个位子开始显示。
被减数和减号





减数和结果


举报

更多回帖

×
20
完善资料,
赚取积分