按键和液晶,可以组成最简易的计算器。下面我们来写一个简易整数计算器。为了让程序不过于复杂,我们这个计算器不考虑连加,连减等连续计算,不考虑小数情况。加减乘除分别用上下左右来替代,回车表示等于,ESC 表示归 0。程序共分为三部分,一部分是 1602 液晶显示,一部分是按键动作和扫描,一部分是主函数功能。

/Lcd1602.c 文件程序源代码/

#include <reg52.h>
#define LCD1602_DB P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E = P1^5;
/* 等待液晶准备好 */
void LcdWaitReady()
{unsigned char sta;LCD1602_DB = 0xFF;LCD1602_RS = 0;LCD1602_RW = 1;do {LCD1602_E = 1;sta = LCD1602_DB; //读取状态字LCD1602_E = 0;} while (sta & 0x80); //bit7 等于 1 表示液晶正忙,重复检测直到其等于 0 为止
}
/* 向 LCD1602 液晶写入一字节命令,cmd-待写入命令值 */
void LcdWriteCmd(unsigned char cmd)
{LcdWaitReady();LCD1602_RS = 0;LCD1602_RW = 0;LCD1602_DB = cmd;LCD1602_E = 1;LCD1602_E = 0;
}
/* 向 LCD1602 液晶写入一字节数据,dat-待写入数据值 */
void LcdWriteDat(unsigned char dat)
{LcdWaitReady();LCD1602_RS = 1;LCD1602_RW = 0;LCD1602_DB = dat;LCD1602_E = 1;LCD1602_E = 0;
}
/* 设置显示 RAM 起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */
void LcdSetCursor(unsigned char x, unsigned char y)
{unsigned char addr;if (y == 0) //由输入的屏幕坐标计算显示 RAM 的地址addr = 0x00 + x; //第一行字符地址从 0x00 起始elseaddr = 0x40 + x; //第二行字符地址从 0x40 起始LcdWriteCmd(addr | 0x80); //设置 RAM 地址
}
/* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
{LcdSetCursor(x, y); //设置起始地址while (*str != '\0') //连续写入字符串数据,直到检测到结束符{LcdWriteDat(*str++);} }/* 区域清除,清除从(x,y)坐标起始的 len 个字符位 */
void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len)
{LcdSetCursor(x, y); //设置起始地址while (len--) //连续写入空格{LcdWriteDat(' ');} }
/* 整屏清除 */
void LcdFullClear()
{LcdWriteCmd(0x01);
}/* 初始化 1602 液晶 */
void InitLcd1602()
{LcdWriteCmd(0x38); //16*2 显示,5*7 点阵,8 位数据接口LcdWriteCmd(0x0C); //显示器开,光标关闭LcdWriteCmd(0x06); //文字不动,地址自动+1LcdWriteCmd(0x01); //清屏
}

/keyboard.c 文件程序源代码/

#include <reg52.h>
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;
unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表{ '1', '2', '3', 0x26 }, //数字键 1、数字键 2、数字键 3、向上键{ '4', '5', '6', 0x25 }, //数字键 4、数字键 5、数字键 6、向左键{ '7', '8', '9', 0x28 }, //数字键 7、数字键 8、数字键 9、向下键{ '0', 0x1B, 0x0D, 0x27 } //数字键 0、ESC 键、 回车键、 向右键
};
unsigned char pdata KeySta[4][4] = { //全部矩阵按键的当前状态{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
};
extern void KeyAction(unsigned char keycode);
/* 按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用 */
void KeyDriver()
{unsigned char i, j;static unsigned char pdata backup[4][4] = { //按键值备份,保存前一次的值{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}};for (i=0; i<4; i++) //循环检测 4*4 的矩阵按键{for (j=0; j<4; j++){if (backup[i][j] != KeySta[i][j]) //检测按键动作{if (backup[i][j] != 0) //按键按下时执行动作{KeyAction(KeyCodeMap[i][j]); //调用按键动作函数}backup[i][j] = KeySta[i][j]; //刷新前一次的备份值}}} }
/* 按键扫描函数,需在定时中断中调用,推荐调用间隔 1ms */
void KeyScan()
{unsigned char i;static unsigned char keyout = 0; //矩阵按键扫描输出索引static unsigned char keybuf[4][4] = { //矩阵按键扫描缓冲区{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}};//将一行的 4 个按键值移入缓冲区keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;//消抖后更新按键状态for (i=0; i<4; i++) //每行 4 个按键,所以循环 4 次{if ((keybuf[keyout][i] & 0x0F) == 0x00){ //连续 4 次扫描值为 0,即 4*4ms 内都是按下状态时,可认为按键已稳定的按下KeySta[keyout][i] = 0;}else if ((keybuf[keyout][i] & 0x0F) == 0x0F){ //连续 4 次扫描值为 1,即 4*4ms 内都是弹起状态时,可认为按键已稳定的弹起KeySta[keyout][i] = 1;}}//执行下一次的扫描输出keyout++; //输出索引递增keyout &= 0x03; //索引值加到 4 即归零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;} }

/main.c 文件程序源代码/

#include <reg52.h>
unsigned char step = 0; //操作步骤
unsigned char oprt = 0; //运算类型
signed long num1 = 0; //操作数 1
signed long num2 = 0; //操作数 2
signed long result = 0; //运算结果
unsigned char T0RH = 0; //T0 重载值的高字节
unsigned char T0RL = 0; //T0 重载值的低字节
void ConfigTimer0(unsigned int ms);
extern void KeyScan();
extern void KeyDriver();
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
extern void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len);
extern void LcdFullClear();
void main()
{EA = 1; //开总中断ConfigTimer0(1); //配置 T0 定时 1ms
InitLcd1602(); //初始化液晶LcdShowStr(15, 1, "0"); //初始显示一个数字 0while (1){KeyDriver(); //调用按键驱动} }
/* 长整型数转换为字符串,str-字符串指针,dat-待转换数,返回值-字符串长度 */
unsigned char LongToString(unsigned char *str, signed long dat)
{signed char i = 0;unsigned char len = 0;unsigned char buf[12];if (dat < 0) //如果为负数,首先取绝对值,并在指针上添加负号{dat = -dat;*str++ = '-';len++;}do { //先转换为低位在前的十进制数组buf[i++] = dat % 10;dat /= 10;} while (dat > 0);len += i; //i 最后的值就是有效字符的个数while (i-- > 0) //将数组值转换为 ASCII 码反向拷贝到接收指针上{*str++ = buf[i] + '0';}*str = '\0'; //添加字符串结束符return len; //返回字符串长度
}
/* 显示运算符,显示位置 y,运算符类型 type */
void ShowOprt(unsigned char y, unsigned char type)
{switch (type){case 0: LcdShowStr(0, y, "+"); break; //0 代表+case 1: LcdShowStr(0, y, "-"); break; //1 代表-case 2: LcdShowStr(0, y, "*"); break; //2 代表*case 3: LcdShowStr(0, y, "/"); break; //3 代表/default: break;} }
/* 计算器复位,清零变量值,清除屏幕显示 */
void Reset()
{num1 = 0;num2 = 0;step = 0;LcdFullClear();
}
/* 数字键动作函数,n-按键输入的数值 */
void NumKeyAction(unsigned char n)
{unsigned char len;unsigned char str[12];if (step > 1) //如计算已完成,则重新开始新的计算{Reset();}if (step == 0) //输入第一操作数{num1 = num1*10 + n; //输入数值累加到原操作数上len = LongToString(str, num1); //新数值转换为字符串LcdShowStr(16-len, 1, str); //显示到液晶第二行上}else //输入第二操作数{num2 = num2*10 + n; //输入数值累加到原操作数上len = LongToString(str, num2); //新数值转换为字符串LcdShowStr(16-len, 1, str); //显示到液晶第二行上} }
/* 运算符按键动作函数,运算符类型 type */
void OprtKeyAction(unsigned char type)
{unsigned char len;unsigned char str[12];if (step == 0) //第二操作数尚未输入时响应,即不支持连续操作{len = LongToString(str, num1); //第一操作数转换为字符串LcdAreaClear(0, 0, 16-len); //清除第一行左边的字符位LcdShowStr(16-len, 0, str); //字符串靠右显示在第一行ShowOprt(1, type); //在第二行显示操作符LcdAreaClear(1, 1, 14); //清除第二行中间的字符位LcdShowStr(15, 1, "0"); //在第二行最右端显示 0oprt = type; //记录操作类型step = 1;}
}
/* 计算结果函数 */
void GetResult()
{unsigned char len;unsigned char str[12];if (step == 1) //第二操作数已输入时才执行计算{step = 2;switch (oprt) //根据运算符类型计算结果,未考虑溢出问题{case 0: result = num1 + num2; break;case 1: result = num1 - num2; break;case 2: result = num1 * num2; break;case 3: result = num1 / num2; break;default: break;}len = LongToString(str, num2); //原第二操作数和运算符显示到第一行ShowOprt(0, oprt);LcdAreaClear(1, 0, 16-1-len);LcdShowStr(16-len, 0, str);len = LongToString(str, result); //计算结果和等号显示在第二行LcdShowStr(0, 1, "=");LcdAreaClear(1, 1, 16-1-len);LcdShowStr(16-len, 1, str);} }
/* 按键动作函数,根据键码执行相应的操作,keycode-按键键码 */
void KeyAction(unsigned char keycode)
{if ((keycode>='0') && (keycode<='9')) //输入字符{NumKeyAction(keycode - '0');}else if (keycode == 0x26) //向上键,+{OprtKeyAction(0);}else if (keycode == 0x28) //向下键,-{OprtKeyAction(1);}else if (keycode == 0x25) //向左键,{OprtKeyAction(2);}else if (keycode == 0x27) //向右键,÷{OprtKeyAction(3);}else if (keycode == 0x0D) //回车键,计算结果{GetResult();}else if (keycode == 0x1B) //Esc 键,清除{Reset();LcdShowStr(15, 1, "0");} }
/* 配置并启动 T0,ms-T0 定时时间 */
void ConfigTimer0(unsigned int ms)
{unsigned long tmp; //临时变量tmp = 11059200 / 12; //定时器计数频率tmp = (tmp * ms) / 1000; //计算所需的计数值tmp = 65536 - tmp; //计算定时器重载值tmp = tmp + 28; //补偿中断响应延时造成的误差T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节T0RL = (unsigned char)tmp;TMOD &= 0xF0; //清零 T0 的控制位TMOD |= 0x01; //配置 T0 为模式 1TH0 = T0RH; //加载 T0 重载值TL0 = T0RL;ET0 = 1; //使能 T0 中断TR0 = 1; //启动 T0
}
/* T0 中断服务函数,执行按键扫描 */
void InterruptTimer0() interrupt 1
{TH0 = T0RH; //重新加载重载值TL0 = T0RL;KeyScan(); //按键扫描
}

51 计算器实例 按键和液晶,组成最简易的计算器相关推荐

  1. 计算器php脚本之家,PHP实现简易图形计算器

    本文实例为大家分享了PHP实现简易图形计算器的具体代码,供大家参考,具体内容如下 主函数:index.php 图形计算器 * { margin: 0px; padding: 0px; } #conta ...

  2. 基于51单片机的简易电子计算器——有温度显示和万年历等额外功能

    基于51单片机的简易电子计算器--有温度显示和万年历等额外功能 设计实际所实现的功能 模式一可显示日期.时钟.温度,模式二可进行计算器的计算,且显示日期温度模式和计算器模式可随意切换,模式三可以滚动显 ...

  3. 基于51单片机的简易数字计算器Proteus仿真(源码+仿真+全套资料)

     资料编号:115 全套资料齐全,功能说明: 该计算器系统51 系列的单片机进行的数字计算器系统设计,可以完成计算器的键盘输入, 进行加. 减.乘.除的简单四则运算,并在 LCD屏幕上相应的显示结果. ...

  4. Java实例---计算器实例

    1.计算器上的键的显示名字 1.0 继承JFrame类 1 public class Calculate extends JFrame { 2 3 } View Code 1.1定义常量 1 /** ...

  5. 51单片机开发 矩阵键盘中断实验与简易计算器及proteus仿真

    51单片机开发 矩阵键盘中断实验与简易计算器及proteus仿真 文章目录 51单片机开发 矩阵键盘中断实验与简易计算器及proteus仿真 实验内容 一.实验一 矩阵键盘中断实验 数码管显示 1.仿 ...

  6. python tkinter计算器实例_Python+tkinter使用80行代码实现一个计算器实例

    Python+tkinter使用80行代码实现一个计算器实例 本文主要探索的是使用Python+tkinter编程实现一个简单的计算器代码示例,具体如下. 闲话不说,直奔主题.建议大家跟着敲一遍代码, ...

  7. 基于PHP以及JQUERY AJAX的计算器实例

    此处提供的是作者自己无事的时候写的一个基于JQUERY与PHP之间通过AJAX进行数据交互的简易计算器实例,不做过多的说明,此处直接提供源码的下载,欢迎大家下载参考并写出更智能的计算器. 文件:简易计 ...

  8. 微软计算器按钮按键AC CE MC MR MS M+中英文对照 使用方法

    计算器按钮按键AC CE MC MR MS M+中英文对照 使用方法 AC,All Clear 全部清除,清除除Memory外的全部数据 CE,Clear Enter.清除输入,仅仅清除最后输入值,例 ...

  9. 51单片机——矩阵按键逐行扫描短按长按一直按方案1.2

    1.删减了<51单片机--独立按键.矩阵按键多种方案1.1>里的一些不怎么用的代码. 2.添加了逐行扫描按键(需要定时器20毫秒配合使用). 3.支持按键短按.长按.一直按,代码里只做了短 ...

最新文章

  1. react 合并数组_React 数组合并去重,实现请求三遍返回三组数据的合并去重。
  2. P1108 低价购买
  3. xampp访问php显示空白,运行PHP项目显示空白
  4. 从String中移除空白字符的多种方式!?差别竟然这么大!
  5. Atom 相关配置备份
  6. MySQL常用语句一、连接MySQL格式:mysql-h主机地址-u用户名-p用户密
  7. 【线性查询之间存在相关性误差】差分隐私系统学习记录(七)
  8. 弹性地基梁板的计算理论_造价人常用小帮手:30个实用小软件+44套计算表,绝对实用...
  9. Head First设计模式读书笔记十 第十一章 代理模式
  10. (转)Hadoop入门进阶课程
  11. Android Studio NDK报错:mips64el-linux-android-strip 找不到
  12. python免费 爬取原创力文档并转换成pdf(部分版)
  13. java安装包_Java6 Update
  14. ORID方法在敏捷中的利用
  15. jenkins使用python plugin编写代码构建时报错:Caused: java.io.IOException: Cannot run program “python“
  16. php校园学校宿舍管理系统 php毕业设计题目课题选题 php毕业设计项目作品源码(3)班主任和宿舍管理员功能
  17. 感恩节,《2012》,尖叫
  18. 【特别优惠征稿】2022年第二届人工智能与云计算国际会议
  19. 【云驻共创】华为云之锁与权限为您的数仓保驾护航
  20. python结巴分词 能分英文吗_NLTK(一):英文分词分句

热门文章

  1. 南开大学计算机刘云,王靖-南开大学软件学院官网
  2. 联想微型计算机一体机b505,联想一体机b505电脑怎么样 联想一体机b505电脑的评测详解...
  3. HDU-2389(二分图最大匹配-hopcroft-karp算法)
  4. wps共享文件怎么多人编辑?
  5. 使用HTML5制作物理游戏
  6. OAuth 2.0 学习
  7. mysql 开放远程访问
  8. XMC1402使用Jlink调试记录
  9. 你知道身份证是如何防伪的吗?
  10. 【文献阅读】小样本学习综述:A Survey on Few-Shot Learning(Y. Wang, 等人,ArXiv,201904)