文章目录
(一)前言
本文是简单计算器的设计。使用的是AT89S52芯片,C语言。
目的设计一个简易计算器,以能够进行4位BCD码的加减乘除整数运算,且能够在溢出范围时做出错提醒。用8位数码管动态显示,实现人机交互功能。开机时显示“0”,数字左移显示。计算器功能通过软件实现,以C语言对89S52单片机进行编程以实现所需要的计算器的设计。
本文使用JY2E00编译环境,当然也可以用keil4等。C语言编写程序后通过JY-E2300C仿真器连接硬件,选择AT89S52单片机和8155,输入采用两个2*8的矩阵键盘。显示用6位8段共阴极LED动态显示
(二)设计目的
(1) 8位数码管显示,开机时显示“0”,数字左移显示;
(2) 4位BCD码加减乘除,整数运算;
(3) 运算超范围出错提示
(4)2*8矩阵键盘,其中10个数字键,4个加减乘除运算功能键,1个等于键,1个清零键
(三)硬件及其电路
主要硬件设备:AT89S52单片机、8155、数码管、矩阵键盘
其中AT89S52单片机的电路网上有很多,只需要正常提供时钟,搭建复位电路即可
(1)时钟电路
单片机内部有用于构成振荡器的高增益反相放大器,引脚XTAL1和XTAL2分别是此放大器的输入端和输出端。采用内部方式时钟电路,即外接晶体以及电容C1,C2构成并联谐振电路,接在放大器的反馈回路中,内部振荡器产生自激振荡。
(2)复位电路
使用上电复位电路,即上电瞬间,RST端的电位与Vcc相同,随着电容逐步充电而充电电流减小,RST电位逐渐下降。上电复位所需的最短时间是振荡器建起时间加上两个机器周期,在这段时间内RST端口的电平需要维持高于施密特触发器的下阈值。
(3)数码管显示电路
本次项目需要显示数据量少,故采用LED数码管进行显示,经济且实用。数码管显示有静态显示和动态显示两种方法。
为了减少端口的使用,故选择静态显示,并采用共阴极接法电路,并通过74LS240和8155相连且受单片机控制。
数码管显示电路中,SW3、SW4红色拨码开关打在“ON”位置,数码管代码端和公共端与 8155PA、 PB 口相连。如果 SW3、 SW4 红色拨码开关打在相反位置,即“ OFF”位置,数码管电路与 8155 断开,数码管代码端和公共端对外开放。
(4)8155扩展I/O口电路
8155位可编程并行I/O接口芯片,具有两个8位和一个6位I/O口,以及256字节RAM和一个14位计数器。本次课设只用到了三个扩展I/O口,即将单片机的P0口与8155的输入相连,PA,PC输出给矩阵键盘电路,PB口控制数码管位选口。
(5)矩阵键盘电路
键盘可分为两类:编码键盘和非编码键盘。编码键盘是较多按键和专用驱动芯片的组合,当按下某个按键时能够处理按键抖动、连击等问题,直接输出按键的编码,无需系统软件干预。当系统功能比较复杂,按键数量很多时,采用编码键盘可以简化软件设计。但在需要按键数不多时,为了降低成本和简化电路通常采用非编码键盘。非编码键盘的接口电路有设计者根据需要自行决定,按键信息通过接口软件来获取。
本项目需要的16个按键,用两个28即可,即由8155的PC口和PA口采用两个28矩阵式键盘。
(四)系统程序设计
(1)主程序思路
首先定义需要用到的数码管对应显示的16进制编码、键盘编码和各个需要用到的数组和参量。进入主程序,首先对com8155控制字进行写入,再执行清除命令以清除输入,这两步为系统初始化。然后进入while中,首先进行数码管显示和输入数值计算,然后判断是否输入超过四位,如超过则跳入报错的子程序中,数码管显示ERROR;如未溢出,则继续进行,执行键盘扫描程序。
进入switch判断,对应不同的keycode分别进行四则运算和清零操作。完成后回到while重复执行。
下面是主函数及一些子函数定义声明和变量定义。
#include<reg51.h>
#include<absacc.h>
#include<intrins.h>
#define com8155 XBYTE[0xff20]
#define pa8155 XBYTE[0xff21]
#define pb8155 XBYTE[0xff22]
#define pc8155 XBYTE[0xff23]
unsigned char Led_code[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x79,0xf7,0x00,0x40,0x76,0x38};
unsigned int Led_store[]={0,0,0,0,0,0};
unsigned int Key_code[]={7,4,8,5,9,6,10,11,1,0,2,15,3,14,12,13,0};
long int Inputnum[]={0,0,0,0,0};
long int Outputnum;
int a,i,temp1,temp2,temp3,temp4;
int keynum=0;
int keycode=16;
void Led_display(void);
void keyscan(void);
void if_keyin(void);
void clear_input(void);
void add_compute(void);
void sub_compute(void);
void mul_compute(void);
void dev_compute(void);
void if_error(void);
void delay(int xms);
void out_high_display(void);
void out_low_display(void);
void input_B(void);
void main()
{
com8155=0x43;
clear_input();
while(1)
{
Led_display(); //显示
Inputnum[0]=Led_store[2]+Led_store[3]*10+Led_store[4]*100+Led_store[5]*1000; //输入值计算
if(keynum>4) if_error();
keyscan(); //键盘输入扫描
switch(keycode)
{
case 11: clear_input(); break;
case 6: add_compute(); break;
case 7: sub_compute(); break;
case 14: mul_compute(); break;
case 15: dev_compute(); break;
default: break;
}
}
}
(2)LED动态显示子程序流程图
LED动态显示程序执行流程如下:设置参量并赋初值,进入for循环以实现6位数码管的动态位选,进入switch中进行对应数码管的段选显示,延时继续循环进行。
void Led_display()
{
int temp,j;
temp=0x01;
j=0;
for(i=0;i<6;i++)
{
temp=~temp;
pa8155=temp;
switch(Led_store[j])
{
case 0:pb8155=~Led_code[0];break;
case 1:pb8155=~Led_code[1];break;
case 2:pb8155=~Led_code[2];break;
case 3:pb8155=~Led_code[3];break;
case 4:pb8155=~Led_code[4];break;
case 5:pb8155=~Led_code[5];break;
case 6:pb8155=~Led_code[6];break;
case 7:pb8155=~Led_code[7];break;
case 8:pb8155=~Led_code[8];break;
case 9:pb8155=~Led_code[9];break;
case 10:pb8155=~Led_code[10];break;
case 11:pb8155=~Led_code[11];break;
case 12:pb8155=~Led_code[12];break;
case 13:pb8155=~Led_code[13];break;
case 14:pb8155=~Led_code[14];break;
case 15:pb8155=~Led_code[15];break;
default: break;
}
j++;
temp=~temp;
temp=_crol_(temp,1);
delay(2);
pb8155=0xff;
}
}
(3)键盘扫描子程序流程图
首先判断键盘中有无按键按下。即由PA口输出0x00,再读取PC口状态,若PC低四位不全为1,则有键按下,继续进行消抖程序,即当发现有键按下时,延时一段时间后再判断键盘的状态,若仍有键保持按下状态,则断定有键按下,否则认为是抖动。然后进行扫描求键号,即从PA口输出0xfe并左移,扫描PC口状态读入,进行键号求解并存入Led_store[0]中。然后再等待闭合键的释放,若释放后则认为完成一次按键输入捕获,此时将按键输入存入Led_store数组中,记录keynum++即代表取入一个BCD码。
void keyscan()
{
int line=0;
int temp;
while(a==1)
{
do
{ Led_display();
if_keyin();
delay(5);
}while(a);
delay(6);//12ms
if_keyin();
}
pa8155=0xfe;
ACC=pa8155;
do{
temp=pc8155;
temp=temp&0x0f;
if(temp==0x0e)
{keycode=line;
break;};
if(temp==0x0d)
{keycode=8+line;
break;};
pa8155=pa8155<<1;
line++;
}while(ACC^0);
Led_store[0]=Key_code[keycode];
while(a==0)
{if_keyin();
Led_display();
}
Led_store[5]=Led_store[4];
Led_store[4]=Led_store[3];
Led_store[3]=Led_store[2];
Led_store[2]=Led_store[0];
Led_store[1]=0;
Led_store[0]=Led_store[0];
keynum++;
}
void delay(int xms)
{
int x,y;
for(x=xms;x>0;x--)
for(y=110;y>0;y--);
}
void if_keyin()
{
int b;
a=1;
pa8155=0x00;
b=pc8155&0x0f;
if(b!=0x0f) a=0;
}
(4)溢出报错子程序
定义无符号整数数组temp存入“ERROR”的段选码,并循环存入Led_store数组中,该程序在程序溢出范围时执行,将显示ERROR在高5位数码管,最低位不显示。
void if_error()
{
unsigned int temp[] ={10,11,11,0,11,12};
int i;
keynum=0;
for(i=5;i>=0;i--)
Led_store[i]=temp[5-i];
}
(5)清零子程序
循环清零Led_store数组,并清零keynum,该程序在按下清零键时执行,将显示000000,即可重新输入。
void clear_input()
{
int i;
keynum=0;
for(i=0;i<6;i++)
Led_store[i]=0;
}
(6)运算数值输入计算子程序
该子程序在按下四个功能运算键后执行,计算输入的第二个值,如加数、减数、乘数和除数。具体流程如下:keynum清零,并将Inputnum[0]存入Inputnum[1]中,执行清零子程序清零Inputnum[0],进入while循环,输入按键捕获并运算Inputnum[0]的值,当有等于按键按下时,退出循环,并将Inputnum[1]存入Inputnum[2],Inputnum[0]存入Inputnum[1],此时第一个值在Inputnum[2]中,第二个值在Inputnum[1]中。如果keynum>4,即第二次输入超过四位,则执行溢出报错程序并退出循环。
void input_B()
{
keynum=0;
Inputnum[1]=Inputnum[0];
clear_input();
while(1)
{
if(keycode==13) break;
Inputnum[0]=Led_store[2]+Led_store[3]*10+Led_store[4]*100+Led_store[5]*1000;
if(keynum>4)
{if_error();
break;}
keyscan();
}
Inputnum[2]=Inputnum[1];
Inputnum[1]=Inputnum[0];
}
(7)加法计算子程序
加法计算子程序在按下加法按键后执行,具体流程如下:执行第二个值输入计算子程序,计算求和结果并存入Inputnum[3]中。然后判断当按键输入不是清零时,进入while循环,首先进行按键扫描捕获子程序,并清零keynum,顺序执行先显示高四位子程序,再执行显示低四位子程序,清零temp,最后执行输入清零子程序。这其中只要不按下清零按键,按其他任何键均重复显示运算结果,按下清零键则退出此次运算。
void add_compute()
{
input_B();
Inputnum[3]=Inputnum[2]+Inputnum[1];
while(keycode!=11)
{
keyscan();
keynum=0;
while(temp1<100 && keycode!=11)
{
Led_store[0]=12;
out_high_display();
temp1++;
}
while(temp1<150 && keycode!=11)
{
Led_store[0]=12;
out_low_display();
temp1++;
}
temp1=0;
}
clear_input();
}
(8)减法计算子程序
减法计算子程序在按下加法按键后执行
具体流程如下:先定义一个标志位用于记录结果的正负号,然后执行第二个值输入计算子程序,进入计算判断中,如果被减数大于减数,则计算Inputnum[2]-Inputnum[1]并存入Inputnum[3],记录标志位为12,即为LED不显示的段选码;如果被减数小于减数,则计算Inputnum[1]-Inputnum[2]并存入Inputnum[3],记录标志位为13,即为LED显示“-”的段选码。然后判断当按键输入不是清零时,进入while循环,首先进行按键扫描捕获子程序,并清零keynum,顺序执行先显示高四位子程序,再执行显示低四位子程序,清零temp,最后执行输入清零子程序。这其中只要不按下清零按键,按其他任何键均重复显示运算结果,按下清零键则退出此次运算。
void sub_compute()
{
int sign;
input_B();
if(Inputnum[2]>Inputnum[1])
{
Inputnum[3]=Inputnum[2]-Inputnum[1];
sign=12;
}
else
{
Inputnum[3]=Inputnum[1]-Inputnum[2];
sign=13;
}
while(keycode!=11)
{
keyscan();
keynum=0;
while(temp2<100 && keycode!=11)
{
Led_store[0]=sign;
out_high_display();
temp2++;
}
while(temp2<150 && keycode!=11)
{
Led_store[0]=sign;
out_low_display();
temp2++;
}
temp2=0;
}
clear_input();
}
(9)乘法计算子程序
乘法计算子程序在按下乘法按键后执行,具体流程如下:执行第二个值输入计算子程序,计算求乘积结果并存入Inputnum[3]中。然后判断当按键输入不是清零时,进入while循环,首先进行按键扫描捕获子程序,并清零keynum,顺序执行先显示高四位子程序,再执行显示低四位子程序,清零temp,最后执行输入清零子程序。这其中只要不按下清零按键,按其他任何键均重复显示运算结果,按下清零键则退出此次运算。
void mul_compute()
{
input_B();
Inputnum[3]=Inputnum[2]*Inputnum[1];
while(keycode!=11)
{
keyscan();
keynum=0;
while(temp3<100 && keycode!=11)
{
Led_store[0]=12;
out_high_display();
temp3++;
}
while(temp3<150 && keycode!=11)
{
Led_store[0]=12;
out_low_display();
temp3++;
}
temp3=0;
}
clear_input();
}
(10)除法计算子程序
除法计算子程序在按下乘法按键后执行,具体流程如下:执行第二个值输入计算子程序,然后进行判断除数是否为零,如果除数为零则执行溢出报错子程序;如果不为零则继续执行。然后判断当按键输入不是清零时,进入while循环,首先进行按键扫描捕获子程序,并清零keynum
然后计算Inputnum[3]=Inputnum[2]/Inputnum[1]*10000,并顺序执行先显示高四位子程序,即Inputnum[3]中存入的是商,且位于高四位;
再计算Inputnum[3]=Inputnum[2]%Inputnum[1],即Inputnum[3]中存入的是余数,再执行显示低四位子程序。然后清零temp,最后执行输入清零子程序。这其中只要不按下清零按键,按其他任何键均重复显示运算结果,按下清零键则退出此次运算。
void dev_compute() //高位为商,低位为余
{
input_B();
if(Inputnum[1]==0) if_error();
while(keycode!=11)
{
keyscan();
keynum=0;
while(temp4<100 && keycode!=11)
{
Led_store[0]=12;
Inputnum[3]=Inputnum[2]/Inputnum[1]*10000;
out_high_display();
temp4++;
}
while(temp4<200 && keycode!=11)
{
Led_store[0]=12;
Inputnum[3]=Inputnum[2]%Inputnum[1];
out_low_display();
temp4++;
}
temp4=0;
}
clear_input();
}
(11)显示子程序
因为本项目硬件只利用了六位数码管,而4位BCD码的运算结果有8位,故分开显示高四位和低四位。看看代码即可理解。当然硬件资源够的情况下直接显示全部结果最佳。
void out_high_display()
{
Outputnum=Inputnum[3]/10000;
Led_store[5]=Outputnum/1000;
Led_store[4]=(Outputnum%1000)/100;
Led_store[3]=(Outputnum%100)/10;
Led_store[2]=Outputnum%10;
Led_store[1]=14;
Led_display();
}
void out_low_display()
{
Outputnum=Inputnum[3]%10000;
Led_store[5]=Outputnum/1000;
Led_store[4]=(Outputnum%1000)/100;
Led_store[3]=(Outputnum%1000%100)/10;
Led_store[2]=Outputnum%1000%100%10;
Led_store[1]=15;
Led_display();
}
(五) 结束语
本文仅仅针对该项目作一个思路解析与程序设计,读者们可以从中借鉴一些,想着大家都是搞课设而来的。
本套方案设计还有很多不足之处,比如不能多次运算和有优先级的运算,这些在其他博主都会有讲解,需要的大家去看看,我这个设计比较简单需求不多。
谢谢大家,请不要转载,仅供学习参考,非常欢迎一起交流。