资源描述
<p>创新课程设计
39
2020年4月19日
文档仅供参考
十
进
制
加
减
乘
除
法
计
算
器
院 系:电子与电气工程学院
专 业:电子信息工程
班 级:099411
姓 名:曹斌
学 号:
指导老师:薛晓
一、设计要求
以52系列单片机为核心实现一个简易计算器,设计十进制加减乘除法计算器。要求能(不同时)显示3位输入和4位输出。
二、设计目的
要求计算器能实现加减乘除四种运算,具体如下:
1、加法:整数加法
2、减法:整数减法
3、乘法:多位整数乘法
4、除法:整数除法
5、有清除功能
6、在原有要求上扩展多位输入和输出,超出值显示乱码
三、设计方案论证
按照系统设计的功能的要求,初步确定设计系统由主控模块、监测模块、显示模块、键扫描接口电路共四个模块组成,主控芯片使用51系列STC89C52单片机,采用高性能的静态80C51设计,由先进工艺制造,并带有非易失性Flash程序存储器。它是一种高性能、低功耗的8位COMS微处理芯片,市场应用最多。
监测模块采用三极管和蜂鸣器组成电路。
键盘电路采用4*4矩阵键盘电路。
显示模块采用4枚共阴极数码管和74ls138、74LS47芯片构成等器件构成。
整个单片机的接口电路:
P0(3…0)用于数码管段选显示输出;
P2用于键扫描输入;
P0(6..4)用于数码管位选控制;
P0(7)用于监测模块;
单片机最小系统
单片机最小系统就是支持主芯片正常工作的最小电路部分,包括主控芯片、复位电路和晶振电路。
主控芯片选取STC89C52RC芯片,因其具有良好的性能及稳定性,价格便宜应用方便。
晶振选取11.0592MHz,晶振旁电容选取20pF。
采用按键复位电路,电阻分别选取100Ω和10K,电容选取10μF。
以下为单片机最小系统硬件电路图。
-11-17 12:37 上传
下载附件(38.67 KB)
单片机最小系统硬件电路
键盘接口电路
采用P2口对键盘进行控制,行列式键盘这里主要用反转扫描法进行检测。原理图如下:
-11-17 12:38 上传
下载附件(92.56 KB)
数码管显示电路
采用8位数码管对计算数据和结果的显示(实验时只用到了4位),这里选取共阴数码管,利用74LS138和74LS47对数码管进行驱动。
P0.6~P0.4用来作为位选端,控制哪几位数码管进行显示。 P0.3~P0.0控制数码管数字进行显示。
以下为数码显示电路的硬件电路图
-11-17 12:38 上传
下载附件(97.03 KB)
监视电路
监视电路就是在按键时,发出声音提醒,以确保输入数字有效。这里就采用5V蜂鸣器作为示音设备。用p0.7口输出信号。
以下为报警电路硬件电路图
-11-17 12:38 上传
下载附件(25.23 KB)
(1)键盘扫描函数
键盘电路采用4*4矩阵键盘电路。采用扫描法对键盘进行扫描,对P2口的扫描结果和各按键的地址,我们就能够得到是哪个键按下,从而完成键盘扫描的功能。
unsigned char key_scan(void)
{
unsigned char row,col=0,k=0xff;
KEYIO=0xf0;
if((KEYIO&0xf0)==0xf0)
return k;
delay(10);
if((KEYIO&0xf0)==0xf0)
return k;
for(row=0;row<4;row++)
{
KEYIO=~(1<<row);
k=KEYIO&0xf0;
if(k!=0xf0)
{
while(k&(1<<(col+4)))
col++;
k=row*4+col;
KEYIO=0xf0;
P0&=0x7f;
while((KEYIO&0xf0)!=0xf0);
break;
}
}
return k;
}
键盘调用程序:
调用键盘扫描程序,读取按键的值。实现各个键的加减乘除的功能,采用switch功能进行读取。
unsigned char key_vect(unsigned char keyValue)
{
unsigned char nKey;
switch(keyValue)
{
case 0:
nKey=7;
break;
case 1:
nKey=8;
break;
case 2:
nKey=9;
break;
case 3:
nKey=ADD;
break;
case 4:
nKey=4;
break;
case 5:
nKey=5;
break;
case 6:
nKey=6;
break;
case 7:
nKey=SUB;
break;
case 8:
nKey=1;
break;
case 9:
nKey=2;
break;
case 10:
nKey=3;
break;
case 11:
nKey=MUL;
break;
case 12:
nKey=0;
break;
case 13:
nKey=CLR;
break;
case 14:
nKey=EQU;
break;
case 15:
nKey=DIV;
break;
default :
nKey=ERROR;
}
return nKey;
}
(2)数码管显示函数
采用4位数码管对计算数据和结果的显示,这里选取共阴数码管,利用74LS47和74LS138芯片对数码管进行驱动。
P0.6~P3.4用来作为位选端,控制哪几位数码管进行显示。比如当P0.6~P0.4为0时,其它位全给1。此时就将扫描的数据送给指定数码管显示。
void Led_display(unsigned char wei,unsigned char duan)
{
duan&=0x0f;
wei&=0x07;
P0=((7-wei)<<4)|duan|0x80; 5="10,原来是1位的,如何最终变成两位的??--卢春林" 456="594”,由硬仿件真结果可得出结果。具体见图;" 1996="" void="" -11-17="" 12:37="" 17.27="" 109.35="" 100.48="" 130.05="" .="" case="" equ:="" 1:="" resvalue="fstValue/secValue;" 2:="" 3:="" 4:="" fstvalue="0;" formatloop="">0;FormatLoop--)
{
unsigned long tmp=1;
signed char tmp1=FormatLoop;
for(;(tmp1-1)>0;tmp1--)
tmp*=10; //高位乘10
fstValue+=keyValue[FormatLoop-1]*tmp; //高位乘10后相加
}
4、如果除法有余数时怎么处理-------任小丽
回答:这是硬件的一个缺点,因为数码管不能够显示小数点位,因此在处理计算小数点的过程时,舍弃掉了小数点位,只保留整数。
case EQU:
NumberFormat(1);
switch(MathWay)
{
case 1:
resValue=fstValue+secValue;
break;
case 2:
resValue=fstValue-secValue;
break;
case 3:
resValue=fstValue*secValue;
break;
case 4:
resValue=fstValue/secValue; //除法
break;
}
如果要处理余数的话,可在后面求余除法resValue=fstValue%secValue;
5、计算处理子程序的设计思路----------朱阿松
回答:void NumberFormat(unsigned char bSec)
{
if(bSec)
{
secValue=0;
FormatLoop=nValideLed;
for(;FormatLoop>0;FormatLoop--)
{
unsigned long tmp=1;
signed char tmp1=FormatLoop;
for(;(tmp1-1)>0;tmp1--)
tmp*=10;
secValue+=keyValue[FormatLoop-1]*tmp;
}
}
else
{
fstValue=0;
FormatLoop=nValideLed; //获取按键获取的次数
for(;FormatLoop>0;FormatLoop--)
{
unsigned long tmp=1;
signed char tmp1=FormatLoop;
for(;(tmp1-1)>0;tmp1--) //按键次数进行循环*10的次数
tmp*=10;
fstValue+=keyValue[FormatLoop-1]*tmp; //将获取的值进行组合相加
}
}
}
首先从按键获取第一个数值显示,然后当输入第二个数值时,将第一个数值存入一个数组缓存起来,存入keyValue[]里面,然后乘于10在加上获取的第二个数值,然后组合起来送入fstValue,最后得到最后的数值。最后再将获取的第一个输入的数值和第二个输入数值进行加、减、乘、除运算。 case EQU:
NumberFormat(1);
switch(MathWay)
{
case 1:
resValue=fstValue+secValue; //加法运算
break;
case 2:
resValue=fstValue-secValue; //减法运算
break;
case 3:
resValue=fstValue*secValue; //乘法运算
break;
case 4:
resValue=fstValue/secValue; //除法运算
break;
}
resFormat();
break;
然后主程序调用
void main(void)
{
sys_init();
for(;;)
{
Calculate();
DisplayLoop=nValideLed;
if(DisplayLoop==0)
{
Led_display(0,0);
}
else
{
for(;DisplayLoop>0;DisplayLoop--)
{
Led_display(DisplayLoop-1,keyValue[DisplayLoop-1]);
//将缓存的数值赋给数码管的段选
delay(5);
}
}
}
}
6、请问你们的设计计算器的优缺点是什么?--王坡
回答:优点是能够实现任意位的计算输入和输出。缺点是不能算小数。
7、如果我要计算的是100*100得到的结果怎么显示?------董艳波
回答:按照原先题目要求和设计发现,结果超值,显示错误。这如果要设计计算器的话太不方便。经改过之后,能够显示其100*100的值,。为10000.
8、如果输入的数字是负数怎么处理-------周丹阳
回答:按要求是没有负数的,但如果想要用到负数的话,能够用按键进行判断。能够在unsigned char key_vect(unsigned char keyValue)函数加个case语句,然后在void Calculate(void)函数里加个IF语句进行判断,如:IF(//所按的键){//加个符号};,但由于硬件限制,数码管不能显示符号,因此不能用到。
9、程序能不能实现两个数相加之后数值直接显示,然后自动保存,再乘或除运算之后显示结果?--------袁一方
回答:我们设计的程序不能够实现,程序只可进行一步运算,但能够把结果作为第一个输入的值继续进行运算,也就是说多步运算只能一步一步来。
10、是否能够实现两个整数相减而得到负数?-黄文淑
回答:不能够实现,本程序会显示乱码。
程序代码:
unsigned char key_scan(void);
unsigned char key_vect(unsigned char keyValue);
voidsys_init();
#include "common.h"
#include "sys_init.h"
voidsys_init()
{
P0|=0x80;
}
#include "reg52.h"
#define ADD 21
#define SUB 22
#define MUL 23
#define DIV 24
#define CLR 25
#define EQU 26
#define ERROR 27
void delay(unsigned char z);
#include "common.h"
void delay(unsigned char z)
{
unsigned char x,y;
for(x=50;x>0;x--)
for(y=z;y>0;y--);
}
voidLed_display(unsigned char wei,unsigned char duan);
#include "common.h"
#include "display.h"
voidLed_display(unsigned char wei,unsigned char duan)
{
duan&=0x0f;
wei&=0x07;
P0=((7-wei)<<4)|duan|0x80;
}
void Calculate(void);
voidNumberFormat(unsigned char bSec);
voidresFormat(void);
#include "common.h"
#include "key.h"
#define KEYIO P2
unsigned char key_scan(void)
{
unsigned char row,col=0,k=0xff;
KEYIO=0xf0;
if((KEYIO&0xf0)==0xf0)
return k;
delay(10);
if((KEYIO&0xf0)==0xf0)
return k;
for(row=0;row<4;row++)
{
KEYIO=~(1<<row);
k=KEYIO&0xf0;
if(k!=0xf0)
{
while(k&(1<<(col+4))) k="row*4+col;" keyio="0xf0;" return="" unsigned="" char="" case="" 0:="" nkey="ERROR;" 1:="" 2:="" 3:="" 4:="" 5:="" 6:="" 7:="" 8:="" 9:="" 10:="" 11:="" 12:="" 13:="" 14:="" 15:="" default="" :="" include="" signed="" nvalideled="0,nLoop,FormatLoop,ResLoop;" long="" mathway="0;" secvalue="0;" formatloop="">0;FormatLoop--)
{
unsigned long tmp=1;
signed char tmp1=FormatLoop;
for(;(tmp1-1)>0;tmp1--)
tmp*=10;
secValue+=keyValue[FormatLoop-1]*tmp;
}
}
else
{
fstValue=0;
FormatLoop=nValideLed;
for(;FormatLoop>0;FormatLoop--)
{
unsigned long tmp=1;
signed char tmp1=FormatLoop;
for(;(tmp1-1)>0;tmp1--)
tmp*=10;
fstValue+=keyValue[FormatLoop-1]*tmp;
}
}
}
voidresFormat(void)
{ signed char reschar=7;
for(;reschar>-1;reschar--)
{
unsigned long tmp=1;
signed char tmp1=reschar;
for(;tmp1>0;tmp1--)
tmp*=10;
keyValue[reschar]=(unsigned char)(resValue/tmp);
resValue-=keyValue[reschar]*tmp;
}
for(nValideLed=8;nValideLed>0;nValideLed--)
{
if(keyValue[nValideLed-1]!=0)
break;
}
}
void Calculate(void)
{
nkey=key_vect(key_scan());
if(nkey!=ERROR)
{
if(nkey<10) nloop="">0;nLoop--)
{
keyValue[nLoop]=keyValue[nLoop-1];
}
nValideLed++;
keyValue[0]=nkey;
}
else
{
switch(nkey)
{
case ADD:
NumberFormat(0);
nValideLed=0;
keyValue[0]=0;
MathWay=1;
break;
case SUB:
NumberFormat(0);
nValideLed=0;
keyValue[0]=0;
MathWay=2;
break;
case MUL:
NumberFormat(0);
nValideLed=0;
keyValue[0]=0;
MathWay=3;
break;
case DIV:
NumberFormat(0);
nValideLed=0;
keyValue[0]=0;
MathWay=4;
break;
case CLR:
nValideLed=0;
keyValue[0]=0;
MathWay=0;
break;
case EQU:
NumberFormat(1);
switch(MathWay)
{
case 1:
resValue=fstValue+secValue;
break;
case 2:
resValue=fstValue-secValue;
break;
case 3:
resValue=fstValue*secValue;
break;
case 4:
resValue=fstValue/secValue;
break;
}
resFormat();
break;
}
}
}
}
#include "reg52.h"
#include "common.h"
#include "display.h"
#include "sys_init.h"
#include "key.h"
#include "calculate.h"
extern signed char nValideLed;
extern unsigned char keyValue[];
signed char DisplayLoop;
void main(void)
{
sys_init();
for(;;)
{
Calculate();
DisplayLoop=nValideLed;
if(DisplayLoop==0)
{
Led_display(0,0);
}
else
{
for(;DisplayLoop>0;DisplayLoop--)
{
Led_display(DisplayLoop-1,keyValue[DisplayLoop-1]);
delay(5);
}
}
}
}</p><!--10)--><!--<(col+4)))--><!--<4)|duan|0x80;-->
展开阅读全文