数码管之简单加减法计算器项目总结.docx
《数码管之简单加减法计算器项目总结.docx》由会员分享,可在线阅读,更多相关《数码管之简单加减法计算器项目总结.docx(25页珍藏版)》请在冰点文库上搜索。
数码管之简单加减法计算器项目总结
数码管之简单加减法计算器--项目总结
总结人:
徐冉
1.项目使用到的硬件板载资源
1)单片机STC89C52RC作为系统的处理器
2)6位共阳极数码管作为计算器的数字显示器件
3)LCD1602液晶显示一些提示信息如计算时的符号(正/负)
4)4*4矩阵按键作为计算器的数字输入设备
5)8个贴片LED小灯,用于走马灯效果作为修饰
6)用到了单片机的内部资源定时器T0
2.项目的基本功能
通过程序的烧录,将工程中的hex文件烧录到Kingst-51开发板上。
现象如下:
初始时
1)最右边的数码管上显示0,其余数码管不显示。
2)五个LED小灯像小火车一样循环跑着。
3)LCD1602液晶的左上角显示Symbol:
字样,用于显示符号。
正数不显示,负数时将显示‘-’。
进行简单的加减法运算
当用户使用按键输入相应的数字时,会在数码管上显示。
数字值将进行进位累加方式实现。
数字键(k13k1~k3k5~k7k9~k110~9),k4加法功能键,k12减法功能键,k14ESC清零键,k4和k12是双功能键,即k4既是加法键也是计算键,k12既是减法键也是计算键。
达到一键两用的效果。
注意:
用户在进行加法计算时可以随意计算,但在计算减法时第一步需要进行一次加法,然后才能进行减法运算。
当计算结果是负数时会在数码管上显示相应的数值的绝对值,而在LCD1602液晶上显示‘-’。
在计算期间LED小灯会一直循环流动。
这个计算器可以实现连加连减,计算十分方便,但没有处理小数。
当计算完成时,或输入错误时可按k14进行清零操作。
3.项目的创新点
首先该项目使用了LED的跑马灯效果进行了装饰,为了程序的简单且直观性程序使用了LCD1602进行运算时符号的显示。
程序在计算上使用了一键两用的功能设计,这样设计方便快捷。
4.项目的设计流程
N
Y
5.项目的不足之处
本项目只考虑了简单的加法和减法运算,且只是整数运算,而未能考虑到小数运算。
乘除法亦没有考虑到。
在进行减法运算时都必须进行一次加法运算才可以运算正常。
6.项目需要改进的地方
可对本项目进行全面的扩展,使其既能进行加减乘除运算,又能处理小数和负数的功能。
符号显示直接使用数码管显示即可。
将计算功能键单独进行处理,做成真正的计算器。
7.项目的硬件电路连接图
8.项目的源代码
/**
********************************************************************************
*@filemytype.h
*@authorqlp
*@date2014年6月18日
*@versionV1.2.3
*@brief自定义类型头文件
********************************************************************************
*/
#ifndef_MYTYPE_H_H
#define_MYTYPE_H_H
typedefunsignedcharuint8;
typedefunsignedintuint16;
typedefunsignedlonguint32;
#endif//_MYTYPE_H_H
/**
***********************************************************************
*@fileLcd1602.c
*@authorqlp
*@date2014年6月18日
*@versionV1.2.3
*@briefLCD1602液晶底层驱动
***********************************************************************
*/
#include
//LCD1602_IO
sbitLCD1602_RS=P1^0;
sbitLCD1602_RW=P1^1;
sbitLCD1602_EN=P1^5;
//74HC138
sbitADDR0=P1^0;
sbitADDR1=P1^1;
sbitADDR2=P1^2;
sbitADDR3=P1^3;
sbitENLED=P1^4;
bittmpADDR0=0;
bittmpADDR1=0;//地址选择缓冲区
#defineLCD1602_DBP0
/*暂停LED扫描*/
voidLEDRefreshPause()
{
ENLED=1;//关闭LED使能
tmpADDR0=ADDR0;//因为LED和LCD同时使用了P1^0和P1^1引脚,所以要暂时保存ADDR0和ADDR1的数据即LED扫描地址值
tmpADDR1=ADDR1;
P0=0xFF;//数码管+LED小灯去抖动
}
/*继续扫描LED*/
voidContinueRefreshLED()
{
ADDR0=tmpADDR0;
ADDR1=tmpADDR1;//恢复原来LED扫描的地址选择值
ENLED=0;//选择LED
P0=0xFF;//数码管和LED去抖
}
/*液晶忙碌等待*/
voidLCD1602Wait()
{
unsignedcharsta;
LCD1602_DB=0xFF;//总线拉高,检测液晶状态字
LCD1602_RS=0;
LCD1602_RW=1;
do
{
LCD1602_EN=1;
sta=LCD1602_DB;
LCD1602_EN=0;//避免液晶输出数据
}while(sta&0x80);//状态字最高位STA7==0空闲,1忙碌
}
/*液晶写命令*/
voidLCD1602WriteCmd(unsignedcharcmd)
{
LEDRefreshPause();//暂停LED数码管刷新
LCD1602Wait();
LCD1602_RS=0;
LCD1602_RW=0;
LCD1602_EN=0;
LCD1602_DB=cmd;
LCD1602_EN=1;
LCD1602_EN=0;
ContinueRefreshLED();//继续LED数码管刷新
}
/*液晶写数据*/
voidLCD1602WriteData(unsignedchardat)
{
LEDRefreshPause();//暂停LED数码管刷新
LCD1602Wait();
LCD1602_RS=1;
LCD1602_RW=0;
LCD1602_EN=0;
LCD1602_DB=dat;
LCD1602_EN=1;
LCD1602_EN=0;
ContinueRefreshLED();//继续LED数码管刷新
}
/*液晶初始化*/
voidInitalLCD1602()
{
LCD1602WriteCmd(0x38);
LCD1602WriteCmd(0x0C);
LCD1602WriteCmd(0x06);
LCD1602WriteCmd(0x01);//清屏
}
/*写数据到液晶上,字符串str,坐标(x,y),地址addr*/
voidLcdShowStr(unsignedcharx,unsignedchary,unsignedchar*str)
{
unsignedcharaddr;
if(y==0)
{
addr=0x00+x;
}
else
{
addr=0x40+x;
}
LCD1602WriteCmd(addr|0x80);
while(*str!
='\0')
{
LCD1602WriteData(*str++);
}
}
/**
********************************************************************************
*@filemain.c
*@authorqlp
*@date2014年6月18日
*@versionV1.2.3
*@brief简易加减法可实现连加连减
*@note单片机STC89C52RCMCU晶振11.0592MHZ
********************************************************************************
*/
#include
#include
#include"mytype.h"
//74HC138
sbitADDR0=P1^0;
sbitADDR1=P1^1;
sbitADDR2=P1^2;
sbitADDR3=P1^3;
sbitENLED=P1^4;
//时钟晶振和系统时钟
#defineXTAL11059200UL//11.0592MHZ
#defineSYS_XTALXTAL/12//系统时钟,为晶振的12分频
//按键输入输出
sbitKEY_IN_1=P2^4;
sbitKEY_IN_2=P2^5;
sbitKEY_IN_3=P2^6;
sbitKEY_IN_4=P2^7;
sbitKEY_OUT_1=P2^3;
sbitKEY_OUT_2=P2^2;
sbitKEY_OUT_3=P2^1;
sbitKEY_OUT_4=P2^0;
bitflag=0;//0:
add1:
sub
//按键状态枚举
typedefenum{
KEY_DOWN=0,KEY_UP=1
}ekey;
//数据输入口
#defineDISP_DBP0
staticvolatileuint8keySta[4][4]={
{1,1,1,1},
{1,1,1,1},
{1,1,1,1},
{1,1,1,1}
};//按键当前值
/*矩阵按键到PC标准键盘键码映射表*/
uint8codekeyCodeMap[4][4]={
{0x01,0x02,0x03,0x0A},//数字键1-3向上键UP
{0x04,0x05,0x06,0x0D},//数字键4-6向左键Left
{0x07,0x08,0x09,0x0C},//数字键7-9向下键DOWN
{0x00,0x0E,0x0F,0x0B}//数字键0ESC键回车键向右键Right
};
uint8codeLedTable[]={
0xC0,//"0"
0xF9,//"1"
0xA4,//"2"
0xB0,//"3"
0x99,//"4"
0x92,//"5"
0x82,//"6"
0xF8,//"7"
0x80,//"8"
0x90,//"9"
0xBF//"-"
};
/*定义跑马灯数组*/
unsignedcharcodeLedTable2[]={
0xE0,//11100000
0xC1,//11000001
0x83,//10000011
0x07,//00000111
0x0E,//00001110
0x1C,//00011100
0x38,//00111000
0x70//11100000
};
uint8LedBuff[7]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};//数码管显示缓冲区
uint8thr0,tlr0;
//定义用于加法计算的变量
signedlongresult=0;//结果
signedlongaddNum=0;//加数
voidKeyHandle();
voidKeyAction(uint8keycode);
voidKeyScan();
voidshowNum(uint32num);
voidConfigHC138();
voidConfigTimer0(uint16xms);
externvoidInitalLCD1602();
externvoidLcdShowStr(unsignedcharx,unsignedchary,unsignedchar*str);
voidmain()
{
InitalLCD1602();
ConfigHC138();
ConfigTimer0
(1);
LcdShowStr(0,0,"Symbol:
");//符号
while
(1){
KeyHandle();
}
}
/**
*@brief:
Led小灯扫描
*@param:
无
*@retval:
无
*/
voidLED_Scan(void)
{
staticunsignedcharj=0;
P0=0xFF;/*消隐*/
LedBuff[6]=LedTable2[j++];/*送入要求的数据到LED显示数据口*/
j&=0x07;/*到8归零*/
}
/**
*@brief定时器T0配置
*@paramxms
*@retval无
*/
voidConfigHC138()
{
ADDR3=1;
ENLED=0;
LedBuff[0]=LedTable[0];
}
/**
*@brief定时器T0配置
*@paramxms
*@retval无
*/
voidConfigTimer0(uint16xms)
{
uint16tmp;
tmp=65536-xms*SYS_XTAL/1000;
thr0=(uint8)(tmp>>8);
tlr0=(uint8)(tmp&0x00FF);
TMOD&=0xF0;
TMOD|=0x01;
TH0=thr0;
TL0=tlr0;
TR0=1;
EA=1;
ET0=1;
}
/**
*@brief按键驱动函数(根据按下的按键的键码执行相应的动作)
*@param无
*@retval无
*/
voidKeyHandle()
{
staticuint8backup[4][4]={
{1,1,1,1},
{1,1,1,1},
{1,1,1,1},
{1,1,1,1}
};//按键备份值
uint8i=0,j=0;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
{
if(keySta[i][j]!
=backup[i][j])
{
if(backup[i][j]==KEY_DOWN)
{//按键弹起
KeyAction(keyCodeMap[i][j]);//调用按键动作函数,根据按下的按键的键码执行相应的操作
}
backup[i][j]=keySta[i][j];//备份按键值
}
}
}
/**
*@brief按键动作函数(根据按下的按键的键码执行相应的动作)
*@param按下按键的键码uint8keycode
*@retval无
*/
voidKeyAction(uint8keycode)
{
if(result>=0)
{
LcdShowStr(9,0,"");//清除
}
else
{
}
if((keycode>=0x01)&&(keycode<=0x09))//keycode键码是0-9的按键
{
addNum=(addNum*10)+(keycode-0x00);//将原来的加数扩大十倍后,在加上用户按下的数字,即为用户输入的加数
showNum(addNum);//数据分解
}
elseif(keycode==0x0A)//向上键+
{
//将上一个加数+到result中
result+=addNum;//可以实现连加
addNum=0;
showNum(result);//分解并显示结果
}
elseif(keycode==0x0C)//向下键-
{
result-=addNum;//连减
addNum=0;
if(result>=0)
{
showNum(result);
}
else
{
result=-result;
LcdShowStr(9,0,"-");
showNum(result);
result=0;
}
}
elseif(keycode==0x0E)//ESC键
{
result=0;
addNum=0;//清零
showNum(addNum);
LcdShowStr(9,0,"");//清除
}
elseif(keycode==0x0F)//回车键
{
addNum=0;
showNum(result);
}
}
/**
*@brief数据分解,只显示有效位
*@param待分解的数据uint32num
*@retval无
*/
voidshowNum(uint32num)
{
uint8buff[6];//中间缓冲区
signedchari=0;
for(i=0;i<6;i++)
{
buff[i]=num%10;//取最低位数字
num/=10;//num缩小10倍
}
//去掉无效位,不显示0
for(i=5;i>=1;i--)//从最高位开始检测无效位,遇0就赋值0xFF
{
if(buff[i]==0)
{
LedBuff[i]=0xFF;//不显示
}
else
{
break;//遇到第一个有效位就退出
}
}
//将有效位数字存入缓冲区
for(;i>=0;i--)
{
LedBuff[i]=LedTable[buff[i]];
}
}
/**
*@brief按键检测
*@param无
*@retval无
*/
voidKeyScan()
{
staticuint8keybuff[4][4]={
{0xFF,0xFF,0xFF,0xFF},
{0xFF,0xFF,0xFF,0xFF},
{0xFF,0xFF,0xFF,0xFF},
{0xFF,0xFF,0xFF,0xFF}
};//按键检测值缓冲区
staticuint8keyout=0;//行索引
uint8i=0;
keybuff[keyout][0]=(keybuff[keyout][0]<<1)|KEY_IN_1;//检测第keyout行的第0个按键的值
keybuff[keyout][1]=(keybuff[keyout][1]<<1)|KEY_IN_2;
keybuff[keyout][2]=(keybuff[keyout][2]<<1)|KEY_IN_3;
keybuff[keyout][3]=(keybuff[keyout][3]<<1)|KEY_IN_4;
//更新消抖后的按键值
for(i=0;i<4;i++)
{
if((keybuff[keyout][i]&0x1F)==0x1F)//五次连续检测都是1
{
keySta[keyout][i]=KEY_UP;
}
elseif((keybuff[keyout][i]&0x1F)==0x00)//五次连续检测都是0
{
keySta[keyout][i]=KEY_DOWN;
}
}
keyout++;//行++
if(keyout>=4)
keyout=0;
switch(keyout)
{
case0:
KEY_OUT_4=1;KEY_OUT_1=0;break;
case1:
KEY_OUT_1=1;KEY_OUT_2=0;break;
case2:
KEY_OUT_2=1;KEY_OUT_3=0;break;
case3:
KEY_OUT_3=1;KEY_OUT_4=0;break;
default:
break;//只有一行被选中
}
}
/**
*@briefLED数码管刷新
*@param无
*@retval无
*/
voidrefresh()
{
staticuint8j=0;
DISP_DB=0xFF;//消隐
switch(j)
{
case0:
ADDR2=0;ADDR1=0;ADDR0=0;break;
case1:
ADDR2=0;ADDR1=0;ADDR0=1;break;
case2:
ADDR2=0;ADDR1=1;ADDR0=0;break;
case3:
ADDR2=0;AD