51单片机——矩阵键盘+动态数码管

目录

实验内容

Protues电路连接

所需元件

电路原理图

74HC573基本功能

引脚功能

工作原理

应用特点

 Keil代码 

按键扫描代码优化 

实验内容

基于protues8,13,搭建51单片机最小系统连接4*4矩阵键盘(P3)和两位数码管(段选P0、位选P20\P21)

功能要求;两位数码管显示00~15,00~15与矩阵键盘对应,上电后数码管不显示,按下按键后显示对应的数字。

Protues电路连接

所需元件

最小系统的部分就不再提了,第一个是2位数码管、74HC573锁存器等。

电路原理图

其他元件就不再描述了,简单介绍一下74HC573的功能。

74HC573基本功能

原本想用锁存器控制数码管上电不亮)74HC573包含八路3态输出的非反转透明锁存器,每个锁存器都可以独立地存储和传输数据。这些锁存器在数字系统中扮演着重要的角色,如缓冲寄存器、I/O通道、双向总线驱动器和工作寄存器等。

引脚功能
  • D0-D78位数据输入端,用于接收要存储或传输的数据。
  • Q0-Q78位数据输出端,输出锁存器内部存储的数据或传输的数据。
  • OE(Output Enable)输出使能端,低电平有效。当OE为低电平时,允许数据从锁存器输出到数据输出线;当OE为高电平时,输出线为高阻态,即不输出数据。
  • LE(Latch Enable)锁存使能端,高电平有效。当LE为高电平时,锁存器处于透明状态,此时输入端的数据可以直接传输到输出端;当LE为低电平时,锁存器将数据锁存在内部,不再随输入端的数据变化而变化。
工作原理
  1. 数据存储当LE为高电平时,输入端的数据(D0-D7)可以直接传输到输出端(Q0-Q7),此时锁存器处于透明状态。如果此时OE也为低电平,则数据将被有效地输出到外部电路。
  2. 数据锁存当LE为低电平时,无论输入端的数据如何变化,输出端都将保持锁存状态之前的数据不变。此时,即使OE为低电平,输出端也不会随输入端的数据变化而变化。
  3. 输出控制OE端用于控制输出端是否有效。当OE为低电平时,输出端有效;当OE为高电平时,输出端为高阻态,不输出数据。
应用特点
  • 高噪声抵抗特性74HC573芯片具有较高的噪声抵抗能力,能够在复杂的电磁环境中稳定工作。
  • 三态总线驱动输出:输出端为三态门设计,可以方便地与系统总线接口并驱动总线。
  • 置数全并行存取所有锁存器都可以同时接收和存储数据,提高了数据传输的效率。

 Keil代码 

 在本次实验中,因为按键只作为一个赋值的功能,所以直接让keyscan()函数返回一个key的值

并且将按键扫描嵌套到display()中,再将key赋值给num完成数码管的显示。

为了实现上电不显示的功能, 我设置了一个Key_flag标志位,通过标志位的判断决定是否显示。刚上电,Key_flag=0,所以if(Key_flag)为假,条件不成立P0没有得到有效值,故数码管不显示,当按键按下后Key_flag=1,所以if(Key_flag)为真,可以进行显示功能。

/*要求:上电时,两位数码管都不显示。按下矩阵键盘某一按键,在数码管上显示
按键的编号。编号从00~15。
*/
/*思路:矩阵键盘的扫描方式,选用“行扫描法”。

*/
#include <reg51.h>

#define uint8 unsigned char
#define uint16 unsigned int
volatile uint8 Key_flag = 0;
uint8 code table[]={0x3F,0x06,0x5B,0x4F,	//共阴型数码管段码表
										0x66,0x6D,0x7D,0x07,
										0x7F,0x6F,0x77,0x7C,
										0x39,0x5E,0x79,0x71,	//0-F
										0x00};	//段码内容0x00,数码管不显示
//以下为3个子函数的声明
void display();
int keyscan();
void Delay10ms();		
										
void main()
{
	display();
}

void Delay10ms()	//@12.000MHz
{
	unsigned char data i, j;	//将i,j两个变量的存储空间设置在data区

	i = 20;
	j = 113;
	do
	{
		while (--j);
	} while (--i);
	
}

void display()
{
	unsigned int num = 0xFF; // 初始化为一个无效的数字,表示不显示  
  unsigned char high_digit, low_digit;  
	while(1)
	{
		int key=keyscan();
		if (key != -1&& key <= 15)
			{ // 如果有键按下  
            num = key; // 更新显示数字  
						Key_flag=1;
			
        }
			if (Key_flag)  
        {  
            high_digit = num / 10; // 高位数字  
            low_digit = num % 10;  // 低位数字  
  
            // 显示十位数字  
            P2 = 0xFE; // 选中第一个数码管  
            P0 = table[high_digit];  
            Delay10ms();  
  
            // 显示个位数字  
            P2 = 0xFD; // 选中第二个数码管  
            P0 = table[low_digit];  
            Delay10ms();  
						
		
				}
		}
}

int keyscan()
{
	int key=-1;
	P3=0xFE;	//P3=1111,1110
	if(P3!=0xFE)
	{
		Delay10ms();
		if(P3!=0xFE)
		{
			switch(P3)
			{
				case 0xEE:	key=0; break;//若读回P3的高4位为1110,则K0被按下
				case 0xDE:	key=1; break;//若读回P3的高4位为1101,则K1被按下
				case 0xBE:	key=2; break;//若读回P3的高4位为1011,则K2被按下
				case 0x7E:	key=3; break;//若读回P3的高4位为0111,则K3被按下
				default: break;
			}
			while(P3!=0xFE);	//等待按键释放
		}
	}
	
	P3=0xFD;	//P3=1111,1101
	if(P3!=0xFD)
	{
		Delay10ms();
		if(P3!=0xFD)
		{
			switch(P3)
			{
				case 0xED:	key=4; break;//若读回P3的高4位为1110,则K4被按下
				case 0xDD:	key=5; break;//若读回P3的高4位为1101,则K5被按下
				case 0xBD:	key=6; break;//若读回P3的高4位为1011,则K6被按下
				case 0x7D:	key=7; break;//若读回P3的高4位为0111,则K7被按下
				default: break;
			}
			while(P3!=0xFD);	//等待按键释放
		}
	}
	
	P3=0xFB;	//P3=1111,1011
	if(P3!=0xFB)
	{
		Delay10ms();
		if(P3!=0xFB)
		{
			switch(P3)
			{
				case 0xEB:	key=8;  break;//若读回P3的高4位为1110,则K8被按下
				case 0xDB:	key=9;  break;//若读回P3的高4位为1101,则K9被按下
				case 0xBB:	key=10; break;//若读回P3的高4位为1011,则K10被按下
				case 0x7B:	key=11; break;//若读回P3的高4位为0111,则K11被按下
				default: break;
			}
			while(P3!=0xFB);	//等待按键释放
		}
	}
	
	P3=0xF7;	//P3=1111,0111
	if(P3!=0xF7)
	{
		Delay10ms();
		if(P3!=0xF7)
		{
			switch(P3)
			{
				case 0xE7:	key=12; break;//若读回P3的高4位为1110,则K12被按下
				case 0xD7:	key=13; break;//若读回P3的高4位为1101,则K13被按下
				case 0xB7:	key=14; break;//若读回P3的高4位为1011,则K14被按下
				case 0x77:	key=15; break;//若读回P3的高4位为0111,则K15被按下
				default: break;
			}
			while(P3!=0xF7);	//等待按键释放
		}
	}
	return key;
}
按键扫描代码优化 
  1. 我将列扫描的逻辑封装在了一个for循环中,以减少重复代码。

  2. 我使用了~col来设置P3,这样当前列就会被设置为低电平,而其他列则保持高电平。这假设您的硬件连接方式是当某列被拉低时,可以通过读取P3的低4位来确定哪一行有按键被按下。

  3. 按键编号的计算方式(key = i * 4 + row;)是基于假设每列有4个按键,并且按键是线性排列的。根据您的实际硬件连接,这个计算方式可能需要调整。

  4. 我保留了等待按键释放的while循环,但它是被注释掉的。如果您希望在检测到按键按下后立即返回,而不是等待按键释放,那么可以保留这种状态。然而,如果您希望在按键释放之前不响应新的按键按下,那么应该取消注释这个while循环。但请注意,这样做可能会阻塞CPU,特别是在按键被长时间按下时。一个更好的做法是使用中断或定时器来轮询按键状态。

uint8_t keyscan(void)  
{  
    uint8_t key = 0xFF; // 初始化为无按键状态  
    uint8_t col = 0x01; // 列扫描初始值,从0000,0001开始  
  
    // 遍历所有列(4列)  
    for (int i = 0; i < 4; i++)  
    {  
        P3 = ~col; // 设置当前列为低,其他列为高(注意这里使用了取反)  
  
        // 检查是否有按键被按下  
        if (P3 != ~col)  
        {  
            Delay10ms(); // 防抖延时  
  
            // 再次检查按键是否仍然被按下  
            if (P3 != ~col)  
            {  
                // 根据当前列和P3的低4位确定按键编号  
                // 这里假设按键连接在P3的低4位上,且每个列只对应一个按键  
                // 注意:这里假设按键按下时对应位为0,因此使用了P3 & 0x0F  
                uint8_t row = P3 & 0x0F; // 获取P3的低4位  
                  
                // 计算按键编号,这里假设按键是线性排列的  
                // 根据您的硬件连接,这里的计算方式可能需要调整  
                key = i * 4 + row; // 假设每列有4个按键,编号从0到15  
  
                // 等待按键释放(可选,但请注意这可能会阻塞CPU)  
                // while (P3 != ~col); // 取消注释以启用等待按键释放  
            }  
        }  
  
        // 移动到下一列  
        col <<= 1;  
    }  
  
    return key;  
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值