基于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只有两行,第一行输入显示运算的两个数字,功能键显示在第二行的第一个位子,结果从第二行的第二个位子开始显示。
被减数和减号

减数和结果

基于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只有两行,第一行输入显示运算的两个数字,功能键显示在第二行的第一个位子,结果从第二行的第二个位子开始显示。
被减数和减号

减数和结果

举报