一个简易计算器的实现

本文是关于一个嵌入式系统课程设计的报告,详细介绍了简易计算器"计算精灵"的设计与实现过程,包括需求分析、概要设计、详细设计与实现以及测试。计算器功能包括加减乘除和键盘中断输入,采用C语言编写,涉及数据结构和token处理。测试结果显示功能基本达到要求,提出了功能扩展和性能改进方案。

一个简易计算器的实现

Author: bripengandre

相关源码见链接:http://download.csdn.net/source/982727

 

摘要:嵌入式系统,以其占用资源少、专用性强,在汽车电子、航空和工控领域得到了广泛地应用。本文为嵌入式系统课程设计的报告,文中给出了一个简易计算器的设计与实现过程。本文先通过需求分析,介绍该计算器要实现的功能;接着通过概要设计,给出了该计算器的整体框架;然后通过详细设计及实现,讲述该计算器的具体实现;最后通过测试结果及分析,给出了对该计算器的评价及改进意见。

关键词:嵌入式;计算器;栈

1         项目名称

本课程设计的项目名称是“一个简易计算器的实现”。本项目要实现的计算器的名称为“计算精灵(Calculator Daemon)”。本文的后续部分将使用“计算精灵”来称呼本项目。

2         需求分析

计算精灵的功能框图如 1 所示,从左到右,对应计算精灵从输入到输出所要具备的功能。

一个简易计算器的实现的blog文插图

1 计算精灵的功能框图

输入模块要有的功能是:采用键盘中断的方式来读取输入,能修改计算表达式(如支持后退功能)。

处理模块是计算精灵的核心,所支持的功能有:能够检查计算表达式的有效性(如输入不合法,能及时给出提示),支持加减乘除四则运算,操作式支持多位数和浮点数,同时预留扩充接口(以后可能将增添支持其它操作符、支持十六进制计算、支持函数、支持表达式记忆、支持保存计算结果等功能)。

输出模块的功能简单,只需支持串口输出和lcd(液晶显示屏)显示即可。但为了使输出美观,对串口输出和lcd的显示还要做一定的美工设计。

3         概要设计

在需求分析的基础上,得到如 2 所示的系统框图。

一个简易计算器的实现的blog文插图

2 计算精灵的系统框图

系统分为键盘输入模块、计算处理模块、串口及LCD输出模块,各模块分别提供需求分析中提到的输入模块、处理模块和输出模块的功能。

2 中的箭头代表数据的流动方向。系统的IPO表(输入、处理和输出表)如 1 所示。

编号

输入数据

处理

输出数据

备注

1

用户操作键盘

键盘输入模块读取键值,形成计算式

计算式

2

计算式

计算处理模块按流程处理计算式,如果中途判断出计算式非法,则及时给出提示

计算结果或非法提示

计算结果和非法提示都看成是处理结果

3

计算处理结果

串口和LCD根据结果得出要显示的内容

处理结果通过串口输出,并显示在LCD

1 计算精灵系统的IPO

4         项目分工

本项目由彭令鹏和肖品负责,具体的分工如 2 所示。

任务

负责人

起止时间

备注

需求分析

彭令鹏、肖品

2008.12.15-2008.12.18

明确需求很关键,所以花了较长的时间

概要设计

彭令鹏

2008.12.19-2008.12.20

键盘输入模块的设计与实现

肖品

2008.12.21-2008.12.22

该部分还包括模块测试

计算处理模块的设计与实现

彭令鹏

2008.12.21-2008.12.23

该部分还包括模块测试

串口及LCD输出模块的设计与实现

肖品

2008.12.23-2008.12.23

该部分还包括模块测试

集成测试

彭令鹏、肖品

2008.12.24-2008.12.25

测试是发现问题并解决问题的关键环节

项目总结

彭令鹏、肖品

2008.12.26-2008.12.28

2 项目的分工和进度安排

5         详细设计与实现

该部分仅介绍我所负责的计算处理模块的设计与实现。

5.1.      计算处理模块的设计

5.1.1.        模块框图

根据数据在模块中的流动,得到如 3 所示的设计框图。

一个简易计算器的实现的blog文插图

3 计算处理子模块的设计框图

3 中看出,计算处理模块根据功能又可细分为解析表达式子模块、组合计算token子模块和格式化结果子模块等三个子模块。

这三个子模块的功能等信息如 3 所示。

子模块名称

输入数据

功能

输出结果

解析表达式子模块

来自键盘输入模块的计算式

从计算式中解析出操作符和操作数

操作数和操作符(统称token),或错误提示

组合计算token子模块

来自解析表达式子模块的token

将各token按数学运算规则组织起来,进行数学运算

计算结果或错误

格式化结果子模块

来自组合计算token模块的计算结果,以及来自前面各子模块的错误提示

将计算结果或错误提示,分别格式化成相应的格式,以方便输出模块直接读取

格式化后的结果,该结果流向串口及LCD显示模块

3 各子模块的责任表

5.1.2.        接口数据结构设计

计算处理模块与键盘输入处理模块的接口数据结构

因为计算处理模块和键盘输入处理模块最终都会以模块的形式被主程序调用,所以这两者之间的接口可以通过一个在两者的生存期内一直存在的字符串数组实现。具体设计中,为既避免传形参的麻烦,又避免该数据结构全局可见,采用如下所示的静态数据结构。

typedef struct _expr_info

{

char expr[MAX_EXPR_LEN]; /* 存放计算式 */

int len; /* 计算式的长度 */

}expr_info_t;

计算处理模块与键盘输出处理模块的接口数据结构

同样地,基于与上一个数据结构同样的理由,采用如下的静态数据结构。

typedef struct _display_info

{

char expr_buf[MAX_EXPR_LEN]; /* 存放计算式(有可能包含计算结果) */

int expr_len; /* 计算式长度 */

char alert_buf[MAX_ALERT_LEN]; /* 存放提示信息 */

int alert_len; /* 提示信息长度 */

char isdirty; /* reserved */

char isrefresh; /* 是否刷新(输出完一个计算表达式的结果后,再输入新计算式需刷新) */

}display_info_t;

注意,结构体提供了expr_buf来存放格式化后的计算式(可能包含计算结果,如“3+5=8),提供了alert_buf来存放格式化了的错误提示信息。

5.2.      计算处理模块的实现

该部分介绍各个子模块的实现。

5.2.1.        解析表达式子模块的实现

(1)     关键的数据结构的实现

从计算式中解析出token(操作数和操作符)是该子模块最重要的功能。因为操作符仅仅是‘+’、‘-’、‘*’和‘/’这样的字符,而操作数有则是一串数字(如3.14159),因此解析出操作数是该子模块的的难点和重点。为解析出操作数,设计如下的数据结构。

struct _opnd

{

char isfloat; /* 是否是浮点数 */

double float_factor; /* 当前的小数因子,第i位小数的因子为(0.1)^i */

double value; /* 操作数当前的值 */

};

(2)     程序流程

解析表达式子模块得到一个输入字符后,首先判断该字符是不是数字或小数点,如果是的话,则说明这字符及之后的若干字符构成一个操作数,因此将从这字符开始的连续的字符或小数点解析成一个操作数,否则将当前字符解析成一个操作符。整个程序流程如 4 所示。

一个简易计算器的实现的blog文插图

4 解析表达式子模块的程序流程

5.2.2.        组合计算token子模块的实现

(1)     关键的数据结构的实现

经解析子模块后,本模块得到了一个个操作数或操作符,本模块所要实现的就是将这些操作token组合起来并按照数学规则来运算,这依赖于两个栈来处理计算顺序。

这两个栈一个用来存放操作数,一个用来存放操作符,前者的数据类型是double,后者的数据类型是char,所以如果将栈单元实现成double类型,无疑会造成存储浪费,这在嵌入式系统中是不大允许的。为此,该子模块设计了可自定义栈单元大小的栈,相关的数据结构如下。

typedef struct _seq_stack

{

void *base; /* 栈底指针 */

void *top; /* 栈顶指针 */

int node_cnt; /* 栈内当前的数据个数 */

int node_size; /* 栈单元的大小 */

int stack_size; /* 栈空间的大小 */

}seq_stack_t, *pseq_stack_t;

 

利用这个结构体,我们就可以来管理自定义栈单元大小的栈了。那我们是通过什么接口来自定义栈大小的呢?看下面这个函数

int init_stack(seq_stack_t * stack, void *buf, int stack_size, int node_size);

通过这个函数,可以初始化栈,第一个参数用来返回栈指针(指向管理栈的结构体),第二个参数用来传入栈所用的空间,第三个参数用来传入栈空间的大小(栈单元的个数),第四个参数用来传入栈单元的大小(自定义栈单元的大小)。

另外为方便扩充操作符,以及预留支持函数运算,采用了如下所示的两个表。

static optr_info_t optr_info[] =

{

{'+', 0, add},

{'-', 1, sub},

{'*', 2, mul},

{'/', 3, div},

/* {'%', 4, mod}, */

{'(', 4, NULL},

{')', 5, NULL},

{'=', 6, NULL},

{0, 0, NULL} /* indicate array size */

};

 

static char optr_pri_table[][sizeof(optr_info)/sizeof(optr_info_t)-1]=

{

{GREAT_PRI, GREAT_PRI, LOW_PRI, LOW_PRI/*, LOW_PRI*/, LOW_PRI, GREAT_PRI, GREAT_PRI},

{GREAT_PRI, GREAT_PRI, LOW_PRI, LOW_PRI/*, LOW_PRI*/,LOW_PRI, GREAT_PRI, GREAT_PRI},

{GREAT_PRI, GREAT_PRI, GREAT_PRI, GREAT_PRI/*, GREAT_PRI*/, LOW_PRI, GREAT_PRI, GREAT_PRI},

{GREAT_PRI, GREAT_PRI, GREAT_PRI, GREAT_PRI/*, GREAT_PRI*/, LOW_PRI, GREAT_PRI, GREAT_PRI},

/* {GREAT_PRI, GREAT_PRI, GREAT_PRI, GREAT_PRI, GREAT_PRI, LOW_PRI, GREAT_PRI, GREAT_PRI},*/

{LOW_PRI, LOW_PRI, LOW_PRI, LOW_PRI/*,  LOW_PRI*/, LOW_PRI, EQUAL_PRI, OTHER_PRI},

{GREAT_PRI, GREAT_PRI, GREAT_PRI, GREAT_PRI/*, GREAT_PRI*/, OTHER_PRI, GREAT_PRI, GREAT_PRI},

{LOW_PRI, LOW_PRI, LOW_PRI, LOW_PRI/*,LOW_PRI*/, LOW_PRI, OTHER_PRI, EQUAL_PRI}

};

第一个表是用结构体数组实现的,结构体的第一个元素是操作符,第二个元素为操作符索引,第三个元素是对应的操作函数。很明显当需要增加sin函数时,只需在表中添加{'sin', 7, sin}一项,这是非常方便的。当然当表过大时,查找可能比较耗时,可采用hash查找等算法改进之。

第二个表是操作数的优先级表。新增操作符时,只需改表即可。当然随着操作数的增加,该表层O( )增长,这可通过将操作符分类的方法来避免(如像C语言中一样,因为加和减的优先级和结合性一样,可划为一类;同样地,乘和除也可划为一类)。

(2)     程序流程

组合计算token采用的是经典的表达式计算算法,这里不再详述,可参考参考文献 [3]

整个程序流程如 5 所示,需要注意的是为表达简洁,图中省去了出错处理。

一个简易计算器的实现的blog文插图

5 组合计算token子模块的程序流程

5.2.3.        格式化结果子模块的实现

(1)     关键的数据结构的实现

本模块相对比较简单,关键的数据结构,就是用来与串口及LCD显示输出模块交互接口数据结构,已在 0 中给出。

(2)     程序流程

本模块的程序流程如 6 所示。需要注意的是当计算并输出完计算结果后,再输出新的计算表达式时,需刷新屏幕(针对LCD而言)。

一个简易计算器的实现的blog文插图

6 格式化结果子模块

5.3.      计算处理模块的测试

该模块的测试分功能测试和性能测试两部分。因为并不与硬件直接相关,所以测试在vc6.0下进行。

功能性测试。生成一个有多个计算式的文件,本模块读取这个文件,然后对这些计算式进行计算处理,将处理结果与计算式应得到的结果比较,如果相同,则可认为功能基本达到,否则有问题。我通过这种方法,前前后后改了不下5次,才通过功能测试。

性能测试。因为该部分是纯粹的运算,对于小型运算其速度应该不错,所以没有测试。有一个性能改进点是“边输入边计算”,而不是像现在一样只当表达式全部读取完后才开始计算,但这使能修改表达式的实现难度增加了。

6         集成测试

将各模块级联成一个系统,然后输入多组测试用例数据,看输出是否满足要求。注意,在联调较大的系统时,宜把相关性较大的几个模块先联成一个较大的模块,只有当这个较大的模块通过测试时,才能进行更大程度的集成。

考虑到我们的系统很小,所以我们将三个模块一次性集成到了一次,编译倒是非常顺利,只改了一些包含语句就搞定了,但在输入输出的处理上还是出现了很多bug。比如最开始时,我们的键盘中断输入死活工作不正常,一运行程序,程序就跑飞了,后来发现原来是中断服务程序里没有清除中断标志。所以干事情还是要心细。

7         项目总结及改进方案

本项目成功实现了一个简易的计算器。在项目开发中遇到了不少问题,我们所做的是硬着头皮去解决它们。虽然这个解决问题的过程几近于痛苦,但我们最终还是熬过去了,于是也就尝到了大苦之后的甘甜。通过该项目,我们掌握了嵌入式开发的基本流程,以及遇到问题后分析并解决问题的方法,并且培养了团队合作的意识,这些都是难能可贵的。

至于项目的改进方案,在之前的各节中已有论述,这里总结下。本项目在功能上还可以添加支持其它操作符、支持十六进制计算、支持函数运算、支持表达式记忆、支持保存计算结果等。在性能上,计算表达式时,可采用“边输入边尽量计算”的方法,这可充分利用用户与程序的交互空闲;同时,当扩充的功能比较多时,程序中用到的表会很大,这可通过优化表存储来减少存储空间,并通过建立hash索引等来优化表查找。

参考资料

[1]       鄢舒. 嵌入式系统设计课件. 武汉:华中科技大学内部资料, 2008

[2]       孙天泽, 袁文菊等. 嵌入式设计及Linux驱动开发指南——基于ARM9处理器. 北京:电子工业出版社, 2005

[3]       严蔚敏, 吴伟民. 数据结构(C语言版). 北京:清华大学出版社, 1997

 
课程程设计,简易计算器!实验已经成功目录 1、引言……………………………………………………………………………………………… 2 1.1 计算器意义………………………………………………………………………………2 1.2 电子计算器的特殊键 …………………………………………………………………2 2 、单片机概述.……………………………………………………………………………………2 3 、芯片简介 ………………………………………………………………………………………3 3.1 MSC-51芯片简介…………………………………………………………………………3 4 、相关知识 ………………………………………………………………………………………6 4.1数码管显示…………………………………………………………………………………6 4.2矩阵按键 …………………………………………………………………………………6 5 、计算器硬件电路设 …………………………………………………………………………7 6 、计算器程序设计………………………………………………………………………………7 6.1存储单元分配………………………………………………………………………………7 6.2 主程序设计…………………………………………………………………………………7 6.3 数码管显示数据转换子程序CONV ……………………………………………………7 6.4 数码管动态显示子程序………………………………………………………………… 7 7 、系统硬件设计……………………………………………………………………………………7 7.1 系统总框图如下……………………………………………………………………………8 7.2 计算器硬件线路图…………………………………………………………………………8 7.3 系统工作原理 ………………………………………………………………………………9 8、汇编语言程序源代码……………………………………………………………………………10 9 、结语………………………………………………………………………………………………17 10、设计实物图……………………………………………………………………………18 摘要 近年来随着科技的飞速发展,单片机的应用正在不断深入,同时带动传统控制检测技术日益更新。在实时检测和自动控制的单片机应用系统中,单片机往往作为一个核心部件来使用,仅单片机方面知识是不够的,还应根据具体硬件结构软硬件结合,加以完善。 本任务是个简易的两位数的四则运算,程序都是根据教材内和网络中的程序参考编写而成,在功能上还并不完善,限制也较多。本任务重在设计构思与团队合作,使得我们用专业知识、专业技能分析和解决问题全面系统的锻炼。 关键词: 单片机 计算器 范围 加减乘除 1 引言 1.1 计算器的历史 说起计算器,值得我们骄傲的是,最早的计算工具诞生在中国。中国古代最早采用的一种计算工具叫筹策,又被叫做算筹。这种算筹多用竹子制成,也有用木头,兽骨充当材料的。约二百七十枚一束,放在布袋里可随身携带。直到今天仍在使用的珠算盘,是中国古代计算工具领域中的另一项发明,明代时的珠算盘已经与现代的珠算盘几乎相同。17世纪初,西方国家的计算工具有了较大的发展,英国数学家纳皮尔发明的"纳皮尔算筹",英国牧师奥却德发明了圆柱型对数计算尺,这种计算尺不仅能做加减乘除、乘方、开方运算,甚至可以计算三角函数,指数函数和对数函数,这些计算工具不仅带动了计算器的发展,也为现代计算器发展奠定了良好的基础,成为现代社会应用广泛的计算工具。 1.2 电子计算器的特殊键 在使用电子计算器进行四则运算的时候,一般要用到数字键,四则运算键和清除数据键。除了这些按键,还有一些特殊键,可以使计算更加简便迅速。 2 单片机概述 单片机微型计算机是微型计算机的一个重要分支,也是颇具生命力的机种。单片机微型计算机简称单片机,特别适用于控制领域,故又称为微控制器。 通常,单片机由单块集成电路芯片构成,内部包含有计算机的基本功能部件:中央处理器、存储器和I/O接口电路等。因此,单片机只需要和适当的软件及外部设备相结合,便可成为一个单片机控制系统。 单片机经过1、2、3、3代的发展,目前单片机正朝着高性能和多品种方向发展,它们的CPU功能在增强,内部资源在增多,引角的多功能化,以及低电压底功耗。 3 芯片简介 3.1 MSC-51芯片简介 MCS-51单片机内部结构 AT89C51是与MCS-51系列单片机兼容的典型产品,我们以这一代表性的机型进行系统的讲解。 AT89C51单片机包含中央处理器、程序存储器(ROM)、数据存储器(RAM)、定时/计数器、并行接口、串行接口和中断系统等几大单元及数据总线、地址总线和控制总线等三大总线,现在我们分别加以说明: •中央处理器: 中央处理器(CPU)是整个单片机的核心部件,是8位数据宽度的处理器,能处理8位二进制数据或代码,CPU负责控制、指挥和调度整个单元系统协调的工作,完成运算和控制输入输出功能等操作。 •数据存储器(RAM) AT89C51内部有128个8位用户数据存储单元和128个专用寄存器单元,它们是统一编址的,专用寄存器只能用于存放控制指令数据,用户只能访问,而不能用于存放用户数据,所以,用户能使用的RAM只有128个,可存放读写的数据,运算的中间结果或用户定义的字型表。 图1 •程序存储器(ROM): AT89C51共有4096个8位掩膜ROM,用于存放用户程序,原始数据或表格。 •定时/计数器(ROM): AT89C51有两个16位的可编程定时/计数器,以实现定时或计数产生中断用于控制程序转向。 •并行输入输出(I/O)口: AT89C51共有4组8位I/O口(P0、 P1、P2或P3),用于对外部数据的传输。 •全双工串行口: AT89C51内置一个全双工串行通信口,用于与其它设备间的串行数据传送,该串行口既可以用作异步通信收发器,也可以当同步移位器使用。 •中断系统: AT89C51具备较完善的中断功能,有两个外中断、两个定时/计数器中断和一个串行中断,可满足不同的控制要求,并具有2级的优先级别选择。 •时钟电路: AT89C51内置最高频率达12MHz的时钟电路,用于产生整个单片机运行的脉冲时序,但AT89C51单片机需外置振荡电容。 单片机的结构有两种类型,一种是程序存储器和数据存储器分开的形式,即哈佛(Harvard)结构,另一种是采用通用计算机广泛使用的程序存储器与数据存储器合二为一的结构,即普林斯顿(Princeton)结构。INTEL的MCS-51系列单片机采用的是哈佛结构的形式,而后续产品16位的MCS-96系列单片机则采用普林斯顿结构。 下图是MCS-51系列单片机的内部结构示意图2。 图2 MCS-51的引脚说明: MCS-51系列单片机中的8031、AT89C51及8751均采用40Pin封装的双列直接DIP结构,右图是它们的引脚配置,40个引脚中,正电源和地线两根,外置石英振荡器的时钟线两根,4组8位共32个I/O口,中断口线与P3口线复用。现在我们对这些引脚的功能加以说明: MCS-51的引脚说明: MCS-51系列单片机中的8031、AT89C51及8751均采用40Pin封装的双列直接DIP结构,右图是它们的引脚配置,40个引脚中,正电源和地线两根,外置石英振荡器的时钟线两根,4组8位共32个I/O口,中断口线与P3口线复用。现在我们对这些引脚的功能加以说明:如图3 图3 Pin9:RESET/Vpd复位信号复用脚,当AT89C51通电,时钟电路开始工作,在RESET引脚上出现24个时钟周期以上的高电平,系统即初始复位。初始化后,程序计数器PC指向0000H,P0-P3输出口全部为高电平,堆栈指针写入07H,其它专用寄存器被清“0”。RESET由高电平下降为低电平后,系统即从0000H地址开始执行程序。然而,初始复位不改变RAM(包括工作寄存器R0-R7)的状态,AT89C51的初始态。 AT89C51的复位方式可以是自动复位,也可以是手动复位,见下图4。此外,RESET/Vpd还是一复用脚,Vcc掉电其间,此脚可接上备用电源,以保证单片机内部RAM的数据不丢失。 图4 •Pin30:ALE/ 当访问外部程序器时,ALE(地址锁存)的输出用于锁存地址的低位字节。而访问内部程序存储器时,ALE端将有一个1/6时钟频率的正脉冲信号,这个信号可以用于识别单片机是否工作,也可以当作一个时钟向外输出。更有一个特点,当访问外部程序存储器,ALE会跳过一个脉冲。 如果单片机是EPROM,在编程其间, 将用于输入编程脉冲。 •Pin29: 当访问外部程序存储器时,此脚输出负脉冲选通信号,PC的16位地址数据将出现在P0和P2口上,外部程序存储器则把指令数据放到P0口上,由CPU读入并执行。 •Pin31:EA/Vpp程序存储器的内外部选通线,8051和8751单片机,内置有4kB的程序存储器,当EA为高电平并且程序地址小于4kB时,读取内部程序存储器指令数据,而超过4kB地址则读取外部指令数据。如EA为低电平,则不管地址大小,一律读取外部程序存储器指令。显然,对内部无程序存储器的8031,EA端必须接地。 在编程时,EA/Vpp脚还需加上21V的编程电压。 4 相关知识 4.1数码管显示 在本任务中用4位数码管显示当前数值的千,百,十,个,由于数码管个数多,如采用静态显示方式,则占用单片机的I/O口线太多,如果用定时器/计数器的串行移位寄存器工作方式及外接串入并出移位寄存器74LS164的方式,则电路复杂。所以,在数码管个数较多时,常采用动态显示方式。 如图1-1所示为单片机应用系统中的一种数码管动态显示电路图,4位数码管的相同段并联在一起,由一个8位I/O(P1口)输出字形码控制显示某一字形,每个数码管的公共端由另外一个I/O口(P0口)输出的字位码控制,即数码管显示的字形是由单片机I/O口输出的字形码确定,而哪个数码管点亮是由单片机I/O口输出的字位码确定的。4个数码管分时轮流循环点亮,在同一时刻只有1个数码管点亮,但由于数码管具有余辉特性及人眼具有视觉暂留特性,所以适当地选取循环扫描频率,看上去所有数码管是同时点亮的,察觉不出闪烁现象。动态显示方式所接数码管不能太多,否则会因每个数码管所分配的实际导通时间太少,使得数码管的亮度不足。在本任务中,为了简便,字形码和字位码都没由加驱动电路,在实际应用中应加驱动电路。数码管有共阴极和共阳极两种,对于共阳数码管,字形驱动输出0有效,字位驱动输出1有效;而对于共阴数码管则相反,即:字形驱动输出1有效,字位驱动输出0有效。 4.2矩阵按键 键盘是单片机系统中最常用的人机对话输入设备,用户通过键盘向单片机输入数据或指令。键盘控制程序需完成的任务有:监测是否有键按下,有键按下时,在无硬件去抖的动电路时,应用软件延时方法消除按键抖动影响;当有多个键同时按下时,只处理一个按键,不管一次按键持续多长时间,仅执行一次按键功能程序。 矩阵按键扫描程序是一种节省IO口的方法,按键数目越多节省IO口就越可观,思路:先判断某一列(行)是否有按键按下,再判断该行(列)是那一只键按下。但是,在程序的写法上,采用了最简单的方法,使得程序效率最高。本程序中,如果检测到某键按下了,就不再检测其它的按键,这完全能满足绝大多数需要,又能节省大量的CPU时间。 本键盘扫描程序的优点在于:不用专门的按键延时程序,提高了CPU效率,也不用中断来扫描键盘,节省了硬件资源。另外,本键盘扫描程序,每次扫描占用CPU时最短,不论有键按下或者无键按下都可以在很短的时间完成一次扫描。 本键盘扫描子程序名叫key,每次要扫描时用lcall key调用即可。 5 计算器硬件电路设计 AT89C51单片机的P2口作键盘口,其中P2.4-P2.7为键盘扫描输出线,P2.0-P2.3为键盘扫描输入线。键盘由4*4共16个按键组成,10个数字键(由0-9组成)5个运算符号(加减乘除等于)组成,1个清除键(作用相当于整体复位)。4个数码管用于显示当前数值的千,百,十,个,采用动态显示方式,P1口接4个数码管的七段,P0口分别接4个数码管的公共端,P1口输出数码管的字形码,P0口输出数码管的字位码。 6 计算器程序设计 6.1存储单元分配 30H单元:数值个位显示单元;31H单元:数值十位显示单元;32H单元:数值百位显示单元;33H单元:数值千位显示单元;23H单元:第一操作数存储单元;24H单元:第二操作数存储单元;25H单元:键值暂存单元;27H单元:清除键状态;34H-37H单元:结果数据转换暂存单元;38H-39H单元:结果高低8位暂存单元;R5单元:操作数计数单元;R4单元:操作数数值位数计数单元;R3单元:运算符号存储单元。 6.2 主程序设计 主程序进行程序中用到的一些存储单元的初始化,数值显示和4*4键盘扫描。首先,进行存储单元初始化,给数码管显示单元30H-33H赋予“0000”字形数据,将数值计数单元,存储单元,23H-25H,34H-37H,38H,39H,3AH,3BH,3CH,赋予初值零。之后,调用键盘扫描子程序,和数码管显示数据转换程序,数码管动态显示子程序。主程序不断进行键盘扫描,数码管显示数据转换子程序和动态显示子程序。 6.3 数码管显示数据转换子程序CONV 由于数值单元存放的是二进制数,而用户熟悉的是十进制数,所以应将数值单元中的二进制转换为十进制数,即BCD码。要通过数码管显示出当前数值,还必须将BCD码进一步转换为七段码,转换的最终结果数据存放于显示缓冲区30H-33H单元中,其中30H单元存放数值的个位七段码,31H单元存放数值的十位七段码,32H单元存放数值的百位七段码,33H单元存放数值的千位七段码。 6.4 数码管动态显示子程序 本任务由P1口输出字形码,P0口输出字位码。先将存放于30H单元的数值个位七段码由P1口输出,同时P0口输出使数值个位显示数码管点亮的字位码。由于采用的是共阳数码管,所以只有该位数码管对应的P0.0为1,其他位P0.1-P0.3位0,点亮延时10MS。然后P1口输出数值十位七段码,P0.1位1,数值十位数码管点亮,延时10MS。接着P1口输出数值百位七段码,P0.2为1,数值百位数码管点亮,延时10MS。最后P1口输出数值千位七段码,P0.3为1,数值千位数码管点亮,延时10MS。 7 系统硬件设计 选用设备AT89C51单片机一片,选用设备:AT89C51单片机一片,17个键盘,4位共阳极的七段数码管一个,连线若干,电容3个,电阻5个,晶振1个。 7.1 系统总框图如下:
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值