简易计算器

  • 实验说明
  • 硬件设计
    • 动态数码管介绍
    • 矩阵按键介绍
      • 行列扫描
      • 线翻转法
    • 硬件实物连接
  • 软件设计
    • `AC代码:`
    • `main函数代码:`
  • 实验现象
  • 实验总结

实验说明

利用51单片机和keil uVision4共同完成通过按键来控制8位共阴数码管显示数据,并且通过按键来完成连续的加减乘除运算,文章中还有实物连接图。
按键图如下:

上面代表的数字及运算符相应位置为
0 1 2 3
4 5 6 7
8 9 + -

  • / 清零 =

硬件设计

动态数码管介绍

多位数码管,即是两个或两个以上单个数码管并列集中在一起形成一体的数码管。当多位一体时,它们内部的公共端是独立的,而负责显示什么数字的段线全部是连接在一起的,独立的公共端可以控制多位一体中的哪一位数码管点亮,而连接在一起的段线可以控制这个能点亮数码管亮什么数字,通常我们把公共端叫做“位选线”,连接在一起的段线叫做“段选线”,有了这两个线后,通过单片机及外部驱动电路就可以控制任意的数码管显示任意的数字了。

动态显示,就是利用减少段选线,分开位选线,利用位选线不同时选择通
断,改变段选数据来实现的。比如在第一次选中第一位数码管时,给段选数据 0,下一次位选中第二位数码管时显示 1。为了在显示 1 的时候,0 不会消失( 当然实际上是消失了),必须在人肉眼观察不到的时间里再次点亮第一次点亮的 0。而这时就需要记住,人的肉眼正常情况下只能分辨变化超过 24ms 间隔的运动。也就是说,在下一次点亮 0 这个数字的时间差不得大于 24ms。这时就会发现,数码管点亮是在向右或者向左一位一位点亮, 形成了动态效果。 如果把间隔时间改长就能直接展现这现象。

考虑到 51 单片机 IO 口资源的限制,通常我们会使用一种 IO 扩展芯片,比如 74HC138、74HC164、74HC595 芯片等,只需要很少的单片机 IO 口就可以扩展出 8 个控制口,通过级联方式甚至可扩展出更多的控制口。我的开
发板上使用的是 74HC138 译码器芯片,只需单片机 3 个 IO 口就可以实现 8 个位选管脚的控制,大大节省了芯片的 IO 资源。

具体的动态数码管以及IO 扩展芯片之间的关系原理可百度搜索,这里不多讲。总之你要知道IO 扩展芯片只需单片机 3 个 IO 口就可以实现对8个位选管脚的控制。

矩阵按键介绍

开发板上将 16 个按键排成 4 行 4 列,第一行将每个按键的一端连接在一起构成行线,第一列将每个按键的另一端连接在一起构成列线,这样便一共有 4 行 4 列共 8 根线,我们将这 8 根线连接到单片机的 8 个 I/O 口上,通过程序扫描键盘就可检测 16 个键。

单片机检测其是否被按下的依据都是一样的,也就是检测与该键对应的 I/O 口是否为低电平。矩阵键盘两端都与单片机 I/O 口相连,因此在检测时需编程通过单片机 I/O 口送出低电平。检测方法有多种,最常用的是行列扫描和线翻转法。这次实验使用的是行列扫描法

行列扫描

行列扫描法检测时,先送一列为低电平,其余几列全为高电平(此时我们确
定了列数),然后立即轮流检测一次各行是否有低电平,若检测到某一行为低电平(这时我们又确定了行数),则我们便可确认当前被按下的键是哪一行哪一列的,用同样方法轮流送各列一次低电平,再轮流检测一次各行是否变为低电平,这样即可检测完所有的按键,当有键被按下时便可判断出按下的键是哪一个键。当然我们也可以将行线置低电平,扫描列是否有低电平。从而达到整个键盘的检测

线翻转法

线翻转法,就是使所有行线为低电平时,检测所有列线是否有低电平,如果
有,就记录列线值;然后再翻转,使所有列线都为低电平,检测所有行线的值,由于有按键按下,行线的值也会有变化,记录行线的值。从而就可以检测到全部按键。

硬件实物连接


其中矩阵按键的8个排线和stc89c516的P1排线连接,74HC138 译码器芯片的3个排线A、B、C分别和stc89c516的P22、 P23、P24连接,控制动态数码管模块的8个排线和stc89c516的P0排线连接(注意:连线时不要连错了,当然连接取决于你的软件编码,这只是本篇文章的连接,你也可以根据你的软件编码设计来连接排线)

软件设计

通过以上的原理介绍,下面就直接编代码吧。

AC代码:

#include"reg51.h"
typedef unsigned char u8;         //对数据类型进行声明定义
typedef unsigned int u16;sbit LSA=P2^2;  //74HC138译码器数码管位选
sbit LSB=P2^3;
sbit LSC=P2^4;#define GPIO_KEY P1
#define GPIO_DIG P0u16 KeyValue;        //用来存放读取到的键值
u16 keyflag,i;       //用来判断按下的数字还是运算符或清空键
u8 code smgduan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40};  //显示0~F、负号‘-’u16 wei[8]={0};    //用来存放每一位数码管数字的数组void delay(u16 i)//延时函数
{  while(i--);
}void display()    //扫描显示动态数码管
{    LSA=0; LSB=0; LSC=0; GPIO_DIG=smgduan[wei[7]];delay(50); GPIO_DIG=0x00; //消隐LSA=1; LSB=0; LSC=0; GPIO_DIG=smgduan[wei[6]];delay(50); GPIO_DIG=0x00;LSA=0; LSB=1; LSC=0; GPIO_DIG=smgduan[wei[5]];delay(50); GPIO_DIG=0x00; LSA=1; LSB=1; LSC=0; GPIO_DIG=smgduan[wei[4]];delay(50); GPIO_DIG=0x00; LSA=0; LSB=0; LSC=1; GPIO_DIG=smgduan[wei[3]];delay(50); GPIO_DIG=0x00; LSA=1; LSB=0; LSC=1; GPIO_DIG=smgduan[wei[2]];delay(50); GPIO_DIG=0x00; LSA=0; LSB=1; LSC=1; GPIO_DIG=smgduan[wei[1]];delay(50); GPIO_DIG=0x00; LSA=1; LSB=1; LSC=1; GPIO_DIG=smgduan[wei[0]];delay(50); GPIO_DIG=0x00;
}void KeyDown(void)//检测有按键按下并读取键值
{u16 a=0;GPIO_KEY=0x0f;if(GPIO_KEY!=0x0f)//读取按键是否按下{delay(1000);//延时10ms进行消抖if(GPIO_KEY!=0x0f)//再次检测键盘是否按下{   //测试列GPIO_KEY=0x0f;switch(GPIO_KEY)//行列扫描法{case(0X07): KeyValue=0;break;case(0X0b): KeyValue=1;break;case(0X0d): KeyValue=2;break;case(0X0e): KeyValue=3;break;}//测试行GPIO_KEY=0xf0;switch(GPIO_KEY){case(0X70): KeyValue=KeyValue;break;case(0Xb0): KeyValue=KeyValue+4;break;case(0Xd0): KeyValue=KeyValue+8;break;case(0Xe0): KeyValue=KeyValue+12;break;}if(KeyValue==0 || KeyValue==1 || KeyValue==2 || KeyValue==3 || KeyValue==4 || KeyValue==5 || KeyValue==6 || KeyValue==7 || KeyValue==8 || KeyValue==9){keyflag=1;}while((a<50)&&(GPIO_KEY!=0xf0))  //检测按键松手检测{delay(1000);a++;}}}
}

main函数代码:

void main()
{  u16 a=0,b=0,c=0;while(1){    display();                     /* 第一个数字输入*/KeyDown();               if(keyflag==1)  {                                 for(i=7;i>0;i--)              //输入一位,数字向左移动一位 {wei[i]=wei[i-1];}             wei[0]=KeyValue;                            keyflag=0;                                  }else if(KeyValue==14){for(i=0;i<8;i++)wei[i]=0;              //对数码管清零display(); }else if(KeyValue==10)//加法运算      {a=wei[0]+wei[1]*10+wei[2]*100+wei[3]*1000+wei[4]*10000+wei[5]*100000+wei[6]*1000000+wei[7]*10000000;   //计算a的值for(i=0;i<8;i++)wei[i]=0;while(1)                                //输入第二个数{display();KeyDown();if(KeyValue==15) break;//当读到等于号,既,KeyValue=15时,停止输入if(keyflag==1){  for(i=7;i>0;i--){wei[i]=wei[i-1];}wei[0]=KeyValue;keyflag=0;}}b=wei[0]+wei[1]*10+wei[2]*100+wei[3]*1000+wei[4]*10000+wei[5]*100000+wei[6]*1000000+wei[7]*10000000;   //计算b的值c=a+b;wei[0]=c%10;                    //计算C的各个位的数字wei[1]=c/10%10;wei[2]=c/100%10;wei[3]=c/1000%10;wei[4]=c/10000%10;wei[5]=c/100000%10;wei[6]=c/1000000%10;wei[7]=c/10000000%10;display();}else    if(KeyValue==11)//减法运算       {a=wei[0]+wei[1]*10+wei[2]*100+wei[3]*1000+wei[4]*10000+wei[5]*100000+wei[6]*1000000+wei[7]*10000000;for(i=0;i<8;i++)wei[i]=0;while(1){                                     //输入第二个数display();KeyDown();if(KeyValue==15) break;if(keyflag==1){  for(i=7;i>0;i--){wei[i]=wei[i-1];}wei[0]=KeyValue;keyflag=0;}}b=wei[0]+wei[1]*10+wei[2]*100+wei[3]*1000+wei[4]*10000+wei[5]*100000+wei[6]*1000000+wei[7]*10000000;   //计算b的值if(a>=b){c=a-b;wei[0]=c%10;                 //计算C的各个位的数字wei[1]=c/10%10;wei[2]=c/100%10;wei[3]=c/1000%10;wei[4]=c/10000%10;wei[5]=c/100000%10;wei[6]=c/1000000%10;wei[7]=c/10000000%10;}if(a<b)//相减得负数{u16 e=0;//用于判断负号的位置c=b-a;wei[0]=c%10;              //计算C的各个位的数字wei[1]=c/10%10;if(wei[1]==0){wei[1]=16;e=1;}wei[2]=c/100%10;if(wei[2]==0 && e==0){wei[2]=16;e=1;}wei[3]=c/1000%10;if(wei[3]==0 && e==0){wei[3]=16;e=1;}wei[4]=c/10000%10;if(wei[4]==0 && e==0){wei[4]=16;e=1;}wei[5]=c/100000%10;if(wei[5]==0 && e==0){wei[5]=16;e=1;}wei[6]=c/1000000%10;if(wei[6]==0 && e==0){wei[6]=16;e=1;}wei[7]=c/10000000%10;if(wei[7]==0 && e==0){wei[7]=16;e=1;}}display();}else   if(KeyValue==12)//乘法运算       {a=wei[0]+wei[1]*10+wei[2]*100+wei[3]*1000+wei[4]*10000+wei[5]*100000+wei[6]*1000000+wei[7]*10000000;for(i=0;i<8;i++)wei[i]=0; while(1){                                      //输入第二个数display();KeyDown();if(KeyValue==15) break;if(keyflag==1){  for(i=7;i>0;i--){wei[i]=wei[i-1];}wei[0]=KeyValue;keyflag=0;}}b=wei[0]+wei[1]*10+wei[2]*100+wei[3]*1000+wei[4]*10000+wei[5]*100000+wei[6]*1000000+wei[7]*10000000;    //计算b的值c=a*b;wei[0]=c%10;                 //计算C的各个位的数字wei[1]=c/10%10;wei[2]=c/100%10;wei[3]=c/1000%10;wei[4]=c/10000%10;wei[5]=c/100000%10;wei[6]=c/1000000%10;wei[7]=c/10000000%10;display();}else  if(KeyValue==13)//除法运算        {a=wei[0]+wei[1]*10+wei[2]*100+wei[3]*1000+wei[4]*10000+wei[5]*100000+wei[6]*1000000+wei[7]*10000000;for(i=0;i<8;i++)wei[i]=0;while(1)                                 //输入第二个数{display();KeyDown();if(KeyValue==15) break;if(keyflag==1){  for(i=7;i>0;i--){wei[i]=wei[i-1];}wei[0]=KeyValue;keyflag=0;}}b=wei[0]+wei[1]*10+wei[2]*100+wei[3]*1000+wei[4]*10000+wei[5]*100000+wei[6]*1000000+wei[7]*10000000;    //计算b的值c=a/b;wei[0]=c%10;                   //计算C的各个位的数字wei[1]=c/10%10;wei[2]=c/100%10;wei[3]=c/1000%10;wei[4]=c/10000%10;wei[5]=c/100000%10;wei[6]=c/1000000%10;wei[7]=c/10000000%10;display();               //显示最终结果}}
}

实验现象

通过以上的硬件和软件设计(把两块代码放在main.c里面运行即可)后,这个简易计算器就大功告成了。
举例:
666/3*9-1314+520=?

你可以拿出计算器来算下看是否为这个数字

实验总结

这个简易计算器还有不足的地方,比如小数怎么处理,这里我就没有再深究了,有兴趣的博客可以再去深究,其实通过液晶显示屏和矩阵按键也可以来完成本次实验,效果可能更好;
最后代码中有不足或需要改善的地方求各位大佬博主指出,如果觉得本篇文章写得好,欢迎各位点赞、评论,谢谢大家!

51单片机制作简易计算器(动态数码管、矩阵按键)相关推荐

  1. 【Proteus仿真】51单片机制作简易计算器+ LCD1602显示

    [Proteus仿真]51单片机制作简易计算器+ LCD1602显示 Proteus仿真效果演示 程序代码 #include<reg51.h> //头文件 #define uint uns ...

  2. c语言51单片机计算器,新基于51单片机的简易计算器

    <新基于51单片机的简易计算器>由会员分享,可在线阅读,更多相关<新基于51单片机的简易计算器(24页珍藏版)>请在人人文库网上搜索. 1.基于51单片机的简易计算器1.前言: ...

  3. 51单片机的简易计算器设计(仿真+程序+原理图+PCB+设计报告)

    本设计: 基于51单片机的简易计算器设计(仿真+程序+原理图+PCB+设计报告) 仿真:proteus 7.8 程序编译器:keil 4/keil 5 编程语言:C语言 编号S0001 芯片可以替换为 ...

  4. 51单片机实现简易计算器

    本来做出来之后时间也是比较久了,具体代码的意义我暂时没有再回看,给标注出来,相信读者也是看了就明白,主要是当初在调试折腾的过程需要下一些功夫,因为当时是初学者,完成慢,成品也不好,还请多多包涵. 目录 ...

  5. 基于51单片机的简易计算器的实现

    目录 一.硬件简介 1.LCD1602液晶显示器介绍 (1)LCD1602的组成 (2)各引脚功能介绍 (3)DDRAM 2.矩阵按键介绍 (1)矩阵按键的优点: (2)原理: (3)检测方法: (4 ...

  6. 手把手教学51单片机 | 第四节 动态数码管,用6位数码管做一个时钟

    1.利用定时器做一个流水灯的程序 //流水灯从上到下一次点亮,然后让全部灯闪烁两次,依次循环 #include<reg52.h> #include<intrins.h> #de ...

  7. 51单片机制作简易数字电压表

    首先打开proteus软件导入各个元器件,并连线. 接下来介绍下adc0808的导入方法 频率发生器的导入方法: 接下来就要编写c程序了,代码都是经过测试的,可以方型复制使用. #include&qu ...

  8. 基于51单片机的简易mp3的制作

    基于51单片机的简易mp3制作原理 1.  项目要求 基于AT89C51系列单片机的音乐播放器,采用AT89C51作为主控芯片,采用12864LCD作为液晶显示屏,通过按键实现播放器对应按键功能. 2 ...

  9. 基于51单片机的简易6位密码锁(数码管 led 矩阵)

    本文章是基于51单片机的简易密码锁,无掉电保护,所以没有用到I^C总线协议,仅用到数码管显示,矩阵键盘扫描,led显示. 本例程主要实现功能为:定义一个六位数的初始密码,用矩阵输入,输入正确后led被 ...

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

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

最新文章

  1. 隔空操作之简单的模拟三种行为
  2. day 29 socketsetserver 模块
  3. 腾讯回应两块八卖《鬼谷八荒》:已下架并退款;iMac Pro 售完即停产;iCloud 识别姓氏闹乌龙 | 极客头条...
  4. spark java 案例_Spark入门案例
  5. 【Vue3 + SpringBoot】搭建企业日报管理saas系统
  6. 波形信号发生器设计 Proteus仿真--输出频率可调的正弦波、三角波、方波
  7. 计算机组成原理数据通路实验报告,数据通路组成实验汇总_相关文章专题_写写帮文库...
  8. 经典详细的Struts2教程(附案例源码)
  9. 裤子尺码对照表eur40_裤子的欧码32 34 36 38 40代表什么意思
  10. 微信公众平台的开发流程及其要点
  11. mysql 丢失msvcr120.dll_安装MySQL数据库提示计算机中丢失MSVCR120.dll文件
  12. 关于Ubuntu下浏览器不能上网但是能ping通网络的问题
  13. pdf转换器电脑版免费,好用的办公操作软件集合
  14. Oracle入门精读14_Lsnrctl命令
  15. Mezzanine怎样为BLOG创建分级目录
  16. Python Class 05-字符串
  17. 西湖大学人工智能与生物医学影像实验室招聘科研助理及博士后
  18. 如何查看域控计算机是哪个用户登陆,查看域控制器上登录用户
  19. 常用的企业管理软件有哪些?
  20. Windows下安装python2与python3以及分别对应的virtualenv

热门文章

  1. 爬虫->TpImgspider
  2. 引入组件时的错误:Module not found: Error: Can‘t resolve ‘@/pages/Search‘
  3. C++:建立一个被称为sroot()的函数,返回其参数的二次方根。重载sroot()3次,让它返回整数、长整数与双精度的二次方根
  4. “学习金字塔理论”--了解,认知与实践
  5. php 上传图片 裁剪,thinkphp_图片上传裁剪功能
  6. android 自定义图片裁剪,Android自定义View实现照片裁剪框与照片裁剪功能
  7. Centos7 局域网邮件服务器实践
  8. 翻译:最令人印象深刻的YouTube频道,可让您学习AI,机器学习和数据科学
  9. 讨论小程序游戏开发用什么好?
  10. 不定式和分词作状语的区别