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

news/2024/11/8 23:50:29/

简易计算器

  • 实验说明
  • 硬件设计
    • 动态数码管介绍
    • 矩阵按键介绍
      • 行列扫描
      • 线翻转法
    • 硬件实物连接
  • 软件设计
    • `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=?

在这里插入图片描述

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

实验总结

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


http://www.ppmy.cn/news/317820.html

相关文章

相册计算机软件,电脑相册制作软件免费版,windows自带安全又免费相册制作软件...

很多朋友想在电脑上把自己拍的一些视频和照片做成相册或者是相册视频&#xff0c;那么&#xff0c;就会找电脑相册制作软件免费版&#xff0c;实际上&#xff0c;网络上很多稍微好用的相册制作软件基本都是收费的&#xff0c;免费相册制作软件一般都会功能上有所限制、功能不全…

Html 3D旋转相册制作

程序示例精选 Html 3D旋转相册制作 如需安装运行环境或远程调试&#xff0c;见文章底部微信名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<Html 3D旋转相册制作>>编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习…

基于STM32的简易数码相册

原理&#xff1a;在板子上插入SD卡&#xff0c;并使用FATFS文件系统来循环读取并显示SD卡内的指定目录内的所有BMP图片。 这是显示效果&#xff08;能上传视频的话就能看到循环显示效果&#xff09;&#xff1a; 因为图片显示函数显示的是24位BMP图片转换后的16位BMP图片&#…

数字钟的设计与制作

2006-06-13 11:28:37 数字钟的设计与制作 数字钟是一种用数字电路技术实现时、分、秒计时的装置&#xff0c;与机械式时钟相比具有更高的准确性和直观性&#xff0c;且无机械装置&#xff0c;具有更更长的使用寿命&#xff0c;因此得到了广泛的使用。 数字钟从原理上讲是一种典…

c语言做相册影集,相册制作-影楼相册制作 看了这个你也会相册制作了 电子相册制作软件下载附使用教程...

相信很多的朋友都有用自己的照片来制作电子相册了吧&#xff0c;这是照片的另一种展示方式&#xff0c;这几件可是越来越流行了。大部分的小伙伴都喜欢用自己的照片或是别人的照片、自己偶像的照片、喜欢的照片来制作相册&#xff0c; 并配上喜欢的背景音乐&#xff0c;确实又是…

用会声会影10制作高保真电子相册

用会声会影10制作高保真电子相册 随着数码相机的普及&#xff0c;用数码相机记录旅行风光、亲友相聚、宝宝的成长……己成为个人精神生活的重要内容。制作高保真的电子相册不仅方便观看&#xff0c;而且还可节约大量的相片加洗费用。 制作电子相册有许多方法&#xff0c;我就曾…

数字电子钟的设计与制作

学号姓名实物演示&#xff08;60%&#xff09;论文成绩&#xff08;30%&#xff09;平时成绩&#xff08;10%&#xff09;总成绩0144300 0144297 评语&#xff1a;该小组基于单片机设计了一个系统&#xff0c;主要实现了数字时钟实时显示和远程通信的功能&#xff0c;…

怎样做音乐相册怎样制作?手把手教你制作

大家平时出门游玩的时候&#xff0c;会拍摄一些好看的照片吗&#xff1f;那你们会将这些照片分享在社交平台上吗&#xff1f;普通的照片分享&#xff0c;有时会显得比较枯燥单调&#xff0c;其实我们可以将这些照片制作成音乐相册&#xff0c;这样就可以丰富照片的内容&#xf…