基于51单片机的计算器设计

2020-08-28 14:58发布

能做计算器的单片机

单片机的出现是计算机制造技术高速发展的产物,它是嵌入式控制系统的核心,如今,它已广泛的应用到我们生活的各个领域,电子、科技、通信、汽车、工业等。本次设计是设计一个简易计算器,能够进行多位的加减乘除运算。它主要由51单片机的数码管,键盘等模块组成。本计算器是将键盘输入信息经处理通过缓存,送入数码管显示,数码管采用动态扫描方式,计算功能通过软件实现,用C语言对单片机可编芯片进行编程,实现计算器的设计。 把期中做的作业放上来哈哈

硬件电路描述

要进行数据的计算就必须先进行数据的输入,也就必须确定按键输入的数值是什么,这就需要对键盘进行扫描,从而确定究竟是哪个键按下。 对于键盘的扫描,这里采用行列扫描的方法来完成对键盘的扫描。原理就是先确定按键在哪一行,接着再确定是哪一列,这样就可以知道是哪个按键被按下了。我是将P3口作为按键扫描口的,比如,先使行线输出全“0”,读列线,再使列线输出全为“0”,读行线。两次结果再相与,则得到一个值为键值。同理,每个按键都会有一个对应的十六进制值,把它们列出来进行一一对应就行了。如下图。


888.jpg

程序设计描述

1.程序总流程图


999.jpg


2.编程思路


在单片机接通电源后,单片机就会一直重复检测键盘上的按钮是否被按下。如果有键被按下,就会进入选择判断,当按下数字键,相应数字计入变量keynum中并在数码管上移位显示;当按下运算符键和特殊功能键,也将对应的10到15数字计入到keynum中并进行第二次判断,如果keynum是0~9,则将数据变量dat×10加上keynum;如果是10(加号对应值),进入加法程序(加法标识变量加1,其他运算符标识变量归零,把dat赋值给另一变量datA;当法标识变量大于1时,就是连加,需要将dat等于dat加上datA的值)。其他运算符也是差不多的程序。keynum等于14,就进入等于运算程序。这个程序中也就是四个if语句,如果运算符变量为1就运行相应代码。如加法运算符为1,则使dat加上datA的值赋给dat。最后将dat放入显示程序中显示,而无论何时按下keynum等于15时,所有状态清零。这就是我写的代码的主要思路。

源代码及注释

#include #define long unsigned long 
#define KEYPORT P3 
sbit beep=P1^4; 
bit dot; 
typedef unsigned char byte; 
long dat; //数据 long datA; //过度数据 
byte addflag; //加法标志位 
byte subflag; //减法标志位 
byte mulflag; //乘法标志位 
byte divflag; //除法标志位 
byte clrflag; //数据处理标志位 
byte scanok; 
int checkok; 
int keynum; //按键键值 
static byte dispbuf[6]; //数码管字段表 
sbit duan=P2^6; 
sbit wela=P2^7; 
unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d, 0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00}; //变量定义 
void delayms(int ms)//这个软件大约可以等待ms毫秒 
{ 
int i,k; 
for(k=0; k>=1; posi|=0x20; //循环右移 
} 
} 
/*--------------------------------------------------------------- 移位显示子程序 把显示数组中的最低3位赋给高3位,使最新输入的键盘值赋给数组的最低位 输入:键值 返回:无 ---------------------------------------------------------------*/ 
void digitin(byte val) { dispbuf [3]= dispbuf [2]; dispbuf [2]= dispbuf [1]; dispbuf [1]= dispbuf [0]; dispbuf [0]= val; } /*--------------------------------------------------------------- 扫描得到的键值和实际需要的键盘任务的转换函数 也叫键盘码散转程序 输入:键值,有0x00~0xff钟可能性,根据显示的键值可以修改此函数 返回:无 ---------------------------------------------------------------*/ 
void keybranch(byte k) 
{	
if(scanok) 
{
scanok=0; 
switch(k) 
{ case 0x00:break; 
case 0xee:keynum=1;break; 
case 0xde:keynum=2;break; 
case 0xbe:keynum=3;break; 
case 0xed:keynum=4;break; 
case 0xdd:keynum=5;break; 
case 0xbd:keynum=6;break; 
case 0xeb:keynum=7;break; 
case 0xdb:keynum=8;break; 
case 0xbb:keynum=9;break; 
case 0xd7:keynum=0;break; 
case 0x7e:keynum=10;break;//加法 
case 0x7d:keynum=11;break;//减法 
case 0x7b:keynum=12;break;//乘法 
case 0x77:keynum=13;break;//除法 
case 0xb7:keynum=14;break;//等于 
case 0xe7:keynum=15;break;//清零 
default:break; } checkok=1; } 
}
 /*--------------------------------------------------------------- 处理程序,键值为15时或者清除标志为1时,数据清零 ---------------------------------------------------------------*/ void datchuli(void) 
{if(keynum==15) 
{ 
dat=0; datA=0; HEX_TO_BCD(dat); } 
else if(clrflag) //清除标志为1,则执行以下。 
{ 
dat=0; 
clrflag=0; //为下次使用准备。 
HEX_TO_BCD(dat); 
} 
if(keynum<10) 
{ digitin(keynum); 
dat=dat*10+keynum; 
} 
} 
void add(void) {    

addflag++; //加法标志置1。。。 
subflag=mulflag=divflag=0; //将其它运算标志清零。。(一次只能作一种运算) clrflag=1; //清零标标置1,(当按下加号后,再按第二个加数时,这时应该显示第二加数。。所以要清掉第一个加数。) 
if(addflag>1) 
{ 
dat=datA+dat; 
datA=dat; 
}    
datA=dat; 
} 

void sub(void)	//减法 
{ 
subflag++; 
addflag=mulflag=divflag=0; 
clrflag=1; 
if(subflag>1) 
{ 
dat=datA-dat; 
datA=dat; 
} 
datA=dat; 
} 

void mul(void)	//chengfa 
{ 
mulflag++; 
addflag=subflag=divflag=0; 
clrflag=1; 
if(mulflag>1) 
{ dat=dat*datA; 
datA=dat; 
} 
datA=dat; 
} 

void div(void)	//chufa 
{ 
divflag++; 
addflag=subflag=mulflag=0; 
clrflag=1; 
if(divflag>1) 
{ 
dat=datA/dat; 
datA=dat; 
} 
datA=dat; 
} 

void equ(void) 
{ 
if(addflag) //如果些时做加法运算。。 
{ 
dat=dat+datA; //计算各存入dat(显示程序会将dat显示的。。) 
} 
if(subflag) 
{ dat=datA-dat; } 
if(mulflag) 
{ dat=datA*dat; } 
if(divflag) 
{ dat=datA/dat; } 
addflag=subflag=mulflag=divflag=0;//运算一次完成后将所有运标志清零。为下次运算作准备。。 
HEX_TO_BCD(dat);	
clrflag=1; 
} 

void calculate_handle(void)//计算函数。。 
{	
if(checkok)//如果检测键值完。则执行以下。 
{ 
checkok=0;//检测完标志清零.. 
switch (keynum)//如果是+,-,*,/,=则进入相应的函数。。 
{ 
case 10 : 
{
add();HEX_TO_BCD(dat);
} 
break; //如果是按了"+",则进入加法函数。 
case 11 : 
{sub();HEX_TO_BCD(dat);} 
break; //如果是按了"-",则进入减法函数。 
case 12 : {mul();HEX_TO_BCD(dat);} 
break; //如果是按了"*",则进入乘法函数。 
case 13 : {div();HEX_TO_BCD(dat);} break; //如果是按了"/",则进入除法函数。 
case 14 : equ(); break; //如果是按了"=",则进入等于函数。 
default : datchuli(); //如果不是,计算符(即为数字),则进入数据处理函数。 
} 
}
} 
/*--------------------------------------------------------------- 主函数:将4X4键盘的键值显示在数码管上 ---------------------------------------------------------------*/ 
void main(void) { 
byte k; 
while(1) { 
k=keysearch(); 
if(k!=0xff) 
{ 
delayms(10);//有键按下 
k=keysearch(); 
keybranch(k); do 
{ k=keysearch(); scandisp(); } 
while(k!=0xff); //等待键释放 
} 
calculate_handle(); 
scandisp(); 
} 
}

设计体会

一开始只是会矩阵键盘和LCD扫描显示,所以当时是想做一个计算器应该还是挺容易的,但直到真正开始做的时候,才发现并不简单。一开始,想的就是,把运算键前输入的数值存到一个变量,后面的数值存到另一个变量内,然后再运算。但问题就来了,如何让单片机知道两次输入的数值要存到不同的变量去?如何把这些变量分别显示到显示管上?然后就要引入更多的变量、设计更多的函数。做出来的第一个版本,能实现加减乘除了,但还有一些问题。按下运算符时,数码管就会直接清零,不像真正计算器那样,按下运算符数码管上数值先不变,等下一数值输入时才变。还有不能实现连续运算的问题,最后还是修改好了。总的来说,这次设计的过程是很有挑战的,尤其对于我这种不善于编程的人来讲,遇到的问题,虽说比较麻烦,但还是车到山前必有路