矩阵键盘简易计算器
《微处理器系统与接口技术》课程实践报告
计算器
班 级:
学 号:
学生姓名:
指导老师:
日 期: 2014.7.5
******电子与信息工程学院
目录
1、设计题目:计算器........................................................................................................................ 3
2、设计目的 . .................................................................................................................................... 3
3、计算器总体设计框图 . ................................................................................................................ 3
4、计算器详细设计过程 . ................................................................................................................ 4
4.1输入模块 . ............................................................................................................................ 4
4.2键盘输入电路 . .................................................................................................................... 5
4.3主程序模块 . ........................................................................................................................ 6
5、分析与调试 . ................................................................................................................................ 6
7、运行结果 . .................................................................................................................................... 8
8、结束语......................................................................................................................................... 8
8、参考文献 . .................................................................................................................................... 8
9、源程序附录 . ................................................................................................................................ 9
9.1主程序 . ................................................................................................................................ 9
9.2延时函数delay ................................................................................................................. 12
9.3显示函数display .............................................................................................................. 12
9.4键盘扫描函数 . .................................................................................................................. 14
9.5预定义函数 . ...................................................................................................................... 15
1、设计题目:计算器
2、设计目的
此次课程实践题目是基于单片机简单计数器的设计,本此设计使用的是Intel 公司MCS-51系列的8051AH 单片机。设计的计算器可以实现2位小数的加、减、乘、除运算以及整数的乘方运算,其中用4*4矩阵键盘来输入待参与运算的数据和运算符;八位数码管动态显示输入待参与运算的数据以及运算后产生的结果,每个硬件模块的调用过程中涉及到了函数入口及出口参数说明,函数调用关系描述等。
3、计算器总体设计框图
计算器以MCS-51系列的8051AH 单片机作为整个系统的控制核心,应用其强大的I/O功能和计算速度,构成整个计算器。通过矩阵键盘输入运算数据和符号,送入单片机进行数据处理。经单片机运算后控制LED 数码管的输出。整体框图如图1所示:
图3 整体框图
本系统硬件主要由矩阵键盘、独立键盘I/O输入输出、数码管显示等主要部分组成。各模块的主要功能如下:
(1)矩阵键盘将十六进制编码的数字送到单片机。
(2) 单片机扫描键盘信号并接收,对输入的键盘信号进行处理
(3) LED 以动态扫描的方式移位显示每次输入的数据和最后的运算结果。实践设计的具体流程图如下图2所示:
图3 整体流程图
4、计算器详细设计过程
(处理器型号型号:Intel MCS-51系列8051AH )
计算器设计原理图
4.1输入模块
计算器输入数字和其他功能按键要用到很多按键,如果采用独立按键的方
式,在这种情况下,编程会很简单,但是会占用大量的I/O 口资源,因此在很多情况下都不采用这种方式。为此,我们引入了矩阵键盘的应用,采用四条I/O 线作为行线,四条I/O 线作为列线组成键盘。在行线和列线的每个交叉点上设置一个按键。这样键盘上按键的个数就为4×4个。这种行列式键盘结构能有效地提高单片机系统中I/O 口的利用率,如图3所示:
图3 键盘按键
4.2键盘输入电路
每个按键都有它的行值和列值,行值和列值的组合就是识别这个按键的编码。矩阵的行线和列线分别通过两并行接口和CPU 通信。键盘的一端(列线)通过电阻接VCC ,而接地是通过程序输出数字“0”实现的。键盘处理程序的任务是:确定有无键按下,判断哪一个键按下,键的功能是什么?还要消除按键在闭合或断开时的抖动。两个并行口中,一个输出扫描码,使按键逐行动态接地;另一个并行口输入按键状态,由行扫描值和回馈信号共同形成键编码而识别按键,通过软件查表,查出该键的功能。
当无按键闭合时,P1.0-P1.3 与P1.4-P1.7 之间开路;当有键闭合时,与闭合键相连的两条I/O 口线之间短路。判断有无按键按下的方法是:第一步,置行线P1.4-P1.7 为输入状态,从列线P1.0-P1.3 输出高电平,读入列线数据,若某一列线为高电平,则该列线上有键闭合。第二步,读入列线值,将该值输出到列线值,之后再读出列线值。综合一二两步的结果,最后键盘最后组合码值可确定按键编号。但是键闭合一次只能进行一次键功能操作,因此须等到按键释放后,再进行键功能操作,否则按一次键,有可能会连续多次进行同样的键操作。
4.3主程序模块
这次设计共涉及到了处理器、矩阵键盘、独立键盘、数码管显示等主要的模块,在软件主程序中,通过函数的调用实现相应的功能模块处理。通过
num=KeyPro()函数的调用实现了将通过4*4矩阵行列反转扫描法得到的数据送入单片机,用于处理器进一步处理数据。因为该课程实践要求计数器具有2位小数的加减乘除运算,所以我们在读取矩阵键盘之后,使用语句
sscanf(temp,"%f",&a),该语句的意思是将字符串数组temp 中的字符以浮点数的形式输出到变量a ,因此键盘输的的数据都被转换成了浮点数,所以可以很方便的实现带小数的加减乘除运算,运算所得结果也是浮点数的形式。在对输出结果输出到数码管的过程中,使用语句sprintf(temp,"%f",a);,该语句的意思是把浮点数变量a 输出到字符串数组temp 中,此时就可以调用显示函数对字符形式的计算结果进行输出。
5、分析与调试
在程序设计方法上,模块化程序设计是单片机应用中最常用的程序设计方法。设计的中心思想是把一个复杂应用程序按整体功能划分成若干相对独立的程序模块,各模块可以单独设计、编程和调试,然后组合起来。这种方法便于设计和调试,容易实现多个程序共存,但各个模块之间的连接有一定的难度。根据需要我们可以采用自上而下的程序设计方法,此方法先从主程序开始设计,然后再编制各从属程序和子程序,层层细化逐步求精,最终完成一个复杂程序的设计。这种方法比较符合人们的日常思维,缺点是一级的程序错误会对整个程序产生影响。
本次课程设计的计算器主要完成了矩阵键盘的数据或符号输入、数码管动态显示输入值、单片机处理输入的数据、数码管动态显示计算结果。在整个的设计电路中,输入输出的数据均采用字符的形式(temp[i]=num+'0'(键盘输入)sprintf(temp,"%f",a)(数码管输出));使用在单片机进行数值运算的过程中,数据使用函数sscanf(temp,"%f",&a),即将数据缓冲区的字符型数据转换成浮点型并存到a 变量所对应的地址,这样之后在对变量进行加减乘除运算。因为是浮点数运算,所以结果也是浮点数,在输出的时候,小数点也能够输出,从而免
去在运算结果中对小数的定位工作。
按照上面的思想,在进行小数运算时,输入输出小数并不能正常显示。后来通过和组员一起仔细研究,发现数码管段码中并没有小数点,所以我们在输入数据时将每一个输入的与小数点的ASCII 码进行比较(if(num=='.')),一旦条件成立,将小数点存入数据缓冲temp[8]数组中,同时也将小数点的ASCII 码存入显示缓冲区TempData[8]中(定义小数点的段码0x98)。这样之后关于小数点显示问解决题。
本次实践所设计的计算器的操作数为浮点型,当两个数参与运算之后,运算结果如果出现小数部分全为0的情况,根据实际应该只显示整数部分,但是实验结果是小数0全显示。但是由于时间关系我们没有调试成功。现提供一个思路:在显示小数点的时候,设置一个for 循环,将小数点后的各位与'0' 进行比较,如果该位等于0,设置一个新变量自加一次,之后依次比较,新变量记录小数点后0的个数。循环结束之后,将变量值与小数点后的总位数进行比较,如果相等说明小数点后全为0. 之后设置一个for 循环将小数点后的数据的ASCII 全部赋值0。部分程序如下:
sprintf(temp,"%f",a); //以字符的形式打印浮点数到临时缓冲区字符数组temp 中 for(s=0;s
{
if(temp[s]==0x2d)//表示负号,数码管显示负号 0x40
TempData[s]=0x40;
else if(temp[s]==0x2e)//表示小数点,数码管显示小数点 0x98 {
for(j=s+1;j
{
if(temp[j]==48)
k++;
}
if(k==7-s)
{
for(;s
TempData[s]=0;
break;
}
else
TempData[s]=0x98;
}
else
TempData[s]=dofly_DuanMa[temp[s]-'0'];//其他0-9则进行ASCII 到数字处理,如当前是'3' ,用'3'-'0'=3
//'3'的16进制是0x33,'0' 的16进制是0x30
}
7、运行结果
8、结束语
经过近一周的努力,终于顺利完成了单片机课程实践-计数器的设计。刚开始,我们头绪不是很清楚,不知道从哪里入手,但通过老师的耐心指导并和同学认真研究设计课题,跑图书馆查资料、确定基本设计方案、对所用芯片功能进行查找、调试、上机仿真等,经历了一次次的困难,却积累了很多宝贵的经验。在整个设计的过程中遇到的问题主要有以下二点,第一:基础知识掌握的不牢固,主要表现在一些常用的电路的形式和功能不清楚,对书本上的内容理解不够透彻。第二:相关知识掌握的不够全面,缺少系统设计的经验。
这次设计进一步端了我的学习态度,学会了实事求是,严谨的作风,对自己要严格要求,不能够一知半解,要力求明明白白。急于求成是不好的,我有所感受。如果省略了那些必要的步骤,急于求成,不仅会浪费时间,还会适得其反。我觉得动手之前,头脑里必须清楚该怎么做,这一点是很重要的。就目前来说,我的动手能力虽然差一点,但我想,通过我的不懈努力,在这方面,我总会得到提高。
8、参考文献
[1] C语言程序设计教程/张宗杰主编. -北京:电子工业出版社,2013.8
[2] 增强型51单片机与仿真技术/肖金球,冯骥编著. -北京:清华大学出版社,2011.10
[3] http://baike.baidu.com/view/1295144.htm?fr=aladdin
[4]http://baike.baidu.com/link?url=Uo7SyME1Kcu8ZVH6OJzw2m9JdiOVh-toHZbqW6SneiaFwvoWFZfPxWn7Cec29PIiSelKdcg40-svLlkQaDCYf_
9、源程序附录
9.1主程序
/*-----------------------------------------------
内容:整数之间运算,含小数运算,有负号运算,
------------------------------------------------*/
#include //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义 #include
#include
#include"display.h"
#include"delay.h"
#include"keyboard.h"
sbit CF=P2^5;
//unsigned char TempData[8]; 存储显示值的全局变量
/*------------------------------------------------
主程序
------------------------------------------------*/
main()
{
unsigned char num,i=0,sign;
unsigned char temp[8]; //最大输入8个
bit firstflag;
float a=0,b=0;
unsigned char s,j,k=0;
Init_Timer0(); //初始化定时器0
while (1) //主循环
{
if(CF==0)
num=0x5e;
else
num=KeyPro();//扫描键盘
if(num!=0xff) //如果扫描是按键有效值则进行处理
{
if(i==0) //输入是第一个字符的时候需要把该行清空,方便观看
{
for(s=0;s
TempData[s]=0;
}
if((num=='+') || (i==8) || (num=='-') || (num=='x') || (num=='/') || (num=='=') || (num=='^'))//输入数字最大位数为8或输入符号表示输入结束
{
i=0; //计数器复位
if(firstflag==0) //如果是输入的第一个数据,赋值给a ,并把标志位置1,到下一个数据输入时可以跳转赋值给b
出到变量a
temp 中
{ sscanf(temp,"%f",&a); //将字符串数组temp 中的字符以浮点数的形式输 firstflag=1; } else sscanf(temp,"%f",&b); //b中存放temp 字符数组的各自对应的ASCII 码 for(s=0;s
{
for(j=s+1;j
{
if(temp[j]==48)
k++;
}
if(k==7-s)
{
for(;s
TempData[s]=0;
break;
}
else
TempData[s]=0x98;
}
else
TempData[s]=dofly_DuanMa[temp[s]-'0'];//其他0-9则进行ASCII 到数字处理,如当前是'3' ,用'3'-'0'=3
//'3'的16进制是0x33,'0' 的16进制是0x30
}
sign=0;a=b=0; //用完后所有数据清零
for(s=0;s
temp[s]=0;
}
}
else if(i
{
if(num=='.')
{
temp[i]=num;
TempData[i]=0x98;
i++;
}
else
{
temp[i]=num+'0'; //
TempData[i]=dofly_DuanMa[num];//输出数据
i++;
} //输入数值累加
}
}
}
}
9.2延时函数delay
#include "delay.h"
/*------------------------------------------------
uS 延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M ,精确延时请使用汇编, 大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS 延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M ,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
9.3显示函数display
#include"display.h"
#include"delay.h"
#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换
unsigned char code dofly_DuanMa[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};// 显示段码值0~9 unsigned char code dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分别对应相应的数码管点亮, 即位码
unsigned char TempData[8]; //存储显示值的全局变量
/*------------------------------------------------
显示函数,用于动态扫描数码管
输入参数 FirstBit 表示需要显示的第一位,如赋值2表示从第三个数码管开始显示 如输入0表示从第一个显示。
Num 表示需要显示的位数,如需要显示99两位数值则该值输入2
------------------------------------------------*/
void Display(unsigned char FirstBit,unsigned char Num)
{
static unsigned char i=0;
DataPort=0; //清空数据,防止有交替重影
LATCH1=1; //段锁存
LATCH1=0;
DataPort=dofly_WeiMa[i+FirstBit]; //取位码
LATCH2=1; //位锁存
LATCH2=0;
DataPort=TempData[i]; //取显示数据,段码
LATCH1=1; //段锁存
LATCH1=0;
i++;
if(i==Num)
i=0;
}
/*------------------------------------------------
定时器初始化子程序
------------------------------------------------*/
void Init_Timer0(void)
{
TMOD |= 0x01; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响
//TH0=0x00; //给定初值
//TL0=0x00;
EA=1; //总中断打开
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
}
/*------------------------------------------------
定时器中断子程序
------------------------------------------------*/
void Timer0_isr(void) interrupt 1
{
TH0=(65536-2000)/256; //重新赋值 2ms
TL0=(65536-2000)%256;
Display(0,8);
}
9.4键盘扫描函数
/*-----------------------------------------------
内容:矩阵键盘读入
------------------------------------------------*/
#include //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义 #include"keyboard.h"
#include"delay.h"
#define KeyPort P1
/*------------------------------------------------
按键扫描函数,返回扫描键值
------------------------------------------------*/
unsigned char KeyScan(void) //键盘扫描函数,使用行列反转扫描法
{
unsigned char cord_h,cord_l;//行列值中间变量
KeyPort=0x0f; //行线输出全为0
cord_h=KeyPort&0x0f; //读入列线值
if(cord_h!=0x0f) //先检测有无按键按下
{
DelayMs(10); //去抖
if((KeyPort&0x0f)!=0x0f)
{
cord_h=KeyPort&0x0f; //读入列线值
KeyPort=cord_h|0xf0; //输出当前列线值
cord_l=KeyPort&0xf0; //读入行线值
while((KeyPort&0xf0)!=0xf0);//等待松开并输出
return(cord_h+cord_l);//键盘最后组合码值
}
}return(0xff); //返回该值
}
/*------------------------------------------------
按键值处理函数,返回扫键值
可以根据需要改变返回值
| 1 | 2 | 3 | + |
| 4 | 5 | 6 | - |
| 7 | 8 | 9 | * |
| 0 | . | = | / |
------------------------------------------------*/
unsigned char KeyPro(void)
{
switch(KeyScan())
{
case 0x7e:return 1 ;break;//0 按下相应的键显示相对应的码值
case 0x7d:return 2 ;break;//1
case 0x7b:return 3 ;break;//2
case 0x77:return '+';break;//3
case 0xbe:return 4 ;break;//4
case 0xbd:return 5 ;break;//5
case 0xbb:return 6 ;break;//6
case 0xb7:return '-';break;//7
case 0xde:return 7 ;break;//8
case 0xdd:return 8 ;break;//9
case 0xdb:return 9 ;break;//a
case 0xd7:return 'x';break;//b
case 0xee:return 0 ;break;//c
case 0xed:return '.';break;//d
case 0xeb:return '=';break;//e
case 0xe7:return '/';break;//f
default:return 0xff;break;
}
}
9.5预定义函数
#include
#ifndef __DISPLAY_H__
#define __DISPLAY_H__
#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换
sbit LATCH1=P2^2;//定义锁存使能端口 段锁存
sbit LATCH2=P2^3;// 位锁存
extern unsigned char TempData[8]; //存储显示值的全局变量
extern unsigned char code dofly_DuanMa[10];
/*------------------------------------------------
显示函数,用于动态扫描数码管
输入参数 FirstBit 表示需要显示的第一位,如赋值2表示从第三个数码管开始显示
如输入0表示从第一个显示。
Num 表示需要显示的位数,如需要显示99两位数值则该值输入2
------------------------------------------------*/
void Display(unsigned char FirstBit,unsigned char Num);
/*------------------------------------------------
定时器初始化子程序
------------------------------------------------*/
void Init_Timer0(void);
#endif
/*--------------------------------------------------------------------------
REG52.H
Header file for generic 80C52 and 80C32 microcontroller.
Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc.
All rights reserved.
--------------------------------------------------------------------------*/
#ifndef __REG52_H__ sfr T2CON = 0xC8;
#define __REG52_H__ sfr RCAP2L = 0xCA;
sfr RCAP2H = 0xCB;
/* BYTE Registers */ sfr TL2 = 0xCC;
sfr P0 = 0x80; sfr TH2 = 0xCD;
sfr P1 = 0x90;
sfr P2 = 0xA0;
sfr P3 = 0xB0; /* BIT Registers */
sfr PSW = 0xD0; /* PSW */
sfr ACC = 0xE0; sbit CY = PSW^7;
sfr B = 0xF0; sbit AC = PSW^6;
sfr SP = 0x81; sbit F0 = PSW^5;
sfr DPL = 0x82; sbit RS1 = PSW^4;
sfr DPH = 0x83; sbit RS0 = PSW^3;
sfr PCON = 0x87; sbit OV = PSW^2;
sfr TCON = 0x88; sbit P = PSW^0; //8052 only sfr TMOD = 0x89;
sfr TL0 = 0x8A; /* TCON */
sfr TL1 = 0x8B; sbit TF1 = TCON^7;
sfr TH0 = 0x8C; sbit TR1 = TCON^6;
sfr TH1 = 0x8D; sbit TF0 = TCON^5;
sfr IE = 0xA8; sbit TR0 = TCON^4;
sfr IP = 0xB8; sbit IE1 = TCON^3;
sfr SCON = 0x98; sbit IT1 = TCON^2;
sfr SBUF = 0x99; sbit IE0 = TCON^1;
sbit IT0 = TCON^0;
/* 8052 Extensions */
/* IE */
sbit EA = IE^7; /* SCON */
sbit ET2 = IE^5; //8052 only sbit SM0 = SCON^7; sbit ES = IE^4; sbit SM1 = SCON^6; sbit ET1 = IE^3; sbit SM2 = SCON^5; sbit EX1 = IE^2; sbit REN = SCON^4; sbit ET0 = IE^1; sbit TB8 = SCON^3; sbit EX0 = IE^0; sbit RB8 = SCON^2; sbit TI = SCON^1; /* IP */ sbit RI = SCON^0; sbit PT2 = IP^5;
sbit PS = IP^4; /* P1 */
sbit PT1 = IP^3; sbit T2EX = P1^1; // 8052 only sbit PX1 = IP^2; sbit T2 = P1^0; // 8052 only sbit PT0 = IP^1;
sbit PX0 = IP^0; /* T2CON */
sbit TF2 = T2CON^7; /* P3 */ sbit EXF2 = T2CON^6; sbit RD = P3^7; sbit RCLK = T2CON^5; sbit WR = P3^6; sbit TCLK = T2CON^4; sbit T1 = P3^5; sbit EXEN2 = T2CON^3; sbit T0 = P3^4; sbit TR2 = T2CON^2; sbit INT1 = P3^3; sbit C_T2 = T2CON^1; sbit INT0 = P3^2; sbit CP_RL2 = T2CON^0; sbit TXD = P3^1;
sbit RXD = P3^0; #endif
#ifndef __DELAY_H__
#define __DELAY_H__
/*------------------------------------------------
uS 延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M ,精确延时请使用汇编, 大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t);
/*------------------------------------------------
mS 延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M ,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t);
#endif
/*-----------------------------------------------
名称:矩阵键盘头文件
------------------------------------------------*/
#ifndef __KEYBOARD_H__
#define __KEYBOARD_H__
/*------------------------------------------------
按键扫描函数,返回扫描键值
------------------------------------------------*/
unsigned char KeyScan(void); //键盘扫描函数,使用行列反转扫描法
/*------------------------------------------------
按键值处理函数,返回扫键值
------------------------------------------------*/
unsigned char KeyPro(void);
#endif
/*-------------------------------------------------------------------------- MATH.H
Prototypes for mathematic functions.
Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc. All rights reserved.
--------------------------------------------------------------------------*/
#ifndef __MATH_H__
#define __MATH_H__
#pragma SAVE
#pragma REGPARMS
extern char cabs (char val);
extern int abs (int val);
extern long labs (long val);
extern float fabs (float val);
extern float sqrt (float val);
extern float exp (float val);
extern float log (float val);
extern float log10 (float val);
extern float sin (float val);
extern float cos (float val);
extern float tan (float val);
extern float asin (float val);
extern float acos (float val);
extern float atan (float val);
extern float sinh (float val);
extern float cosh (float val);
extern float tanh (float val);
extern float atan2 (float y, float x);
extern float ceil (float val);
extern float floor (float val);
extern float modf (float val, float *n);
extern float fmod (float x, float y);
extern float pow (float x, float y);
#pragma RESTORE
#endif
/*-------------------------------------------------------------------------- STDIO.H
Prototypes for standard I/O functions.
Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc. All rights reserved.
--------------------------------------------------------------------------*/
#ifndef __STDIO_H__
#define __STDIO_H__
#ifndef EOF
#define EOF -1
#endif
#ifndef NULL
#define NULL ((void *) 0)
#endif
#ifndef _SIZE_T
#define _SIZE_T
typedef unsigned int size_t;
#endif
#pragma SAVE
#pragma REGPARMS
extern char _getkey (void);
extern char getchar (void);
extern char ungetchar (char);
extern char putchar (char);
extern int printf (const char *, ...);
extern int sprintf (char *, const char *, ...);
extern int vprintf (const char *, char *);
extern int vsprintf (char *, const char *, char *);
extern char *gets (char *, int n);
extern int scanf (const char *, ...);
extern int sscanf (char *,const char *, ...);
extern int puts (const char *);
#pragma RESTORE
#endif