02-51单片机数码管与矩阵键盘

news/2025/1/12 19:23:43/

一、数码管模块

1.数码管介绍

如图所示为一个数码管的结构图:

在这里插入图片描述

  • 说明:
    1. 数码管上下各有五个引脚,其中上下中间的两个引脚是联通的,一般为数码管的公共端,分为共阴极或共阳极;
    2. 其它八个引脚分别对应八个二极管,从 a ~ g 包括右下角的点,每个二极管与就近的引脚一一对应。

2.单片机数码管电路图

2.1数码管电路图

在这里插入图片描述

  • 说明:
    1. 如图所示的电路图,可以看到使用的是两个四位一体的共阴极数码管,八个数码管的段选全部并联一起引出,每个数码管一个位选公共端;
    2. 在通过数码管显示数字的时候,先要确定通过哪个数码管显示数字,将其公共端赋为 0 ,其它数码管公共端赋为 1 ,即为共阴极。然后将指定段 0 ~ 7 赋值 0(熄灭) 1(点亮),以此显示相应的数字;

2.2 74HC138译码器

  • 为了节约 MCU 的 IO 口,这里用到了译码器:

在这里插入图片描述

  • 说明:
    1. 这是一个三通道输入,八通道输出的译码器,其中 P22 - P24 是对应的单片机上的 I/O 口,一共三个;
    2. Y0 - Y7 对应的是 LED1 - LED8 八个数码管的公共端,字母上面都有一个横线,低电平有效,因此可以推测这里的数码管共阴极。计算机里三位二进制对应一位八进制,三位二进制范围:000 ~ 111,转换为八进制就是 0 ~ 7,正好 8 个数字,对应八个数码管公共端,这样既可以实现通过三个引脚,控制八个数码管公共端信号了;
    3. G1、G2A、G2B和 GND 为使能端,字母上面有横线的默认是低电平有效。

2.3 74HC245驱动芯片

在这里插入图片描述

  • 说明
    1. 74HC245 是一种三态输出、八路信号收发器,主要应用于大屏显示,以及其它的消费类电子产品中增加驱动;
    2. 它具有双向输出功能,即 DIR 方向控制,当 DIR=1 时,由 A 到 B,等于 0 时,由 B 到 A,因为这里是给数码管供电,即 A 为输入端,B为输出端,因此直接将 DIR 接 VCC,输出使能 OE 接 GND。

3.数码管实验

3.1指定数码管显示指定数值

  • 代码演示
#include <REGX52.H>// 定义一个数组,存放0~9的数值编码
unsigned char Nums[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void Nixie(unsigned char LEDNum, ShowNum)
{// 选择点亮哪个LEDswitch(LEDNum){case 1:P2_4=0;P2_3=0;P2_2=0;break;case 2:P2_4=0;P2_3=0;P2_2=1;break;case 3:P2_4=0;P2_3=1;P2_2=0;break;case 4:P2_4=0;P2_3=1;P2_2=1;break;case 5:P2_4=1;P2_3=0;P2_2=0;break;case 6:P2_4=1;P2_3=0;P2_2=1;break;case 7:P2_4=1;P2_3=1;P2_2=0;break;case 8:P2_4=1;P2_3=1;P2_2=1;break;}P0 = Nums[ShowNum];
}void main()
{Nixie(7, 4);while(1);
}
  • 说明:
    1. 上面演示的结果:使用 LED7 显示数字4,可以指定使用哪个数码管显示指定数值;
    2. 将 0 ~ 9数字的段码数据存放到一个数组中,函数调用时传入对应的数字的下标索引,取出要显示数字的段码数据。断码数据对照数码管电路图计算,a ~ dp 分别对应八位二进制的低位到高位,想让数码管哪个 LED 亮,就将对应二进制位赋 1 就行;
    3. 通过 switch 语句确定要使用哪一个数码管显示数字。

3.2数码管动态显示

  • 代码演示
#include <REGX52.H>
#include <INTRINS.H>// 定义一个数组,存放0~9的数值编码
unsigned char Nums[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};void Delayxms(unsigned int xms)		//@12.000MHz
{unsigned char i, j;while(xms){i = 2;j = 239;do{while (--j);} while (--i);xms--;}
}void Nixie(unsigned char LEDNum, ShowNum)
{// 选择点亮哪个LEDswitch(LEDNum){case 1:P2_4=0;P2_3=0;P2_2=0;break;case 2:P2_4=0;P2_3=0;P2_2=1;break;case 3:P2_4=0;P2_3=1;P2_2=0;break;case 4:P2_4=0;P2_3=1;P2_2=1;break;case 5:P2_4=1;P2_3=0;P2_2=0;break;case 6:P2_4=1;P2_3=0;P2_2=1;break;case 7:P2_4=1;P2_3=1;P2_2=0;break;case 8:P2_4=1;P2_3=1;P2_2=1;break;}P0 = Nums[ShowNum];// 用于消影Delayxms(1);P0=0x00;
}void main()
{while(1){Nixie(8, 1);// Delayxms(20);Nixie(7, 2);// Delayxms(20);Nixie(6, 3);// Delayxms(20);}
}
  • 说明:
    1. 代码演示结果,在 LED8 LED7 LED6 分别显示连续数字 123;
    2. 这里只不过在 while 循环里不断调用函数,在三个数码管快速切换显示数字,利用人眼的余晖效应,类似于多进程的并发。人眼反应不过来,因此看起来是显示了一个连续的数字,实际上是在三个数码管之间快速切换显示;
    3. 显示过程中会有一个问题,就是调用函数的时候,先位选再段选,接着执行下一个函数,调用时也是先位选再段选,但是由于切换太快,切换到下一个位选的时候,下一个段选还没执行到,结果执行了上一个的段选,就导致显示的数值有重影,需要进行消影;
    4. 消影就在每次函数执行的最后,将该位段的段码数据全部置 0 ;
    5. 上面的实现原理是通过单片机的快速扫描实现的,比较消耗单片机的 CPU 资源。

二、LCD1602调试工具

1.LCD1602介绍

在这里插入图片描述

这里就是一个微型的显示屏,规格为 2 × 16 ,可以显示两行,每行最多显示 16 个字符。

我们在编写C语言代码的时候,会经常用到 printf 第三方库函数,将运行的结果打印到终端屏幕上,以进行相关的调试工作。在使用单片机的时候,我们也需要查看调试信息,就可以通过这样一块屏幕,然后写好想要的功能函数,通过头文件包含调用,达到输出显示的效果。

2.LCD1602 + 模块化编程

2.1延时函数模块化

模块化编程属于C语言阶段的基础,我在C语言里面有一篇博客专门讲了多文件编程,也就是模块化编程。

  • Delay.c(这里的文件名自己随便取,但需要见名知意)
#include <INTRINS.H>void Delayxms(unsigned int xms)		//@12.000MHz
{unsigned char i, j;while(xms){i = 2;j = 239;do{while (--j);} while (--i);xms--;}
}
  • Delay.h(头文件名最好与功能文件名相同,只是后缀不相同)
#ifndef __DELAY_H__
#define __DELAY_H__void Delayxms(unsigned int xms);#endif

2.2第三方函数介绍

本笔记是基于B站江科大51单片机视频学习,LCD1602具体的用法还未学习,因此使用江科大提供的模块化功能函数来实现,具体文件的获取,在江科大的网址。

获取文件后,直接将一个.c文件和一个头文件拷贝到和当前项目 main 文件同级目录下,在 main 文件里面包含头文件即可使用。

  • 函数介绍
函数作用
LCD_Init();初始化
LCD_ShowChar(1,1,‘A’);显示一个字符(行数,列数,显示的字符)
LCD_ShowString(1,3,“Hello”);显示字符串(行数,列数,显示的字符串)
LCD_ShowNum(1,9,123,3);显示无符号十进制数字(行数,列数,显示的数值,数值位数)
LCD_ShowSignedNum(1,13,-66,2);显示有符号十进制数字(行数,列数,显示的数值,数值位数)
LCD_ShowHexNum(2,1,0xA8,2);显示十六进制数字(行数,列数,显示的数值,数值位数)
LCD_ShowBinNum(2,4,0xAA,8);显示二进制数字(行数,列数,显示的数值,数值位数)

2.3LCD1602函数演示

  • 代码演示
#include "Delay.h"
#include "LCD1602.h"void main()
{unsigned int num = 0; // 局部变量的定义要放在函数第一行// 记得初始化LCD_Init();
//	LCD_ShowChar(1,1,'A'); // 打印字符 A
//	LCD_ShowString(1,1,"hello world!"); // 打印字符串 hello world!
//	LCD_ShowNum(1,1,123,3); // 打印数值 123
//	LCD_ShowSignedNum(1,1,-66,2); // 打印数值 -66
//	LCD_ShowHexNum(1,1,0xA8,2); // 打印十六进制数 A8
//	LCD_ShowBinNum(1,1,0xAA,8); // 打印二进制数 1010 1010// 显示数值以秒递增while(num < 1000){LCD_ShowNum(1,1,num,3);Delayxms(1000);num++;}while(1);
}
  • 说明:
    1. 上面分别调试了一下,LCD1602相应的函数;
    2. 后面做了一个计时器,从0开始以秒为单位递增到最大 999。

三、矩阵键盘

1.矩阵键盘介绍

1.1矩阵键盘实物介绍

在这里插入图片描述

  • 介绍:
    1. 如图所示,为一个 4×4 的矩阵键盘,可以通过8个 I/O 口控制16个按键;
    2. 一个独立按键,就需要分配一个 I/O 口,但如果按键很多时,还这样分配,比较浪费资源,因此将按键按照行列分布,这样只需要使用行数 + 列数个 I/O 口就可以操作多个按键;
    3. 矩阵键盘获取按键状态的原理和独立按键一样,即将公共端设为低电平,I/O 口读到数据为 0 代表按下,为 1 代表松开。

1.2矩阵键盘电路图

在这里插入图片描述

  • 说明:
    1. 矩阵键盘原理图如图所示,可以看到,这里将16个按键,通过8个 I/O 口控制;
    2. 单独看第一行,就和前面学习的独立按键一样,P17 为公共端,将公共端设置低电平,P10 - P13 就相当于段选端,控制S1 - S4四个按键,谁传回 I/O 的数据为 0 ,就能判断谁按下了。然后,去逐行扫描,就能分别读取每个按键的状态,因为扫描速度非常块,因此我们人在操作的过程中感觉每个按键都是实时判断的一样,本质上是同一时间只能判断一行或者一列;
    3. 上面将每行作为公共端,也可以将每列作为公共端,因此就分为行刷新和列刷新两种方式,为了防止 I/O 口干扰其它模块,这里选择使用列刷新的方式进行实验。

2.矩阵键盘 + LCD1602案例

2.1按下按键显示数值

这里采用模块化编程,各个文件的代码演示如下:

  • MatrixKey.c
#include <REGX52.H>
#include "Delay.h"unsigned char MatrixKey()
{// 定义变量,用于保存按下按键对应的数值unsigned char num=0;// 将公共端初始化,全部置于高电平P1=0xFF;P1_3=0; // 将当前要扫描的列公共端置于低电平if(P1_7==0){Delayxms(20);while(P1_7==0);Delayxms(20);num=1;}if(P1_6==0){Delayxms(20);while(P1_6==0);Delayxms(20);num=5;}if(P1_5==0){Delayxms(20);while(P1_5==0);Delayxms(20);num=9;}if(P1_4==0){Delayxms(20);while(P1_4==0);Delayxms(20);num=13;}P1=0xFF;P1_2=0;if(P1_7==0){Delayxms(20);while(P1_7==0);Delayxms(20);num=2;}if(P1_6==0){Delayxms(20);while(P1_6==0);Delayxms(20);num=6;}if(P1_5==0){Delayxms(20);while(P1_5==0);Delayxms(20);num=10;}if(P1_4==0){Delayxms(20);while(P1_4==0);Delayxms(20);num=14;}P1=0xFF;P1_1=0;if(P1_7==0){Delayxms(20);while(P1_7==0);Delayxms(20);num=3;}if(P1_6==0){Delayxms(20);while(P1_6==0);Delayxms(20);num=7;}if(P1_5==0){Delayxms(20);while(P1_5==0);Delayxms(20);num=11;}if(P1_4==0){Delayxms(20);while(P1_4==0);Delayxms(20);num=15;}P1=0xFF;P1_0=0;if(P1_7==0){Delayxms(20);while(P1_7==0);Delayxms(20);num=4;}if(P1_6==0){Delayxms(20);while(P1_6==0);Delayxms(20);num=8;}if(P1_5==0){Delayxms(20);while(P1_5==0);Delayxms(20);num=12;}if(P1_4==0){Delayxms(20);while(P1_4==0);Delayxms(20);num=16;}// 将结果返回return num;
}
  • MatrixKey.h
#ifndef __MATRIXKEY_H__
#define __MATRIXKEY_H__unsigned char MatrixKey();#endif
  • main.c
#include "Delay.h"
#include "LCD1602.h"
#include "MatrixKey.h"void main()
{	// 51单片机里局部变量必须放在函数的首行unsigned char KeyNum=0;LCD_Init();LCD_ShowString(1, 1, "MatrixKey");while(1){KeyNum=MatrixKey();// 加判断是为了防止被 0 刷新掉。显示不出来if(KeyNum){LCD_ShowNum(2, 1, KeyNum, 2);}}
}
  • 说明:
    1. 上面代码演示效果为:按下矩阵键盘的按键,在LCD1602屏幕上显示对应的数值,如:按下 S12 会在键盘上显示12;
    2. 这里采用的是列刷新的方式,即将 P10 - P13 作为每一列的公共端,先将公共端整体初始化为高电平,然后将当前要扫描的列的公共端置低电平,去判断这一列的四个按键的回馈信号是 0 还是 1 ,为 0 则将变量 num 赋值为按键对应的数值,然后将结果作为函数返回值返回即可,如果没有按键按下,返回的就是默认值0。再继续去扫描下一列,如此循环;
    3. 主函数里只需要不停调用矩阵键盘扫描函数即可,如果函数返回值为 0 ,不调用函数显示,如果非 0 ,则调用函数显示。加 if 判断的原因就是防止结果被 0 刷新,因为程序执行很快,这边刚获取返回值,刚打印到屏幕,函数又执行了几遍,返回 0 ,将 0 赋值给了 KeyNum ,打印 KeyNum 的值,这样上一次运行的结果还没看清,就被 0 刷新了。

2.2矩阵键盘密码锁

  • 代码演示
#include "Delay.h"
#include "LCD1602.h"
#include "MatrixKey.h"unsigned char KeyNum; // 获取键盘数值
unsigned int Password, count; // 存储密码,count用于计算输入数值个数void main()
{	LCD_Init();LCD_ShowString(1, 1, "Password:");while(1){KeyNum=MatrixKey();// 如果键盘扫描的返回值非 0 才执行相应的代码if(KeyNum){if(KeyNum<=10){if(count<4){Password*=10;Password+=(KeyNum%10);}count++;LCD_ShowNum(1, 10, Password, 4);}if(KeyNum==11){if(Password==1234){// 字符串后面的空格是防止上一次结果的干扰,因此通过空格刷新掉LCD_ShowString(2, 1, "Hello Boss!!!   ");Password=0;count=0;LCD_ShowNum(1, 10, Password, 4);}else{LCD_ShowString(2, 1, "Password Error! ");Password=0;count=0;LCD_ShowNum(1, 10, Password, 4);}}if(KeyNum==12){Password=0;count=0;LCD_ShowNum(1, 10, Password, 4);// 16个空格,将提示栏全部刷新为空LCD_ShowString(2, 1, "                ");}}}
}
  • 说明:
    1. 运行效果:输入正确的四位数密码,会打印:Hello Boss!!! ,密码输入错误会打印:Password Error!。最多输入四位,如果已经输入了四位,再输入无效,每次输入完密码按下确认键显示密码正确与否的同时,密码全部恢复为 0000,按取消密码全部恢复为 0000 ,可以再重新输入;
    2. 将键盘 S1 - S10 作为数字键,分别代表 1 - 9 和 0,但是键盘扫描函数的返回值为 0 的话,那么就不会进来执行相关代码了,因此这里的 0 需要通过算法来获得。即用返回值对 10 取余,1 - 9 对 10 取余还是 1 - 9,10 对 10 取余就是0;
    3. 但是屏幕显示的数值是四位数密码,前面键盘按下输出数值的时候,都是后一个会把前一个数值刷新掉。那这里需要实现四位数显示也需要简单的算法,通过一个变量 Password 专门用于存放密码,(注意:Password变量要用无符号 int ,因为无符号 char 最大只能是 255,用 char 变量会越界),Password 初始值为0,对其先乘 10 再加上按键输入值,如此循环,每次对上一次的 Password 值乘10,加上下一次返回值的数值,就能实现多位密码;
    4. 为了限制最多输入四个数值,定义 count 变量用于记录按键输入次数,每输入一次,count + 1,通过条件判断最多输入 4 次,超过四次,不会再执行 Password 赋值操作;
    5. 将 S11 和 S12 作为确认键和取消键,条件判断函数返回值为 11,执行确认键的功能:如果输入的密码正确,则打印 Hello Boss!!! ,同时将 Password 和 count 设置回初始化值0,并且将密码显示为 0000 ,以便下次运行使用。字符串后面的空格是为了将上一次运行的结果刷新掉,保证屏幕只有我们需要显示的结果。如果密码错误,打印 Password Error! ,同样将密码变量和计数变量设为初始值,屏幕显示为 0000;
    6. 条件判断返回值为 12,则执行取消功能,同样将密码变量和计数变量设为初始值,屏幕密码显示为 0000,提示行全部清空。
      上一次的 Password 值乘10,加上下一次返回值的数值,就能实现多位密码;
    7. 为了限制最多输入四个数值,定义 count 变量用于记录按键输入次数,每输入一次,count + 1,通过条件判断最多输入 4 次,超过四次,不会再执行 Password 赋值操作;
    8. 将 S11 和 S12 作为确认键和取消键,条件判断函数返回值为 11,执行确认键的功能:如果输入的密码正确,则打印 Hello Boss!!! ,同时将 Password 和 count 设置回初始化值0,并且将密码显示为 0000 ,以便下次运行使用。字符串后面的空格是为了将上一次运行的结果刷新掉,保证屏幕只有我们需要显示的结果。如果密码错误,打印 Password Error! ,同样将密码变量和计数变量设为初始值,屏幕显示为 0000;
    9. 条件判断返回值为 12,则执行取消功能,同样将密码变量和计数变量设为初始值,屏幕密码显示为 0000,提示行全部清空。

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

相关文章

ARCGIS三维模型及动画模拟

一、实验名称&#xff1a; 三维模型及动画模拟 二、实验目的&#xff1a; 通过本实验练习&#xff0c;掌握ARCGIS三维模型及动画模拟。 三、实验内容和要求&#xff1a; 实验内容&#xff1a; 利用ARCSCENE软件相关分析工具及实验数据&#xff0c;制作三维模型&#xff0…

如何在Jupyter中快速切换Anaconda里不同的虚拟环境

介绍 很多网友在使用Jupyter的时候会遇到各种各样的问题&#xff0c;其中一个比较麻烦的问题就是我在Anaconda有多个Python的环境里面&#xff0c;如何让jupyter快速切换不同的Python环境&#xff0c;就像Pycharm那样简单。 网上的资料通常都是让你输入几个命令&#xff0c;…

<C++学习>C++ Boost 输入与输出教程

C Boost 输入与输出教程 Boost 提供了许多实用的工具来增强 C 的输入与输出功能&#xff0c;包括字符串格式化、文件操作、序列化和日志系统等。在标准 I/O 的基础上&#xff0c;Boost 的功能更丰富、更灵活&#xff0c;能够满足复杂的 I/O 场景需求。 1. Boost 中与 I/O 相关…

AI驱动的可演化架构与前端开发效率

1. 引言 在当今快节奏的数字时代&#xff0c;软件系统需要具备强大的适应能力才能在瞬息万变的市场需求中保持竞争力。软件可演化架构的重要性日益凸显&#xff0c;它能够让软件系统在面对需求变更、技术升级以及市场波动时&#xff0c;能够快速、高效地进行调整和升级&#x…

QML states和transitions的使用

一、介绍 1、states Qml states是指在Qml中定义的一组状态&#xff08;States&#xff09;&#xff0c;用于管理UI元素的状态转换和属性变化。每个状态都包含一组属性值的集合&#xff0c;并且可以在不同的状态间进行切换。 通过定义不同的状态&#xff0c;可以在不同的应用场…

Nginx安全加固系列:只加载批准的内容源 ( CSP )

CSP&#xff0c;也就是内容安全策略&#xff0c;简单的说就是网站只加载信任的来源内容&#xff0c;就是一种白名单机制&#xff0c;这个是通过设置HTTP 响应头实现的。 F12 查看响应标头&#xff0c;看到有Content-Security-Policy这个标头&#xff0c;就表明了这个网站启动了…

流浪猫流浪狗领养PHP网站源码

源码介绍 流浪猫流浪狗领养PHP网站源码&#xff0c;适合做猫狗宠物类的发信息发布。当然其他信息发布也是可以的。 导入数据库&#xff0c;修改数据库配置/application/database.php 设置TP伪静态&#xff0c;设置运行目录&#xff0c; 后台&#xff1a;/abcd.php/dashboard?…

C语言gdb调试

目录 1.gdb介绍 2.设置断点 2.1.测试代码 2.2.设置函数断点 2.3.设置文件行号断点 2.4.设置条件断点 2.5.多线程调试 3.删除断点 3.1.删除指定断点 3.2.删除全部断点 4.查看变量信息 4.1.p命令 4.2.display命令 4.3.watch命令 5.coredump日志 6.总结 1.gdb介绍…