使用51单片机实现简单的四则运算(只包含+ - * / 和 =)
由于51单片机数据存储空间有限,所以不能开辟过多的数据空间,C语言处理四则运算的代码无法直接通过改写,编译烧录。故此次实验采用状态转换的方式实现计算器的简单四则运算。使用数据空间3个:a, b, c。即可实现。下图为状态转换图。
在程序实现过程中,如果遇到’=’则需要显示结果到显示屏,这部分说明请看代码相应位置。对于错误输入在各自状态有相应处理过程,如不存储该错误输入等。此外,在程序实现过程中添加 状态11:用于清屏重置;状态12:ERROR状态。下面介绍接线及运行结果。
/**************************************************************************************
* 简易四则运算计算器 *
* *
* 仪器:普中科技 单片机开发实验仪 *
* 连接方法:Jp8连JP4 Jp9连Jp5 把1602液晶插入 *
* *
***************************************************************************************/
#include //此文件中定义了51的一些特殊功能寄存器
#define uchar unsigned char
#define uint unsigned int
#define MAXLEN 16
sbit EN=P2^7; //LCD的使能引脚
sbit RS=P2^6; //LCD数据命令选择端
sbit RW=P2^5; //LCD的读写选择端
sbit K1=P3^0;
sbit K2=P3^1;
sbit K3=P3^2;
sbit K4=P3^3;
sbit K8=P3^7; // 清屏,重置变量
bit flag = 0; // + 或 - 标识符; 0:+;1:-;
uchar KEY_CODE[]={ 0xed,0xdd,0xbd,0x7d,//3X4矩阵键盘键值表
0xeb,0xdb,0xbb,0x7b,
0xe7,0xd7,0xb7,0x77};
//定义字符键值表
uchar CHAR_TABLE[]={0x30,0x31,0x32,0x33,//这四个会在液晶显示器中显示0 1 2 3
0x34,0x35,0x36,0x37,//这四个会显示4 5 6 7
0x38,0x39,0x2b,0x3d,//这四个会显示8 9 + =
0x2b,0x2d,0x2a,0x2f,//这四个会显示+ - * /
0x45,0x52,0x4f}; //这三个会显示E R O
uchar QUEUE[2*MAXLEN]={' ',' ',' ',' ',' ',' '}; //定义一个队列
int count = 0;
unsigned int state=0;
long a=0,b=0,c=0;
void mscanf(uchar *var); //从矩阵键盘中获取值
void print(uchar *outStr,uint end); //打印字符串
void delay5MS(); //短延时函数
void delay100MS(); //较长延时函数
void writeCMD(uchar com); //写命令子程序
void showOneChar(uchar dat); //写数据子程序
void init(); //初始化子程序,初始化液晶显示屏
void clear(); //清除显示屏上的显示
void clearInit(); //清屏,重置变量
void splitResultNum(long result); // 分离数值
void number(); //state0: 数字输入
void middle_add_sub(); //state:1:
void add_sub(); //state:2:
void middle_add_sub_times(); //state:3;
void middle_add_sub_div(); //state:4;
void add_sub_times(); //state:5;
void add_sub_div(); //state:6;
void mulMidState(); //state7: *中间态
void divMidState(); //state8: /中间态
void mulState(); //state9: *计算态
void divState(); //state10: /计算态
void showError(); //state12:错误状态
/**************** 主函数 ******************/
void main()
{
// state0:数字输入态
// state1:+,-中间态
// state2:+,-计算态
// state3:+/-,*中间态
// state4:+/-,/中间态
// state5:+/-,*计算态
// state6:+/-,/计算态
// state7:*中间态
// state8:/中间态
// state9:*计算态
// state10:/计算态
// state11:清屏重置
// state12:错误状态
state = 0;
init();
while(1){
switch(state){
case 0: // state0:数字输入态
number();
break;
case 1: // state1:+,-中间态
middle_add_sub();
break;
case 2: // state2:+,-计算态
add_sub();
break;
case 3: // state3:+,-,*中间态
middle_add_sub_times();
break;
case 4: // state4:+,-,/中间态
middle_add_sub_div();
break;
case 5: // state5:+,-,*计算态
add_sub_times();
break;
case 6: // state6:+,-,/计算态
add_sub_div();
break;
case 7: // state7:*中间态
mulMidState();
break;
case 8: // state8:/中间态
divMidState();
break;
case 9: // state9:*计算态
mulState();
break;
case 10: // state10:/计算态
divState();
break;
case 11: // state11:清屏重置
clearInit();
break;
case 12: // state12:错误状态
showError();
break;
}
// 错误状态不输出
if(state != 12)
{
clear();
print(QUEUE,count);
}
}
}
/**************** 分离数值 ******************/
void splitResultNum(long result)
{
long tmp;
if(result > 2000000000) // 如果大于20亿则溢出报错
{
state = 12;
return ;
}
tmp = result>0 ? result : -result;
count = 0;
while(tmp>0)
{
QUEUE[count++] = tmp%10;
tmp /= 10;
}
if(result < 0)
{
QUEUE[count++] = '-';
}
else if(result == 0)
{
QUEUE[count++] = 0;
}
for(tmp = count-1; tmp >= count/2; --tmp)
{
result = QUEUE[tmp];
QUEUE[tmp] = QUEUE[count-1-tmp];
QUEUE[count-1-tmp] = result;
}
}
/**************** state0的数字输入 ******************/
void number(){
uchar num=0xff;
mscanf(&num);//获取字符
QUEUE[count++]=num;
if(num==1||num==2||num==3||num==4||num==5||num==6||num==7||num==8||num==9||num==0){
a = a*10+num;
}else if (num=='+'){ //调整状态 +
b = 0;
flag = 0;
state = 1;
}else if (num=='-'){ //调整状态 -
b = 0;
flag = 1;
state = 1;
}else if (num=='*'){ //调整状态 *中间态
c = 0;
state = 7;
}else if (num=='/'){ //调整状态 /中间态
c = 0;
state = 8;
}else if (num=='='){ //输出
splitResultNum(a); //拆分
}else if (num=='C'){
state = 11;
}else{
//输入错误字符,启动报错程序
}
}
/**************** state1:+,-中间态 ******************/
void middle_add_sub(){
uchar num=0xff;
mscanf(&num);
QUEUE[count++]=num;
if(num==0||num==1||num==2||num==3||num==4||num==5||num==6||num==7||num==8||num==9){
b=num;
state=2;
}else if (num=='C'){ // K8 清屏
state = 11;
}else{
// 错误输入不存储
count--;
}
}
/**************** state2:+,-计算态 ******************/
void add_sub(){
uchar num=0xff;
mscanf(&num);
QUEUE[count++]=num;
if(num==0||num==1||num==2||num==3||num==4||num==5||num==6||num==7||num==8||num==9){
b=num+b*10;
}else if(num=='*'){
c=0;
state=3;
}else if (num=='/'){
c=0;
state=4;
}else if(num=='+'){ // done
if(flag == 1){
a=a-b;
}else{
a=a+b;
}
b = 0;
state = 1;
flag= 0;
}else if(num=='-'){ // done
if(flag == 1){
a=a-b;
}else{
a=a+b;
}
b = 0;
state = 1;
flag = 1;
}else if(num=='='){
state = 0; //按=号之后回到状态0
if(flag == 1){ // 1 : -
splitResultNum(a-b);
a = a-b;
}else{ // 0 : +
splitResultNum(a+b);
a = a+b;
}
b = c = 0;
}else if (num=='C'){
state = 11;
}
}
/**************** state3:+,-,*中间态 ******************/
void middle_add_sub_times(){
uchar num=0xff;
mscanf(&num);
QUEUE[count++]=num;
if(num==0||num==1||num==2||num==3||num==4||num==5||num==6||num==7||num==8||num==9){
c=num;
state=5;
return;
}else if (num=='C'){
state = 11;
}else{ //其他不符合规则的情况,不存储
count--;
}
}
/**************** state4:+,-,/中间态 ******************/
void middle_add_sub_div(){
uchar num=0xff;
mscanf(&num);
QUEUE[count++]=num;
if(num==0||num==1||num==2||num==3||num==4||num==5||num==6||num==7||num==8||num==9){
c = num;
if(c == 0){ //如果除数为0,则不存储
count--;
} else {
state = 6; //跳转到state6: +,-,/计算态
}
}else if (num=='C'){
state = 11;
}else{ //其他不符合规则的情况,不存储
count--;
}
}
/**************** state5:+,-,*计算态 ******************/
void add_sub_times(){
uchar num=0xff;
mscanf(&num);
QUEUE[count++]=num;
if(num==0||num==1||num==2||num==3||num==4||num==5||num==6||num==7||num==8||num==9){
c=num+c*10;
}else if(num=='+'){
if(flag==1){
a=a-b*c;
b=0;
c=0;
}else{
a=a+b*c;
b=0;
c=0;
}
flag=0;
state=1;
}else if(num=='-'){
if(flag==1){
a=a-b*c;
b=0;
c=0;
}else{
a=a+b*c;
b=0;
c=0;
}
flag=1;
state=1;
}else if(num=='*'){
b=b*c;
c=0;
state=3;
}else if(num=='/'){
b=b*c;
c=0;
state=4;
}else if(num=='='){
state=0;
if(flag == 1){
//输出 a-b*c
splitResultNum(a-b*c);
a = a-b*c;
}else{
//计算 a+b*c
splitResultNum(a+b*c);
a = a+b*c;
}
b = c = 0;
}else if (num=='C'){
state = 11;
}
}
/**************** state6:+,-,/计算态 ******************/
void add_sub_div(){
uchar num=0xff;
mscanf(&num);
if(num==0||num==1||num==2||num==3||num==4||num==5||num==6||num==7||num==8||num==9){
c=num+c*10;
}else if(num=='+'){
if(flag == 1){
a=a-b/c;
b=0;
c=0;
}else{
a=a+b/c;
b=0;
c=0;
}
flag=0;
state=1;
}else if(num=='-'){
if(flag == 1){
a=a-b/c;
b=0;
c=0;
}else{
a=a+b/c;
b=0;
c=0;
}
flag=1;
state=1;
}else if(num=='*'){
b=b/c;
c=0;
state=3;
}else if(num=='/'){
b=b/c;
c=0;
state=4;
}else if(num=='='){
state=0;
if(flag == 1){
//计算 a-b/c
splitResultNum(a-b/c);
a = a-b/c;
}else{
//计算 a+b/c
splitResultNum(a+b/c);
a = a+b/c;
}
b = c = 0;
}else if (num=='C'){
state = 11;
}
}
/**************** state7:*中间态 ******************/
void mulMidState()
{
uchar num=0xff;
mscanf(&num); //获取字符
QUEUE[count++]=num;
if(num==1||num==2||num==3||num==4||num==5||num==6||num==7||num==8||num==9||num==0){
c = num;
state = 9; //跳转到state9: *计算态
}else if (num=='C'){
state = 11;
}else{ //其他不符合规则的情况,不存储
//state = 12;
count--;
}
}
/**************** state8:/中间态 ******************/
void divMidState()
{
uchar num=0xff;
mscanf(&num); //获取字符
QUEUE[count++]=num;
if(num==1||num==2||num==3||num==4||num==5||num==6||num==7||num==8||num==9||num==0){
c = num;
if(c == 0){ //如果除数为0,不存储
//state = 12;
count--;
} else {
state = 10; //跳转到state10: /计算态
}
}else if (num=='C'){
state = 11;
}else{ //其他不符合规则的情况,不存储
//state = 12;
count--;
}
}
/**************** state9:*计算态 ******************/
void mulState()
{
uchar num=0xff;
mscanf(&num); //获取字符
QUEUE[count++]=num;
if(num==1||num==2||num==3||num==4||num==5||num==6||num==7||num==8||num==9||num==0){
c = c*10+num;
}else if(num=='*'){
a = a*c;
c = 0;
state = 7; // 跳转到state7: *中间态
}else if(num=='/'){
a = a*c;
c = 0;
state = 8; // 跳转到state8: /中间态
}else if(num=='+'){
a = a*c;
c = 0;
flag = 0;
state = 1; // 跳转到state1: +,-中间态
}else if(num=='-'){
a = a*c;
c = 0;
flag = 1;
state = 1; // 跳转到state1: +,-中间态
}else if(num=='='){
// 输出 a*c
state = 0; //按=号之后回到状态0
splitResultNum(a*c); //拆分
a = a*c;
b = c = 0;
}else if (num=='C'){
state = 11;
}else{
//输入错误字符,启动报错程序
}
}
/**************** state10:/计算态 ******************/
void divState()
{
uchar num=0xff;
mscanf(&num); //获取字符
QUEUE[count++]=num;
if(num==1||num==2||num==3||num==4||num==5||num==6||num==7||num==8||num==9||num==0){
c = c*10+num;
}else if(num=='/'){
a = a/c;
c = 0;
state = 8; // 跳转到state8: /中间态
}else if(num=='*'){
a = a/c;
c = 0;
state = 7; // 跳转到state7: *中间态
return ;
}else if(num=='+'){
a = a/c;
c = 0;
flag = 0;
state = 1; // 跳转到state1: +,-中间态
}else if(num=='-'){
a = a/c;
c = 0;
flag = 1;
state = 1; // 跳转到state1: +,-中间态
}else if(num=='='){
// 输出 a/c
state = 0; //按=号之后回到状态0
splitResultNum(a/c); //拆分
a = a/c;
b = c = 0;
}else if (num=='C'){
state = 11;
}else{
//输入错误字符,启动报错程序
}
}
/********************* state11:清屏重置子程序 **********************/
void clearInit()
{
a = b = c = 0;
count = 0;
state = 0;
EN=0;
writeCMD(0x01);
}
/********************* state12:错误状态 **********************/
void showError()
{
uchar num=0xff;
a = b = c = 0;
count = 5;
state = 0;
QUEUE[0] = 'E';
QUEUE[1] = 'R';
QUEUE[2] = 'R';
QUEUE[3] = 'O';
QUEUE[4] = 'R';
clear();
print(QUEUE,count);
while(1)
{
mscanf(&num); // 获取字符
if(num == 'C') // 只有按清屏 K8 才会退出 ERROR状态
{
state = 11;
return ;
}
else
{
clear();
print(QUEUE,count);
}
}
}
/**********从键盘获取值得函数类似于C语言的scanf()函数**************/
void mscanf(uchar *var)
{
uchar temp,num;
int i=1;
temp=i;
while(1){
P3=0xff;
delay100MS(); //延时,软件消除抖动
if(K1==0){
*var = '+';
break;
}else if (K2==0){
*var = '-';
break;
}else if (K3==0){
*var = '*';
break;
}else if (K4==0){
*var = '/';
break;
}else if (K8==0){
*var = 'C';
break;
}
P1=0x0f;//置行为高电平,列为低电平。这样用于检测行值。
if(P1!=0x0f){
delay100MS(); //延时,软件消除抖动。
temp=P1; //保存行值
P1=0xf0; //置行为低电平,列为高电平,获取列
if(P1!=0xf0){
num=temp|P1; //获取了按键位置
//P2=1;
for(i=0;i<12;i++)
if(num==KEY_CODE[i]){
if(i==10)*var='+';//获取等号的值
else if(i==11)*var='=';//获取加号的值
else *var=i;//获取数值
}
break; //跳出循环,为了只获取一个值
}
}
}
}
/*********************短延时函数*************************/
void delay5MS()
{
int n=3000;
while(n--);
}
/*****************定义长点的延时程序**********************/
void delay100MS()
{
uint n=10000;
while(n--);
}
/*******************写命令子程序**************************/
void writeCMD(uchar com)
{
P0=com; //com为输入的命令码。通过P2送给LCD
RS=0; //RS=0 写命令
RW=0;
delay5MS();
EN=1; //LCD的使能端E置高电平
delay5MS();
EN=0; //LCD的使能端E置低电平
}
/*******************写数据子程序**************************/
void showOneChar(uchar dat)
{
P0=dat; //写入数据
RS=1; //RS=1写命令
RW=0;
EN=1;
delay5MS();
EN=0;
}
/*******************初始化函数**************************/
void init()
{
EN=0;
writeCMD(0x38);//设置显示模式
writeCMD(0x0e);//光标打开,不闪烁
writeCMD(0x06);//写入一个字符后指针地址+1,写一个字符时整屏不移动
writeCMD(0x01);//清屏显示,数据指针清0,所以显示清0
writeCMD(0x80);//设置字符显示的首地址
}
/******************显示函数***************************/
void print(uchar arr[],uint end)
{
uint t=0,j=0;
uint location;
if(end==0 || end>MAXLEN){
clear();
return;
}
else {
for(t=0;t//if(t>=MAXLEN){
//writeCMD(0x40+0x80); // 光标换行
//}
if(arr[t]=='=') location=11;
else if(arr[t]=='+') location=10;
else if(arr[t]=='+') location=12;
else if(arr[t]=='-') location=13;
else if(arr[t]=='*') location=14;
else if(arr[t]=='/') location=15;
else if(arr[t]=='E') location=16;
else if(arr[t]=='R') location=17;
else if(arr[t]=='O') location=18;
else{
for(j=0;j<10;j++)
if(arr[t]==j) location=j;
}
showOneChar(CHAR_TABLE[location]);
}
}
}
/*********************清屏子程序**********************/
void clear()
{
EN=0;
writeCMD(0x01);
}
以下是计算器四则运算的C语言程序,该程序可以包含( ) + - * / . 和 =
#include
#include
#include
#include
#include
#define maxn 300
int priv[maxn]; // 运算符优先级数组
double value[maxn]; // 表达式中用到的变量的值
/**** 计算a op b的值并返回 ****/
double calc(double a, double b, char op)
{
switch(op)
{
case '+':
return a+b;
break;
case '-':
return a-b;
break;
case '*':
return a*b;
break;
case '/':
return a/b;
break;
case '^':
return exp(log(a)*b);
}
return 0; // 默认状况
}
/*********** 运算符优先级赋值 *************/
void initPriv()
{
// 数值越小优先级越高;数值相同,优先级相同
priv['+'] = priv['-'] = 3;
priv['*'] = priv['/'] = 2;
priv['^'] = 1;
priv['('] = 10;
}
/*********** 字符变量映射数组 *************/
void initValue()
{
// 表达式中变量初始化代码
// e.g. : value['x'] = 10;
}
/*********** 计算器函数 ***************
char str[] : 需要计算的合法表达式
double val[] : 表达式中变量的值
*/
double calculate(char str[], double val[])
{
double num[maxn]; // 存储数据的堆栈
int numi = 0; // 数据堆栈指针
char oper[maxn]; // 存储运算符的堆栈
int operi = 0; // 运算符堆栈指针
double x, y;
int i;
char last = 0;
// 运算符优先级赋值
initPriv();
// 处理多项式字符串
for(i = 0; i < strlen(str); ++i)
{
if(isalpha(str[i])) // 如果是变量 'A'-'F' 或 'a'-'z' 则需要用 val 数组进行转换
{
num[numi++] = val[str[i]];
}
else if(isdigit(str[i])) // 如果是数字字符 '0'-'9' 则需要
{
num[numi++] = atof(str+i); // 将 str[i] 开头的数字字符串转换为浮点数类型压入栈
// 将指针指向该数值之后的字符
for(; i+1 < strlen(str) && isdigit(str[i+1]); ++i);
if(i+1 < strlen(str) && str[i+1]=='.')
{
for(++i; i+1 < strlen(str) && isdigit(str[i+1]); ++i);
}
}
else if(str[i]=='(')
{
oper[operi++] = str[i];
}
else if(str[i]==')')
{
// 计算 '(' 与 ')' 之间的表达式的数值
while(oper[operi-1] != '(')
{
y = num[numi-1]; --numi;
x = num[numi-1]; --numi;
char op = oper[operi-1]; --operi;
num[numi++] = calc(x,y,op);
}
--operi;
}
else if(str[i]=='-' && (last==0 || last=='('))
{
num[numi++] = 0.0;
oper[operi++] = '-';
}
else if(priv[str[i]]>0)
{
while(operi>0 && priv[str[i]]>=priv[oper[operi-1]])
{
y = num[numi-1]; --numi;
x = num[numi-1]; --numi;
char op = oper[operi-1]; --operi;
num[numi++] = calc(x,y,op);
}
oper[operi++] = str[i];
}
else
{
continue;
}
last = str[i];
}
// 计算剩余运算的多项式数值
while(operi>0)
{
y = num[numi-1]; --numi;
x = num[numi-1]; --numi;
char op = oper[operi-1]; --operi;
num[numi++] = calc(x,y,op);
}
return num[numi-1];
}
int main()
{
// 初始化value数组
initValue();
char str[maxn] = "1+(3+4*5-2)/2+1200";
printf("%lf", calculate(str,value));
return 0;
}