资源描述
51单片机计算器设计
精品文档
1引言
当今时代,是一个新技术层出不穷的时代。在电子领域,尤其是自动化智能控制领域,传统的分立元件或数字逻辑电路构成的控制系统正以前所未见的速度被单片机智能控制系统所取代。单片机具有体积小、功能强、成本低、应用面广等优点,可以说,智能控制与自动控制的核心就是单片机。目前,一个学习与应用单片机的高潮正在工厂、学校及企事业单位大规模地兴起。过去习惯于传统电子领域的工程师、技术员正面临着全新的挑战,如不能在较短时间内学会单片机,势必会被时代所遗弃,只有勇敢地面对现实,挑战自我,加强学习,争取在较短的时间内将单片机技术融会贯通,才能跟上时代的步伐。
它所给人带来的方便也是不可否定的,它在一块芯片内集成了计算机的各种功能部件,构成一种单片式的微型计算机。20世纪80年代以来,国际上单片机的发展迅速,其产品之多令人目不暇接,单片机应用不断深入,新技术层出不穷。20世纪末,电子技术获得了飞速的发展,在其推动下,现代电子产品几乎渗透了社会的各个领域,有力地推动了社会生产力的发展和社会信息化程度的提高,同时也使现代电子产品性能进一步提高,产品更新换代的节奏也越来越快。
本设计是由单片机实现的模拟计算器,它不仅能实现数据的加减乘除运算,而且还能使数据及其计算结果在数码管上显示出来,能够实现0-256的数字四则运算。本设计是用单片机AT89C51来控制,采用共阳极数码显示,软件部分是由C语言来编写的。设计任务
利用键盘和数码管设计一个简单的数学计算器,可以完成简单的如加,减,乘,除的四则运算,并将运算结果在数码管上显示出来。
2.方案论证与设计
根据功能和指标要求,本系统选用MCS 51 单片机为主控机。通过扩展必要的外围接口
电路,实现对计算器的设计。具体设计考虑如下:
①由于要设计的是简单的计算器,可以进行四则运算,对数字的大小范围要求不高,故
我们采用可以进行四位数字的运算,选用8 个LED 数码管显示数据和结果。
②另外键盘包括数字键(0~9)、符号键(+、-、×、÷)、清除键和等号键,故只需要16 个按键即可。系统模块图:
2.1 输入模块:
键盘扫描计算器输入数字和其他功能按键要用到很多按键,如果采用独立按键的方式,在这种情况下,编程会很简单,但是会占用大量的I/O 口资源,因此在很多情况下都不采用这种方式。为此,我们引入了矩阵键盘的应用,采用四条I/O 线作为行线,四条I/O 线作为列线组成键盘。在行线和列线的每个交叉点上设置一个按键。这样键盘上按键的个数就为4×4个。这种行列式键盘结构能有效地提高单片机系统中I/O 口的利用率。矩阵键盘的工作原理:计算器的键盘布局如图2 所示:一般有16 个键组成,在单片机中正好可以用一个P 口实现16 个按键功能,这种形式在单片机系统中也最常用。
由图 3 矩阵键盘内部电路图可以知道,当无按键闭合时,P10~P13 与P14~P17 之间开路。
当有键闭合时,与闭合键相连的两条I/O 口线之间短路。判断有无按键按下的方法是:第一
步,置列线P14~P17 为输入状态,从行线P10~P13 输出低电平,读入列线数据,若某一列
线为低电平,则该列线上有键闭合。第二步,行线轮流输出低电平,从列线P14~P17 读入
数据,若有某一列为低电平,则对应行线上有键按下。综合一二两步的结果,可确定按键编
号。但是键闭合一次只能进行一次键功能操作,因此须等到按键释放后,再进行键功能操作,
否则按一次键,有可能会连续多次进行同样的键操作。
2.2 运算模块:单片机控制
AT89C51 单片机是在一块芯片中集成了CPU、RAM、ROM、定时器/计数器和多功能I/O等一台计算机所需要的基本功能部件。如果按功能划分,它由如下功能部件组成,即微处理器(CPU)、数据存储器(RAM)、程序存储器(ROM/EPROM)、并行I/O 口、串行口、定时器/计数器、中断系统及特殊功能寄存器(SFR)。[3][5]单片机是靠程序运行的,并且可以修改。通过不同的程序实现不同的功能,尤其是特殊的独特的一些功能,通过使用单片机编写的程序可以实现高智能,高效率,以及高可靠性!因此我们采用单片机作为计算器的主要功能部件,可以进行很快地实现运算功能。
2.3 显示模块:
LED 显示发光二极管LED 是单片机应用系统中的一宗简单而常用的输出设备,其在系统中的主要作用是显示单片机的输出数据、状态等。因而作为典型的外围器件,LED 显示单元是反映系统输出和操作输入的有效器件。LED 具备数字接口可以方便的和大年纪系统连接;它的优点是价格低,寿命长,对电压电流的要求低及容易实现多路等,因而在单片机应用系统中获得了广泛的应用。[2][4]通常的数码显示器是由7 段条形的LED 组成(如图4 所示),点亮适当的字段,就可显示出不同的数字。我们采用8 段数码管,其中位于显示器右下角的LED 作小数点用。LED 显示器有两种不同的形式:共阴极和共阳极。本次设计采用共阴极接法(如图5所示)。
3、硬件原理
3.1 硬件连接
图3-1所示是简易计算器电路原理图。P3口用于键盘输入,接4X4键值与键盘对应表如图表所示,P0口和P2口用于显示,P2口用于显示数位值高位,P0用于显示数位值的低位。
键值 0 1 2 3 4 5 6 7 8 9 + - x / = ON/C
功能 0 1 2 3 4 5 6 7 8 9 + - x / = 清零
4、软件设计
在程序设计方法上,模块化程序设计是单片机应用中最常用的程序设计方法。设计的中心思想是把一个复杂应用程序按整体功能划分成若干相对独立的程序模块,各模块可以单独设计、编程和调试,然后组合起来。这种方法便于设计和调试,容易实现多个程序共存,但各个模块之间的连接有一定的难度。根据需要我们可以采用自上而下的程序设计方法,此方法先从主程序开始设计,然后再编制各从属程序和子程序,层层细化逐步求精,最终完成一个复杂程序的设计。这种方法比较符合人们的日常思维,缺点是一级的程序错误会对整个程序产生影响。功能流程图如下:
程序流程图
程序:
#include"at89x52.h" //头文件
//---------------------预定义模块---------------------------//
//*********************预定义数组***************************//
//字形码 0 1 2 3 4 5 6 7
const unsigned char KEY_NUMBER[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07
// 8 9 + - * / = CE
,0x7f,0x6f,0xF0,0xC0,0xF6,0xD2,0x89,0xB9};
//扫描键码 0 1 2 3 4 5 6 7
const unsigned char KEY_VALUE[]={0x7e,0xbe,0xde,0xee,0x7d,0xbd,0xdd,0xed
// 8 9 A B C D E F
,0x7b,0xbb,0xdb,0xeb,0x77,0xb7,0xd7,0xe7};
//数码管各显示位: 0位 1位 2位 3位 4位
const unsigned char DISPLAY_BIT[5]={0xFE,0xFD,0xFB,0xF7,0xEF};
//十进制转换权值
const int bcd_right[5]={1,10,100,1000,10000};
unsigned char buffer_number[5]; //输入数据缓冲区
unsigned char buffer_dis[5]; //计算结果显示缓冲区
unsigned long int buffer_opera[5]; //输入数据十进制转换缓冲区
//*******************预定义变量***************************//
unsigned char const NULL=0xFF;
unsigned char opera=0x00; //输入数据转换指针
unsigned char count=0x00; //键盘输入指针
unsigned char enter_number=0x01; //输入数据显示和计算结果显示开关
unsigned char ptr_i=0x00; //运算结果转换指针
unsigned char buffer_control; //控制键缓冲区
unsigned char key_bcd; //按键的十进制值
unsigned char scan_key; //当前扫描到的键值
unsigned char temp_c; //临时变量
unsigned char ptr_c; //十进制转化指针
unsigned char account; //四则运算标志
unsigned long int result=0x00; //当前计算结果
unsigned long int rlt; //前一个输入的数的十进制值
unsigned long int sec_rlt; //后一个输入的数的十进制值
unsigned long int c_n; //当前转换的运算结果
/*******************************************************
名称: 初始化运行环境
功能: 初始化各寄存器与端口状态
参数: 无
返回值:无
*******************************************************/
void DeviceInit(void)
{
TL0=0xBF; //置定时器中断时间为20ms
TH0=0x63;
TMOD=0x01; //设定定时器/计数器0工作方式为1
IE=0x82; //允许全局中断,允许定时器/计数器0溢出中断
TR0=0x01; //启动定时器/计数器0
}
/*******************************************************
名称: 延时模块
功能: 延时
参数: unsigned char i
返回值:无
*******************************************************/
void Delay(unsigned char i)
{
int j;
while((i--)!=0)
{ for(j=0;j<=0x6f;j++);}
}
/*******************************************************
名称: 清除模块
功能: 清除当前已经输入的数据
通过输入缓冲区中的数乘以它们各自对应的权值再求和
参数: 参数为1时,清除输入数据缓冲区
参数为2时,清除十进制转化缓冲区
返回值:无
*******************************************************/
void Clear(unsigned char clr_pid)
{
temp_c=0x00;
for(temp_c;temp_c<=4;temp_c++)
{
if(clr_pid==1) //参数为1时,输入数据缓冲区全部置0
{
count=0x00; //按键次数置0
buffer_number[temp_c]=0x00;
}
if(clr_pid==2) //参数为2时,输入数据缓冲区全部置0
{
opera=0x00; //输入数的个数置0
buffer_opera[temp_c]=0x00;
}
}
}
/*******************************************************
名称: 数据处理模块
功能: 将输入的数据转化为10进制数
通过输入缓冲区中的数乘以它们各自对应的权值再求和
参数: 无
返回值:无
*******************************************************/
void BcdConvert(void)
{
temp_c=0x00;
ptr_c=opera-1;
if(opera !=0)
{
while(temp_c<=ptr_c) //当前位乘以对应的权值再加上前一次的计算值
{ //将输入数据缓冲区中的数转化为对应的十进制数
buffer_opera[ptr_c-temp_c]*=bcd_right[temp_c];
result+=buffer_opera[ptr_c-temp_c];
temp_c++;
}
Clear(2); //转化完成,将十进制转换缓冲区清0
}
}
/*******************************************************
名称: 计算结果处理模块
功能: 按位分解计算结果,留待显示
将要转化的数除以10后取余数,就可以将其按位分解
转化的结果是反向的
参数: 无
返回值:无
*******************************************************/
void Convert(void)
{
c_n=result;
//将计算结果反向分解,用于显示
do
{ //除10取余
buffer_dis[ptr_i]=c_n%10;
c_n/=10;
ptr_i++;
}
while(c_n!=0x00);
}
/*******************************************************
名称: 四则运算模块
功能: 对先后输入的两个数按规则进行四则运算
参数: 无
返回值:无
*******************************************************/
void Calculate(void)
{
BcdConvert(); //将要运算的数转化成10进制数
enter_number=0x00; //设置运算结果显示标志
switch(account)
{
case 0x00: //加法,将前后输入的两个数相加
result+=rlt;
break;
case 0x01: //减法
if(rlt>=result)
result=rlt-result;
else //如果被减数比减数小
{
buffer_control=0xC0;
result=~(rlt-result-0x01);
}
break;
case 0x02: //乘法
result*=rlt;
break;
case 0x03: //除法
result=rlt/result;
break;
}
Convert(); //调用计算结果处理函数
Clear(2); //清除计算结果
}
/*******************************************************
名称: 控制键处理模块
功能: 对各个控制键进行对应的操作
参数: 无
返回值:无
*******************************************************/
void Control(void)
{
Clear(1); //清除显示缓冲区
//*********************等号处理*********************//
if(buffer_control==0x89)
Calculate(); //调用计算函数
//*********************清除键处理*******************//
else if(buffer_control==0xB9)
{
Clear(1); //清除已输入的数
Clear(2); //清除当前也转化的值
ptr_i=0x00; //清除显示指针
result=rlt=0x00; //清除元算结果
}
//********************运算符号处理******************//
else
{
BcdConvert(); //将先前输入的数转化为10进制数
rlt=result; //保存转化的结果
result=0x00; //清除上一次转化的结果,准备下一次转化
switch(buffer_control)
{
case 0xF0: //加法
account=0x00;
break;
case 0xC0: //减法
account=0x01;
break;
case 0xF6: //乘法
account=0x02; //乘法运算标志
break;
case 0xD2: //除法
account=0x03; //除法运算标志
break;
default:
break;
}
}
}
/*******************************************************
名称: 按键判断模块
功能: 判断是否有键被按下
参数: 无
返回值:有键按下则返回1
没有键按下则返回0
*******************************************************/
unsigned char KeyDown(void)
{
temp_c=0x00;
P2=0xf0;
temp_c=P2;
if(temp_c!=0xf0)
return 1; //有键按下返回1
else
return 0; //没有键按下返回0
}
/*******************************************************
名称: 按键处理函模块
功能: 获取当前按键
参数: 无
返回值:返回当前按键的字形码
如果没有键按下,返回NULL
*******************************************************/
unsigned char KeyPress (void)
{
if(KeyDown()==1) //判断是否有键按下
{
Delay(30); //延时消抖
//**************扫描当前被按下的键值***********************//
P2=0xF0; //扫描低4位
scan_key=P2; //记录扫描结果
P2=0x0F; //扫描高四位
temp_c=P2; //记录扫描结果
scan_key^=temp_c; //两结果相异或得键值
//**************当有键被按下时关闭显示*********************//
do{
P1=0xFF; //如果有键按下,关闭显示
}while(KeyDown()==1); //等待按键释放,如果有键按下,则不显示任何数
Delay(30); //延时消抖
//**********当有键按下后开始扫描所按下的键***************//
for(key_bcd=0;key_bcd<=15;key_bcd++) //查找键值对应的字符码
{
if(KEY_VALUE[key_bcd]==scan_key) //如果找到对应的键值
{
ptr_i=0x00; //清除运算结果显示指针,开始显示输入的数据
//*****************判断是数字键还是控制键****************//
if(key_bcd<=0x09) //小于10的键为数字键
{
enter_number=0x01; //设置输入数据显示标志
if(count<=0x04) //如果缓冲区未满
{
buffer_number[count]=KEY_NUMBER[key_bcd]; //当前键送缓冲区
buffer_opera[opera]=key_bcd; //保存当前按键的十进制值
opera++; //指针自加
count++;
}
else //如果缓冲区溢出
{
count=0x00;
Clear(1); //清楚缓冲区
buffer_number[0]=KEY_NUMBER[key_bcd]; //缓冲区初始位存入当前键的字符码
buffer_opera[opera]=key_bcd; //保存当前按键的十进制值
opera++;
count++;
}
return KEY_NUMBER[key_bcd]; //返回对应的数字字符值
}
else //大于9的键为控制键
{
buffer_control=KEY_NUMBER[key_bcd]; //控制键送控制缓冲区
Control(); //调用控制键处理函数
}
}
}
}
else
return NULL; //如果没有键按下,返回NULL
}
/*******************************************************
名称: 数码管显示模块
功能: 在数码管上显示输入的数据和计算后的结果
参数: 无
返回值:无
*******************************************************/
void Display(void)
{
unsigned char m=0; //显示字符指针
unsigned char n=0; //显示位指针;
while(KeyPress()==NULL) //当有键被按下时开始显示
{
//****************显示当前输入的数据*******************//
if(enter_number==0x01) //显示输入中的数据
{
if(m!=count) //字符指针在界线内
{
if(n<=count-1) //显示位未越界
{
P1=DISPLAY_BIT[n]; //设置显示位
P0=buffer_number[m]; //在数码管上显示字符
Delay(0x02); //延时消抖
n++;
m++;
}
else //显示位越界
n=0;
}
else //字符指针越界
m=0;
}
//****************显示运算后的结果*********************//
else //显示运算的结果
{
if(m!=ptr_i) //字符指针在界线内
{
if(n<=ptr_i-1) //显示位未越界
{
P1=DISPLAY_BIT[ptr_i-n-1]; //设置显示位
P0=KEY_NUMBER[buffer_dis[m]]; //在数码管上显示字符
Delay(0x02); //延时消抖
n++;
m++;
}
else //显示位越界
n=0;
}
else //字符指针越界
m=0;
Clear(1);
}
//*******************显示运算操作符***********************//
P1=0xDF; //打开运算符号显示位
P0=buffer_control; //显示当前运算符号
Delay(0x02);
}
}
/*******************************************************
名称: 主函数
功能: 应用程序入口
参数: 无
返回值:无
*******************************************************/
void main()
{
DeviceInit(); //调用系统初始化函数
while(1)
;
}
/*******************************************************
名称: 定时器中断模块
功能: 调用键盘监视模块获取键盘输入
调用显示模块来显示输入与输出
参数: 无
返回值:无
*******************************************************/
void timer0_over(void) interrupt 1
{
TL0=0xBF; //中断间隔为20ms
TH0=0x63;
KeyPress(); //调用键盘扫描程序
Display(); //调用显示函数
}
5. 总结
通过该计算器的设计我深入学习数码管扫描和键盘控制,提高对了51系列单片机的实际应用能力。同时也掌握应用程序控制51系列单片机进行简单的数学运算。提高了对51系列单片机的编程能力。这门课是最能理论联系实际的课,我们的目的是做出东西,为了这我们需要学关于这方面的各种知识,从被动性的接受知识变成了主动性的寻找知识。这种模式才是学习的最好方式。在此制作过程中我们经历挫折,经历困惑,但有目标在前,我们不能也不想放弃,只有在克服困难后才会有成功的希望。
收集于网络,如有侵权请联系管理员删除
展开阅读全文