当前位置:文档之家› 数码管之简单加减法计算器--项目总结

数码管之简单加减法计算器--项目总结

数码管之简单加减法计算器--项目总结总结人:徐冉1.项目使用到的硬件板载资源1)单片机STC89C52RC作为系统的处理器2)6位共阳极数码管作为计算器的数字显示器件3)LCD1602液晶显示一些提示信息如计算时的符号(正/负)4)4*4矩阵按键作为计算器的数字输入设备5)8个贴片LED小灯,用于走马灯效果作为修饰6)用到了单片机的内部资源定时器T02.项目的基本功能通过程序的烧录,将工程中的hex文件烧录到Kingst-51开发板上。

现象如下:初始时1)最右边的数码管上显示0,其余数码管不显示。

2)五个LED小灯像小火车一样循环跑着。

3)LCD1602液晶的左上角显示Symbol:字样,用于显示符号。

正数不显示,负数时将显示‘-’。

进行简单的加减法运算当用户使用按键输入相应的数字时,会在数码管上显示。

数字值将进行进位累加方式实现。

数字键(k13 k1 ~ k3 k5 ~ k7 k9 ~ k11 0 ~ 9),k4加法功能键,k12减法功能键,k14 ESC清零键,k4和k12是双功能键,即k4既是加法键也是计算键,k12既是减法键也是计算键。

达到一键两用的效果。

注意:用户在进行加法计算时可以随意计算,但在计算减法时第一步需要进行一次加法,然后才能进行减法运算。

当计算结果是负数时会在数码管上显示相应的数值的绝对值,而在LCD1602液晶上显示‘-’。

在计算期间LED 小灯会一直循环流动。

这个计算器可以实现连加连减,计算十分方便,但没有处理小数。

当计算完成时,或输入错误时可按k14进行清零操作。

3.项目的创新点首先该项目使用了LED 的跑马灯效果进行了装饰,为了程序的简单且直观性程序使用了LCD1602进行运算时符号的显示。

程序在计算上使用了一键两用的功能设计,这样设计方便快捷。

4.项目的设计流程NY上电LCD1602液晶初始化74HC138译码器初始化配置定时器T0定时1ms 中断Lcd1602初始化显示Symbol主函数while 循环进行按键动作检测1ms 到按键扫描LED 小灯&数码管扫描结束5.项目的不足之处本项目只考虑了简单的加法和减法运算,且只是整数运算,而未能考虑到小数运算。

乘除法亦没有考虑到。

在进行减法运算时都必须进行一次加法运算才可以运算正常。

6.项目需要改进的地方可对本项目进行全面的扩展,使其既能进行加减乘除运算,又能处理小数和负数的功能。

符号显示直接使用数码管显示即可。

将计算功能键单独进行处理,做成真正的计算器。

7.项目的硬件电路连接图8.项目的源代码/*********************************************************************************** @file mytype.h* @author qlp* @date 2014年6月18日* @version V1.2.3* @brief 自定义类型头文件******************************************************************************** */#ifndef _MYTYPE_H_H#define _MYTYPE_H_Htypedef unsigned char uint8;typedef unsigned int uint16;typedef unsigned long uint32;#endif //_MYTYPE_H_H/************************************************************************** @file Lcd1602.c* @author qlp* @date 2014年6月18日* @version V1.2.3* @brief LCD1602液晶底层驱动************************************************************************/#include <reg52.h>//LCD1602_IOsbit LCD1602_RS = P1^0;sbit LCD1602_RW = P1^1;sbit LCD1602_EN = P1^5;//74HC138sbit ADDR0 = P1^0;sbit ADDR1 = P1^1;sbit ADDR2 = P1^2;sbit ADDR3 = P1^3;sbit ENLED = P1^4;bit tmpADDR0 = 0;bit tmpADDR1 = 0;//地址选择缓冲区#define LCD1602_DB P0/*暂停LED扫描*/void LEDRefreshPause(){ENLED = 1;//关闭LED使能tmpADDR0 = ADDR0;//因为LED和LCD同时使用了P1^0和P1^1引脚,所以要暂时保存ADDR0和ADDR1的数据即LED扫描地址值tmpADDR1 = ADDR1;P0 = 0xFF; //数码管+LED小灯去抖动}/*继续扫描LED*/void ContinueRefreshLED(){ADDR0 = tmpADDR0;ADDR1 = tmpADDR1;//恢复原来LED扫描的地址选择值ENLED = 0;//选择LEDP0 = 0xFF; //数码管和LED去抖}/* 液晶忙碌等待 */void LCD1602Wait(){unsigned char sta;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忙碌}/* 液晶写命令 */void LCD1602WriteCmd(unsigned char cmd){LEDRefreshPause(); //暂停LED数码管刷新LCD1602Wait();LCD1602_RS = 0;LCD1602_RW = 0;LCD1602_EN = 0;LCD1602_DB = cmd;LCD1602_EN = 1;LCD1602_EN = 0;ContinueRefreshLED();//继续LED数码管刷新}/* 液晶写数据 */void LCD1602WriteData(unsigned char dat){LEDRefreshPause(); //暂停LED数码管刷新LCD1602Wait();LCD1602_RS = 1;LCD1602_RW = 0;LCD1602_EN = 0;LCD1602_DB = dat;LCD1602_EN = 1;LCD1602_EN = 0;ContinueRefreshLED();//继续LED数码管刷新}/* 液晶初始化 */void InitalLCD1602(){LCD1602WriteCmd(0x38);LCD1602WriteCmd(0x0C);LCD1602WriteCmd(0x06);LCD1602WriteCmd(0x01);//清屏}/* 写数据到液晶上,字符串str,坐标(x, y),地址addr */void LcdShowStr(unsigned char x, unsigned char y, unsigned char * str){unsigned char addr;if (y == 0){addr = 0x00 + x;}else{addr = 0x40 + x;}LCD1602WriteCmd(addr | 0x80);while (*str != '\0'){LCD1602WriteData(*str++);}}/********************************************************************************** * @file main.c* @author qlp* @date 2014年6月18日* @version V1.2.3* @brief 简易加减法可实现连加连减* @note 单片机STC89C52RC MCU 晶振 11.0592MHZ******************************************************************************** */#include <reg52.h>#include <intrins.h>#include "mytype.h"//74HC138sbit ADDR0 = P1^0;sbit ADDR1 = P1^1;sbit ADDR2 = P1^2;sbit ADDR3 = P1^3;sbit ENLED = P1^4;//时钟晶振和系统时钟#define XTAL 11059200UL //11.0592MHZ#define SYS_XTAL XTAL/12 //系统时钟,为晶振的12分频//按键输入输出sbit KEY_IN_1 = P2^4;sbit KEY_IN_2 = P2^5;sbit KEY_IN_3 = P2^6;sbit KEY_IN_4 = P2^7;sbit KEY_OUT_1 = P2^3;sbit KEY_OUT_2 = P2^2;sbit KEY_OUT_3 = P2^1;sbit KEY_OUT_4 = P2^0;bit flag = 0;//0 : add 1 : sub//按键状态枚举typedef enum {KEY_DOWN = 0, KEY_UP = 1} ekey;//数据输入口#define DISP_DB P0static volatile uint8 keySta[4][4] = {{1, 1, 1, 1},{1, 1, 1, 1},{1, 1, 1, 1},{1, 1, 1, 1}}; //按键当前值/*矩阵按键到PC标准键盘键码映射表*/uint8 code keyCodeMap[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} //数字键0 ESC键回车键向右键Right };uint8 code LedTable[] = {0xC0, //"0"0xF9, //"1"0xA4, //"2"0xB0, //"3"0x99, //"4"0x92, //"5"0x82, //"6"0xF8, //"7"0x80, //"8"0x90, //"9"0xBF //"-"};/* 定义跑马灯数组 */unsigned char code LedTable2[] = {0xE0, // 111000000xC1, // 110000010x83, // 100000110x07, // 000001110x0E, // 000011100x1C, // 000111000x38, // 001110000x70 // 11100000};uint8 LedBuff[7] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};//数码管显示缓冲区uint8 thr0, tlr0;//定义用于加法计算的变量signed long result = 0;//结果signed long addNum = 0;//加数void KeyHandle();void KeyAction(uint8 keycode);void KeyScan();void showNum(uint32 num);void ConfigHC138();void ConfigTimer0(uint16 xms);extern void InitalLCD1602();extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char * str);void main(){InitalLCD1602();ConfigHC138();ConfigTimer0(1);LcdShowStr(0, 0, "Symbol: ");//符号while (1){KeyHandle();}}/*** @brief : Led小灯扫描* @param : 无* @retval : 无*/void LED_Scan( void ){static unsigned char j = 0;P0 = 0xFF; /* 消隐 */LedBuff[6] = LedTable2[j++]; /* 送入要求的数据到LED显示数据口 */ j &= 0x07; /* 到8归零 */}/*** @brief 定时器T0配置* @param xms* @retval 无*/void ConfigHC138(){ADDR3 = 1;ENLED = 0;LedBuff[0] = LedTable[0];}/*** @brief 定时器T0配置* @param xms* @retval 无*/void ConfigTimer0(uint16 xms){uint16 tmp;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 无*/void KeyHandle(){static uint8 backup[4][4] = {{1, 1, 1, 1},{1, 1, 1, 1},{1, 1, 1, 1},{1, 1, 1, 1}}; //按键备份值uint8 i = 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 按下按键的键码 uint8 keycode* @retval 无*/void KeyAction(uint8 keycode){if (result >= 0){LcdShowStr(9, 0, " ");//清除}else{}if ((keycode >= 0x01) && (keycode <= 0x09)) //keycode 键码是0-9的按键{addNum = (addNum * 10) + (keycode - 0x00);//将原来的加数扩大十倍后,在加上用户按下的数字,即为用户输入的加数showNum(addNum);//数据分解}else if (keycode == 0x0A) //向上键 +{//将上一个加数+到result中result += addNum;//可以实现连加addNum = 0;showNum(result);//分解并显示结果}else if (keycode == 0x0C) //向下键 - {result -= addNum;//连减addNum = 0;if (result >= 0){showNum(result);}else{result = -result;LcdShowStr(9, 0, "-");showNum(result);result = 0;}}else if (keycode == 0x0E) //ESC键{result = 0;addNum = 0;//清零showNum(addNum);LcdShowStr(9, 0, " ");//清除}else if (keycode == 0x0F) //回车键{addNum = 0;showNum(result);}}/*** @brief 数据分解,只显示有效位* @param 待分解的数据 uint32 num* @retval 无*/void showNum(uint32 num){uint8 buff[6];//中间缓冲区signed char i = 0;for (i = 0; i < 6; i++){buff[i] = num % 10;//取最低位数字num /= 10;//num缩小10倍}//去掉无效位,不显示0for (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 无*/void KeyScan(){static uint8 keybuff[4][4] = {{0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF}}; //按键检测值缓冲区static uint8 keyout = 0;//行索引uint8 i = 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;}else if ((keybuff[keyout][i] & 0x1F) == 0x00) //五次连续检测都是0{keySta[keyout][i] = KEY_DOWN;}}keyout++;//行++if (keyout >= 4)keyout = 0;switch (keyout){case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;default : break; //只有一行被选中}}/*** @brief LED数码管刷新* @param 无* @retval 无*/void refresh(){static uint8 j = 0;DISP_DB = 0xFF;//消隐switch (j){case 0: ADDR2 = 0; ADDR1 = 0; ADDR0 = 0; break;case 1: ADDR2 = 0; ADDR1 = 0; ADDR0 = 1; break;case 2: ADDR2 = 0; ADDR1 = 1; ADDR0 = 0; break;case 3: ADDR2 = 0; ADDR1 = 1; ADDR0 = 1; break;case 4: ADDR2 = 1; ADDR1 = 0; ADDR0 = 0; break;case 5: ADDR2 = 1; ADDR1 = 0; ADDR0 = 1; break; case 6: ADDR2 = 1; ADDR1 = 1; ADDR0 = 0; break;default: break;}DISP_DB = LedBuff[j++];if (j >= 7){j = 0;}}/*** @brief 定时器T0中断服务* @param 无* @retval 无*/void timer0_ISP() interrupt 1{static unsigned char counter = 0;TH0 = thr0;TL0 = tlr0;counter++;if (counter >= 200){counter = 0;LED_Scan();}KeyScan();//按键检测refresh();//数码管刷新}于2014年6月25日23:40:29。

相关主题