赞
踩
本文是简单计算器的设计。使用的是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单片机的电路网上有很多,只需要正常提供时钟,搭建复位电路即可
单片机内部有用于构成振荡器的高增益反相放大器,引脚XTAL1和XTAL2分别是此放大器的输入端和输出端。采用内部方式时钟电路,即外接晶体以及电容C1,C2构成并联谐振电路,接在放大器的反馈回路中,内部振荡器产生自激振荡。
使用上电复位电路,即上电瞬间,RST端的电位与Vcc相同,随着电容逐步充电而充电电流减小,RST电位逐渐下降。上电复位所需的最短时间是振荡器建起时间加上两个机器周期,在这段时间内RST端口的电平需要维持高于施密特触发器的下阈值。
本次项目需要显示数据量少,故采用LED数码管进行显示,经济且实用。数码管显示有静态显示和动态显示两种方法。
为了减少端口的使用,故选择静态显示,并采用共阴极接法电路,并通过74LS240和8155相连且受单片机控制。
数码管显示电路中,SW3、SW4红色拨码开关打在“ON”位置,数码管代码端和公共端与 8155PA、 PB 口相连。如果 SW3、 SW4 红色拨码开关打在相反位置,即“ OFF”位置,数码管电路与 8155 断开,数码管代码端和公共端对外开放。
8155位可编程并行I/O接口芯片,具有两个8位和一个6位I/O口,以及256字节RAM和一个14位计数器。本次课设只用到了三个扩展I/O口,即将单片机的P0口与8155的输入相连,PA,PC输出给矩阵键盘电路,PB口控制数码管位选口。
键盘可分为两类:编码键盘和非编码键盘。编码键盘是较多按键和专用驱动芯片的组合,当按下某个按键时能够处理按键抖动、连击等问题,直接输出按键的编码,无需系统软件干预。当系统功能比较复杂,按键数量很多时,采用编码键盘可以简化软件设计。但在需要按键数不多时,为了降低成本和简化电路通常采用非编码键盘。非编码键盘的接口电路有设计者根据需要自行决定,按键信息通过接口软件来获取。
本项目需要的16个按键,用两个28即可,即由8155的PC口和PA口采用两个28矩阵式键盘。
首先定义需要用到的数码管对应显示的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; } } }
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; } }
首先判断键盘中有无按键按下。即由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; }
定义无符号整数数组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];
}
循环清零Led_store数组,并清零keynum,该程序在按下清零键时执行,将显示000000,即可重新输入。
void clear_input()
{
int i;
keynum=0;
for(i=0;i<6;i++)
Led_store[i]=0;
}
该子程序在按下四个功能运算键后执行,计算输入的第二个值,如加数、减数、乘数和除数。具体流程如下: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]; }
加法计算子程序在按下加法按键后执行,具体流程如下:执行第二个值输入计算子程序,计算求和结果并存入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(); }
减法计算子程序在按下加法按键后执行
具体流程如下:先定义一个标志位用于记录结果的正负号,然后执行第二个值输入计算子程序,进入计算判断中,如果被减数大于减数,则计算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(); }
乘法计算子程序在按下乘法按键后执行,具体流程如下:执行第二个值输入计算子程序,计算求乘积结果并存入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(); }
除法计算子程序在按下乘法按键后执行,具体流程如下:执行第二个值输入计算子程序,然后进行判断除数是否为零,如果除数为零则执行溢出报错子程序;如果不为零则继续执行。然后判断当按键输入不是清零时,进入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(); }
因为本项目硬件只利用了六位数码管,而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(); }
本文仅仅针对该项目作一个思路解析与程序设计,读者们可以从中借鉴一些,想着大家都是搞课设而来的。
本套方案设计还有很多不足之处,比如不能多次运算和有优先级的运算,这些在其他博主都会有讲解,需要的大家去看看,我这个设计比较简单需求不多。
谢谢大家,请不要转载,仅供学习参考,非常欢迎一起交流。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。