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

更新时间:2023-04-05 20:15:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

摘要

近几年单片机技术的发展很快,其中,电子产品的更新速度迅猛。计算器是日常生活中比较常见的电子产品之一。如何才能使计算器技术更加的成熟,充分利用已有的硬件和软件条件,设计出更出色的计算器。

本设计是以STC89C52单片机为核心的计算器模拟系统设计,输入采用4*4矩阵键盘,可以进行加、减、乘、除带符号数字运算(六位整数、两位小数),并在液晶显示屏LCD1602上静态显示操作过程及结果。对字符液晶显示模块的工作原理,如初始化、清屏、显示、调用及外特性有较清楚的认识,并会使用LCD (液晶显示模块)实现计算结果的显示;掌握液晶显示模块的驱动和编程,设计LCD和单片机的接口电路,以及利用单片机对液晶显示模块的驱动和操作;在充分分析内部逻辑的概念,进行软件和调试,学会使用,并能够以其为平台设计出具有四则运算能力简易计算器的硬件电路和软件程序。

关键词:单片机;液晶显示屏;计算器;矩阵键盘

错误!未找到目录项。

引言

现如今,人们的日常生活中已经离不开计算器了,社会的各个角落都有它的身影,比如商店、办公室、学校……因此设计一款简单实用的计算器会有很大的实际意义。本设计旨在进一步掌握单片机理论知识,理解单片机系统的硬软件设计,加强对实际应用系统设计的能力。通过本设计的学习,使我掌握单片机程序设计的基本方法,并能综合运用本科阶段所学软、硬件知识分析实际问题,提高解决毕业设计实际问题的能力,为单片机应用和开发打下良好的基础。

一、项目总体设计框架

1、芯片STC89C52

单片机是单片微型机的简称,故又称为微控制器MCU(Micro Control Unit)。通常由单块集成电路芯片组成,内部包含有计算机的基本功能部件:中央处理器CPU,存储器和I/O接口电路等。因此,单片机只要和适当的软件及外部设备相结合,便可成为一个单片机控制系统。单片机广泛应用于智能产品,智能仪表,测控技术,智能接口等,具有操作简单、实用方便、价格便宜等优点。

而其中STC89C52是一种带8K字节闪烁可编程可擦除只读存储器(FPEROM-Flash Programable and Erasable Read Only Memory)的低电压,高性能COMOS8的微处理器。该器件与工业标准的MCS-51指令集和输出管脚相兼容;且是单片机中最典型的代表,应用于各种控制领域。

2、输入模块采用矩阵式键盘作为输入模块

矩阵式按键输入模块,其特点是:电路和软件稍复杂,但相比之下,当键数越多时,越节约I/O口,比较节省资源。其原理图如图所示。

图矩阵键盘输入

3、显示模块

采用LED数码管的静态显示,其特点是:其亮度较高;这种显示方式接口,

编程容易且管理简单;不足的是,占用的I/O的线资源较多。如果采用单片机或CPLD/FPGA来控制的话,势必存在浪费I/O口资源的问题。采用LED数码管的动态显示,其特点是:其亮度比静态显示的亮度要差一些;但其电路比较简单,适合于显示位数较多的情况。采用LCD1602液晶显示,其特点是:可以调节其背光亮度,这种显示方式接口,编程虽然有些麻烦,但管理较方便,占用的I/O口资源线也不多。在计算器运算中,需显示的数字、符号较多,按很据个方面的特点,而后可以发现LCD液晶显示,虽然在价格上的确是稍贵于LED数码管;但数码管在硬件设计电路中,会因线太多、线路复杂而过于繁琐,则舍弃LED数码管,选择LCD液晶显示。

4、硬件资源分配

主要用到的硬件:单片机STC89C52 、液晶显示屏LCD1602 、4*4按键键盘硬件分配:

1、P1口:作为输入口,与键盘连接,实现数据的输入;

2、P0、P2口:作为输出口(P2口为高位,P0口为低位),控制LCD液晶显示屏显示数据的结果;

3、液晶显示屏LCD1602显示输出。

二、计算器硬件设计

1、系统组成及硬件框图

2、元器件简介

2.1 STC89C52特点

图 2-2-1 单片机STC89C52

1,、主要性能:

与MCS-51单片机产品兼容;8K字节在系统可编程Flash存储器;1000次擦写周期;全静态操作:0Hz-33Hz;三级加密程序存储器;32个可编程I/O口线、三个16位定时器/计数器、8个中断源;全双工UART串行通信;低功耗空闲和掉电模式,掉电后中断可唤醒;看门狗定时器;双数据指针;掉电标识符。

2、STC89C52的功能特性概述

STC89C52是一种低功耗、高性能CMOS 8位微控制器,具有8K在系统可编程Flash存储器。使用高密度非易失性存储器技术制造,与工业80C51产品指令和引脚完全兼容。片上Flash允许程序存储器在系统可编程,亦适于常规编程器。

2.2 LCD1602液晶显示屏

1、液晶显示原理

液晶显示的原理是利用液晶的物理特性,通过电压对其显示区域进行控制,有电就有显示,这样即可以显示出图形。液晶显示器有厚度薄、适用于大规模集成电路直接驱动、易于实现全彩色显示的特点,目前已经被广泛应用在便携式计算机、数字摄像机、PDA移动通信工具等众多领域。

2、LCD1602的应用简介

LCD1602液晶显示器采用HD44780及其兼容芯片作为点阵式LCD的控制器驱动器,还采用HD44100进行LCD的时分割驱动。HD44780的内部结构主要包括显示数据RAM(DDRAM)、字符发生器ROM(CGROM)、字符发生器RAM(CGRAM)、指令寄存器IR、数据寄存器DR、地址计数器AC(Address Counter)和忙标志BF (Busy Flag)等逻辑电路。

1602液晶模块内部的控制器共有11条控制指令,丰富的指令可以完成液晶

的时序控制、工作方式设置和数据显示等。

采用的LCD1602液晶模块是标准16针插座,接口电路如图2-2-5所示,及各引脚说明如表2-2-2所示。

第1脚Vss为地电源

第2脚VDD接5V正电源

第3脚Vo为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高,对比度过高时会产生“鬼影”,使用时

可以通过一个10K的电位器调整对比度。

第4脚Rs为寄存器选择,高电平选择数据寄存器、低电平选择指令

寄存器。

第5脚Rw为读写信号线,高电平时进行读操作,低电平时选择指令

寄存器。

第6脚E端为使能端,当E端由高电平跳变成低电平时,液晶模块

执行命令。

第7-14脚D0-D7为8位双向数据线。

第15-16脚背光阳极和背光阴极。

3、其他方面简介

DDRAM用来暂存显示字符的代码,共80个字节,DDRAM的各个单元对应着显示屏上的各个字符位,如图2-2-6所示。因此,DDRAM的地址也就意味着显示字符的地址,显示字符时首先要向LCD送显示字符地址。

0 0 0

1

2

3

4

5

6

7

8

9

0A 0B 0C 0D OE OF 1

2

7

LCD

16字*2行

三、系统的硬件设计

为了更好地实现系统的功能,硬件电路的设计应该遵循以下原则:

1、优化硬件电路

采用软件设计与硬件设计相结合的方法。尽管采用软件来实现硬件系统的功能时,也许响应的时间会比单纯使用硬件时长,而且还要占用微处理器(MCU)的时间;但是,用软件实现硬件的功能可以简化硬件结构,提高电路的可靠性。所以,在设计本系统的时候,在满足可靠性和实时性的前提下,尽可能地通过软件来实现硬件功能。

2、可靠性及抗干扰设计

根据可靠性设计理论,系统所用芯片数量越少,系统的平均无故障时间越长。而且,所用芯片数量越少,地址和数据总线在电路板上受干扰的可能性也就越小。因此,系统的设计思想是在满足功能的情况下争取较少数量的芯片。

3、灵活的功能扩展

功能扩展是否灵活是衡量一个系统优劣的重要指标。一次设计往往不能完全考虑到系统的各个方面,系统需要不断完善以及进行功能升级。进行功能扩展时,应该在原有设计的基础上,通过修改软件程序和少量硬件完成。对于本系统而言,就是要求在系统硬件不变的情况下,能够通过修改软件程序,完成功能的升级和扩展。

根据提出的系统设计方案,结合以上三条原则,确定了系统硬件的设计。计算器主要由以下一些功能模块组成:非编码键盘模块、运算模块(单片机内部)、LCD液晶显示模块等。

该系统的硬件设计采用了模块化的设计方法。STC89C52单片机与LCD液晶显示屏显示电路是整个电路的核心,它们能实现系统的功能要求。

简易计算器主要包括:键盘电路、运算电路、输出显示电路。

前面说明了该系统的设计,系统采用了比较简单的设计方案,所以该系统的硬件设计的总外围电路不会产生过多的干扰。下面对系统的外围电路分别作了说

明。键盘部分采用4*4按键键盘,显示部分采用LCD液晶显示屏完全能够很好地实现显示方面的要求。

4、键盘电路的设计

键盘可分为两类:编码键盘和非编码键盘。编码键盘是较多按键(20个以上)和专用驱动芯片的组合;当按下某个按键时,它能够处理按键抖动、连击等问题,直接输出按键的编码,无需系统软件干预。

通用计算机使用的键盘就是编码键盘。在智能仪器中,使用并行接口芯片8279或串行接口HD7279均可以组成编码键盘,同时还可以兼顾数码管的显示驱动,其相关的接口电路和接口软件均可在芯片资料中得到。当系统功能比较复杂,按键数量很多时,采用编码键盘可以简化软件设计。

非编码键盘成本低廉。从成本角度出发,本设计选用的是非编码键盘。如图

图键盘电路

5、显示电路的设计

当系统需要显示少量数据时,采用LCD液晶显示屏进行显示是一种经济实用的方法。P0口作为液晶显示的数据端口,P2.0-P2.2口作为其控制端口,控制LCD液晶显示屏显示输出数据。最终显示电路如图所示

6、其它电路部分

四、计算器软件设计

五、项目总结与体会

这个项目的选择是基于提升自我的目的,在程序设计方面有一定的难度。

主要是在系统还没有到设计完全的时候就开始很心急地买元器件及动手焊板子。后来发现与设计的要求还有偏差,而后反复地改了好几次,浪费了大量的时间和精力。从这次做板子的过程中就发现,虽然制作电路板不是很难,但是马虎不得。不管你是在量长度或者是图线条是有一点疏忽,都可能导致做出的板子有问题不能正常工作。所以我们对待每一个部分都必须认真,这样才更能成功。

此次的项目是由我们小组共同完成的。其中遇到了许多的问题,例如有些意见不同。但是我们都尝试着去理解他人的想法,最终得以结合大家的意见完成项目,也让我们明白了合作的重要性。

六、程序部分

/* 简易计算器程序 */

#include

#include

#define uint unsigned int

#define uchar unsigned char

sbit lcdrs = P2^3; //定义端口

sbit lcdrw = P2^4;

sbit lcden = P2^5;

sbit sound = P2^0;

uchar code table[]={'0','1','2','3','4','5','6','7','8','9',

'+','-','*','/','=','.'};

uchar key,flag1,cal,judge1,judge2;

//cal: 计算calculation flag :标志 flag1用于标记按键进度

//judge1, judge2为两个判断变量

uchar a[20],b[25];

float Answer=0;

/******************************************************************** * 名称 : delay()

* 功能 : 延时,延时时间大概为5US。

********************************************************************* **/

void delay()

{

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

}

void Delay(uint i)

{

uint x,j;

for(j=0;j

for(x=0;x<=148;x++);

}

void delay1(uchar z) //用于普通延时

{

uchar x,y;

for(x=z;x>0;x--)

for(y=110;y>0;y--);

}

/******************************************************************** * 名称 : bit Busy(void)

* 功能 : 这个是一个读状态函数,读出函数是否处在忙状态

********************************************************************* **/

bit Busy(void)

{

bit busy_flag = 0;

lcdrs = 0;

lcdrw = 1;

lcden = 1;

delay();

busy_flag = (bit)(P0 & 0x80);

lcden = 0;

return busy_flag;

}

/******************************************************************** * 名称 : wcmd(uchar del)

* 功能 : 1602命令函数

********************************************************************* **/

void wcmd(uchar del)

{

while(Busy());

lcdrs = 0;

lcdrw = 0;

lcden = 0;

delay();

P0 = del;

delay();

lcden = 1;

delay();

lcden = 0;

}

/********************************************************************

* 名称 : wdata(uchar del)

* 功能 : 1602写数据函数

********************************************************************* **/

void wdata(uchar del)

{

while(Busy());

lcdrs = 1;

lcdrw = 0;

lcden = 0;

delay();

P0 = del;

delay();

lcden = 1;

delay();

lcden = 0;

}

/******************************************************************** * 名称 : L1602_init()

* 功能 : 1602初始化,请参考1602的资料

********************************************************************* **/

void L1602_init(void)

{

wcmd(0x38);

Delay(5);

wcmd(0x38);

Delay(5);

wcmd(0x38);

Delay(5);

wcmd(0x38);

wcmd(0x0d);

}

/*蜂鸣器发声函数*/

void voice()

{

sound=1;

delay1(2000);

sound=0;

}

/* LCD写命令函数 */

void write_com(uchar com)

{

lcdrs=0; //写指令控制端为低 P0=com;

delay1(5);

lcden=1; //给个高脉冲结束

delay1(4);

lcden=0;

}

/*写数据函数*/

void write_data(uchar date)

{

lcdrs=1; //写数据控制端高电平 P0=date;

delay1(5);

lcden=1; //给个高脉冲结束

delay1(5);

lcden=0;

}

/*键盘扫描函数 */

void keyscan()

{

uchar temp,xy=0xf7; //xy变量存储第几行

uint i;

for(i=0;i<4;i++) //行移动

{

xy=_crol_(xy,1); //_crol_为左移函数 P1=xy;

temp=P1;

temp=temp&0x0f;

if(temp!=0x0f)

{ delay1(10); //去抖

temp=P1;

temp=temp&0x0f;

if(temp!=0x0f)

{ temp=P1;

voice();

switch(temp) //按键定位

{

case 0xee: key=7; break;

case 0xde: key=8; break;

case 0xbe: key=9; break;

case 0x7e: key=10; break;

case 0xed: key=4; break;

case 0xdd: key=5; break;

case 0xbd: key=6; break;

case 0x7d: key=11; break;

case 0xeb: key=1; break;

case 0xdb: key=2; break;

case 0xbb: key=3; break;

case 0x7b: key=12; break;

case 0xe7: key=0; break;

case 0xd7: key=15; break;

case 0xb7: key=14; break;

case 0x77: key=13; break;

}

while(temp!=0x0f)

{ temp=P1;

temp=temp&0x0f;

}

flag1=1; //flag=1标志按键检测完毕 }

}

}

}

/*计算结果并显示函数*/

void cal_show()

{ uchar i,j,n;

long double s,temp2;

long int s1,s2,a1,b1,c1,temp1;

a1=b1=1;

s1=s2=0;

s=0;

if(judge2!=0)

{

for(j=0;j

{ for(i=0;i

{a1=a1*10;}

s1=s1+a[j]*a1;

a1=1;

}

for(j=judge1+1;j

{ for(i=0;i

{b1=b1*10;}

s2=s2+a[j]*b1;

b1=1;

}

if(cal==10) //判断是什么运算,并执行运算

{s=s1+s2; }

if(cal==11) {s=s1-s2; }

if(cal==12)

{

s=s1*s2;

}

if(cal==13)

{

s=(s1*0.1)/(s2*0.1); //把s1,s2转化为小数形式 ? }

Answer=s;

}

else s=Answer;

//下面对结果进行四舍五入

c1=(long int)(s*1000)%10;

if(c1>5 || c1==5) s=s+0.01;

//下面对数据进行处理

temp1=(long int)(s); //temp1 是答案的整数部分

temp2=s-temp1; //temp2 是答案的小数部分

n=0;

while(temp1) //整数部分放入数组

{ b[n]=temp1%10;

temp1=temp1/10;

n++;

}

b[n]=temp2*10; //把小数部分的第一位变成整数并放进数组

b[n+1]=(uchar)(temp2*100)%10; //小数第二位......放进数组

//下面把得到的结果显示到液晶上面去

for(i=n;i>0;i--)

{

write_data(table[b[i-1]]);//输出整数部分

}

write_data(table[15]);//输出小数点间隔整数部分和小数部分

write_data(table[b[n]]);//小数点后第一位

write_data(table[b[n+1]]);//小数点后第二位

}

/******************************************************************** * 名称 : L1602_string(uchar hang,uchar lie,uchar *p)

* 功能 : 1602写字符串函数

********************************************************************* **/

void L1602_string(uchar hang,uchar lie,uchar *p)

{

uchar a,b=0;

if(hang == 1) a = 0x80;

if(hang == 2) a = 0xc0;

a = a + lie - 1;

while(1)

{

wcmd(a++);

if((*p == '\0')||(b==16)) break;

b++;

wdata(*p);

p++;

}

}

/******************************************************************** * 名称 : L1602_char(uchar hang,uchar lie,char sign)

* 功能 : 1602写指令函数

********************************************************************* **/

void L1602_char(uchar hang,uchar lie,char sign)

{

uchar a;

if(hang == 1) a = 0x80;

if(hang == 2) a = 0xc0;

a = a + lie - 1;

wcmd(a);

wdata(sign);

}

void main()

{

uchar k,i,j;

sound=0;

L1602_init();

for(k=5;k>0;k--)

{

L1602_string(1,1,"Welcome To Use");

L1602_string(2,1," Calculator ");

L1602_char(2,16,k% 10 + 48);

sound=1;

Delay(400);

sound=0;

Delay(600);

}

sound=0;

L1602_init();

write_com(0x01);

write_com(0x80); //将液晶指针定位在第一行第一个单元 key=15;

i=flag1=0;

while(1)

{ keyscan();

if(flag1==1)

{if(key==15) //清零

{write_com(0x01);

i=0;

for(j=0;j<20;j++)

a[j]=b[j]=0;

judge1=judge2=0; //judge2=0,不会在液晶存放数据

}

else if(key<10) //10以下是数字显示部分

{ write_data(table[key]);

a[i]=key;

i++;

}

else if(key<14) //10~14属于运算符号 +

{ write_data(table[key]);

judge1=i-1;

cal=key;

}

else // key=14时为=号

{

L1602_string(2,1,"Answer");

write_data(table[key]); //输出等于号

if(i==0)

judge2=0;

else

judge2=i-1;

write_com(0x80+0x47); //将液晶指针定位在第二行第一个单元 cal_show(); //调用计算并显示函数

}

flag1=0; //键盘检测标志置0进行下一次检测

}

}

}

本文来源:https://www.bwwdw.com/article/3pdl.html

Top