首页 > 其他分享 >02-51单片机数码管与矩阵键盘

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

时间:2025-01-10 12:58:44浏览次数:3  
标签:02 P2 P1 20 51 数码管 while Delayxms

一、数码管模块

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)
{
	// 选择点亮哪个LED
	switch(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)
{
	// 选择点亮哪个LED
	switch(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,提示行全部清空。

标签:02,P2,P1,20,51,数码管,while,Delayxms
From: https://blog.csdn.net/qq_63958145/article/details/145055847

相关文章

  • 01-51单片机LED与独立按键
    一、单片机概述注意:个人学习笔记,里面涉及到的C语言和进程转换相关的知识在C语言部分已经写了,这里是默认都会的状态学习单片机。1.什么是单片机单片机,英文MicroControllerUnit,简称MCU。其内部集成了CPU、RAM、ROM、定时器、中断系统、通讯接口等一系列电脑的常用硬件功......
  • 【每日一题】20250110
    【每日一题】从区间\([0,1]\)随机抽取\(2n\)个数\(x_1,x_2,\ldots,x_n,y_1,y_2,\ldots,y_n\),构成\(n\)个数对\((x_1,y_1)\),\((x_2,y_2),\ldots\),\((x_n,y_n)\),其中两数的平方和小于\(1\)的数对共有\(m\)个,则用随机模拟的方法得到的圆周率\(\pi\)的近......
  • 2025-1-6 / 2025-1-7 做题笔记
    2025-1-6/2025-1-7做题笔记持续更新中……目录2025-1-6/2025-1-7做题笔记P11365[Ynoi2024]新本格魔法少女りすかCF1693D-DecincDividingATUTPC2023G-GraphWeightingABC269Ex-AntichainP11365[Ynoi2024]新本格魔法少女りすかケロシの代码namespaceIO{ ......
  • 2024年终总结-gxngxngxn
    2024年终总结-gxngxngxn引言原本应该早就写完这一篇年终总结的,但由于期末和一些琐事,一直没时间,一拖再拖。到了最近放假了,才得空下笔。想写年终总结的想法起源于去年的这个时候,也就是2024年的一月份左右。那时我如往常一样打开收藏的大佬们的博客,映入眼帘的便是一篇篇年终总结......
  • SSM高校校园招聘系统-毕业设计源码15185
    目  录1绪论1.1研究背景及意义1.2国内外研究现状1.3研究内容1.4论文结构与章节安排2 高校校园招聘系统系统分析2.1可行性分析2.2系统流程分析2.2.1 数据流程3.3.2 业务流程2.3 系统功能分析2.3.1功能性分析2.3.2非功能性分析2.4 系......
  • 使用Typora+Gitee+PicGo解决.md文件发给别人时无法显示图片的问题【2025最新超详细】
    下面是一个关于如何使用Typora、Gitee和PicGo解决.md文件发送给别人时无法显示图片的详细教程。在撰写.md文件时,通常会嵌入一些图片,尤其是在使用Typora进行编辑时。然而,当你将包含图片的.md文件发送给他人时,如果图片没有正确上传到公共的图片服务器,接收方往往......
  • springboot社会救助信息管理系统-毕业设计源码55502
    springboot社会救助信息管理系统摘 要在网络飞速发展的信息时代,各个行业都离不开信息的处理,在这种时代背景下,社会以人们健康为导向,以社会救助信息管理系统的持续创新,根据这两点,为当前形势最重要的社会救助信息管理系统就很有必要。系统采用了B/S结构,在此基础上,对各业务模......
  • 【2025最新】Kali linux零基础学习教程(超详细),从下载、安装到使用,看这一篇就够了!
    kali镜像官网:Indexof/kali-images/1.打开虚拟机选择新建虚拟机安装的位置需要提前新建好桥接网络-把物理机当成了交换机。特点:虚拟机的ip和物理机的ip是同一个网段的。前提:确定自己是否有足够多的ip.对应vmnet0NAT:网络地址转换:对应vmnet1,主机模式:虚拟机......
  • 2025版最新渗透测试零基础入门教程,带你入门到精通(超详细),收藏这篇就够了
    一、渗透测试是什么?释义:我们理解的渗透测试是通过各种⼿段对⽬标进⾏⼀次渗透(攻击),通过渗透来测试⽬标的安全防护能⼒和安全防护意识。打个⽐⽅:⽐如电视剧《我是特种兵》⾥⾯的演习,特种部队(进攻⽅)渗透到蓝军(防守⽅)的指挥部进⾏斩⾸,如果斩⾸成功,那么就可以知道蓝⽅的防守能......
  • 2025版最新黑客最常用的10款黑客工具,零基础入门到精通,收藏这篇就够了
    前言以下所有这些工具都是捆绑在一起的Linux发行版,如KaliLinux或BackBox,建议安装一个合适的Linux黑客系统,尤其是因为这些黑客工具可以(自动)更新。1、Metasploit(渗透测试软件,免费与付费)漏洞利用工具MetasploitFramework(MSF)是一款开源安全漏洞检测工具,附带数千个已知......