蓝桥杯单片机设计与开发⑤ ---矩阵键盘

1.电路图分析

在这里插入图片描述

当跳线帽接 3、2时为独立键盘 S7 S6 S5 S4 当跳线帽接2、1时为矩阵键盘

2.独立按键

当跳线帽接3、2时,一旦S7 S6 S5 S4这四个按键某个按键被按下,相应的I/O口就是接地状态,为低电平。
写单片机时按键按下需要消抖,之前是使用delay延时程序消抖,但是不建议在程序中使用delay,我们可以使用定时器延时来完成消抖。
之前写独立按键原理:
检测按键按下—>延时消抖—>再次检测按键是否还在按下状态—>确认按键按下—>操作—>检测按键是否松开—>延时消抖—>再次检测按键是否松开—>确定按键松开—>操作

但是对于多个按键来说,这样就显得繁琐了,所以在这里介绍一种简单一些的方法。

我们使用定时器定时2ms,然后每次中断扫描一下按键,将扫描值左移至一个变量中,每移动一次就判断当前连续的8次按键状态是不是全1或者全0,如果是全1则判断为弹起,如果是全0则判断为按下,如果是0和1交错,就认为是抖动,不做任何判定。
利用这种方法就可以避免通过延时消抖占用单片机执行时间,而是转化成了一种按键状态判定而非按键过程判定,我们只对当前按键的连续16ms的状态进行判断,而不再关心它在这16ms内都做了什么事情。

sys.h

#ifndef __SYS_H__
#define __SYS_H__


//头文件包含
#include <STC15F2K60S2.H>
#include <intrins.h>

//管脚声明
sbit s4 = P3^3;
sbit s5 = P3^2;
sbit s6 = P3^1;
sbit s7 = P3^0;
//变量类型声明
typedef unsigned int uint;
typedef unsigned int u16;
typedef unsigned char uchar;
typedef unsigned char u8;


//外部变量声明

//函数声明
void ALL_Init();
void Operate_Delay(unsigned int ms);
void Timer0Init(void);
//key.c
void Key_Scan();
void Key_Drive();
void Key_Action(u8 dat);

void Led_Iullme(u8 Led_dat);
#endif

key.c

#include "sys.h"

u8 KeySta[4] = {1,1,1,1};//当前按键状态
u8 KeyBackup[4] = {1,1,1,1};//按键状态备份,保存前一次的按键值
u8 Keycode[4] = {1,2,3,4};
u8 Led_dat = 0xff;
//独立键盘扫描函数 在定时器中调用 定时1ms
void Key_Scan()
{
		u8 i;
		static u8 KeyBuff[] = {0xff,0xff,0xff,0xff};
		KeyBuff[0] = (KeyBuff[0] << 1)|s4;
		KeyBuff[1] = (KeyBuff[1] << 1)|s5;
		KeyBuff[2] = (KeyBuff[2] << 1)|s6;
		KeyBuff[3] = (KeyBuff[3] << 1)|s7;
		for(i = 0; i<4; i++)
		{
				if(KeyBuff[i] == 0xff)
				{
						KeySta[i] = 1;   //连续扫描8次都是1,16ms内都是弹起状态,按键已松开
				}else if(KeyBuff[i] == 0x00)
				{
						KeySta[i] = 0;	 //连续扫描8次都是0,16ms内都是按下状态,按键已按下
				}else
				{
					//其他状态键值不稳定,不作处理	
				}	
		}
}
//按键处理
void Key_Drive()
{
	u8 i;
	for(i = 0; i < 4; i++)
	{
			if(KeyBackup[i] != KeySta[i])
			{
					if(KeyBackup[i] != 0)
					{
							Key_Action(Keycode[i]);
					}
					KeyBackup[i] = KeySta[i];
			}
	}
}

void Key_Action(u8 dat)
{
		switch(dat)
		{
			case 1: Led_dat = 0xfe; break;
			case 2: Led_dat = 0xfd; break;
			case 3: Led_dat = 0xfb; break;
			case 4: Led_dat = 0xf7; break;
			default:break;
		}
		Led_Iullme(Led_dat);
}

led.c

#include "sys.h"

void Led_Iullme(u8 Led_dat)
{
	P2 = (P2&0X1F)|0X80;
	P0 = Led_dat;
}

main.c

#include "sys.h"

void main()
{
	ALL_Init();
	Timer0Init();
	while(1)
	{
		Key_Drive();
	}
}

3.矩阵按键

将开发板上面的J5跳线帽接到KBD端,也就是J5的1和2相接。

这里注意:在开发板上,根据转接板原理图,WR端接到单片机的P42管脚,RD端接到单片机的P44管脚。

我们知道,按键按下通常会保持100ms以上,如果在按键扫描中断中,每次让矩阵按键的一个KeyOut输出低电平其他三个输出高电平,判断当前所有Keyln的状态,下次中断时再让下一个KeyOut输出低电平,其他三个输出高电平.再次判断所有KeyIn,通过快速的中断不停地循环进行判断,就可以最终确定哪个按键按下了。

这个原理是不是跟数码管动态扫描有点类似?数码管在动态赋值,而按键在动态读取状态。至于扫描间隔时间和消抖时间,因为现在有4个KeyOut输出,要中断4次才能完成一次全部按键的扫描,显然2ms中断时间较长,这里我们使用1ms。

sys.h

#ifndef __SYS_H__
#define __SYS_H__


/***头文件包含***/
#include <STC15F2K60S2.H>
#include <intrins.h>

/***管脚声明***/
sbit KEY_IN_4 = P3^4;
sbit KEY_IN_3 = P3^5;
sbit KEY_IN_2 = P4^2;
sbit KEY_IN_1 = P4^4; 
/***变量类型声明***/
typedef unsigned int uint;
typedef unsigned int u16;
typedef unsigned char uchar;
typedef unsigned char u8;


/***外部变量声明***/

/***函数声明***/
//sys.c
void ALL_Init();
void Operate_Delay(unsigned int ms);
void Time0_Init();
//keyboard.c
void Key_Scan(void);
void Key_Drive();
void Key_Action(u8 Keycode);
//led.c
void paomaled(u8 num);
#endif

keyboard.c

/*******************************************************************************
* 文件名:keyboard.c
* 描  述:
* 作  者:guyao
* 版本号:v1.0
* 日  期: 2021年1月5日
* 备  注:矩阵键盘核心程序
*         
*******************************************************************************/
#include "sys.h"

u8 KeySta[4][4] =     {{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}};//当前按键状态缓存
u8 Key_backup[4][4] = {{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}};//按键状态备份,保存前一次的按键值
u8 Keycode[4][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};//键值
/**
*@brief     按键扫描函数 在定时器中断程序里调用 定时器间隔1ms  
*@param[in] none
*@return    none
**/
void Key_Scan(void)
{
	u8 i;
	static u8 keyout = 0;//矩阵按键的输出索引
	static u8 keybuff[4][4] = {{0xff,0xff,0xff,0xff,},
							   {0xff,0xff,0xff,0xff,},
							   {0xff,0xff,0xff,0xff,},
							   {0xff,0xff,0xff,0xff,}};//矩阵按键扫描缓冲区
	keybuff[keyout][0] = (keybuff[keyout][0] << 1) | KEY_IN_1;
	keybuff[keyout][1] = (keybuff[keyout][1] << 1) | KEY_IN_2;	
	keybuff[keyout][2] = (keybuff[keyout][2] << 1) | KEY_IN_3;
	keybuff[keyout][3] = (keybuff[keyout][3] << 1) | KEY_IN_4;	//将每一行的4个按键值移入缓冲区
								
	//消抖后更新按键状态
	for(i=0;i<4;i++)
	{
		if((keybuff[keyout][i]&0x0f) == 0x00)
		{						 //一个按键需要等待4ms才能被扫描到一次 需要扫描4次才能确定是否按键被按下
			KeySta[keyout][i] = 0; //连续4次扫描值都是0,即4*4ms内都是按下状态,认为按键已平稳按下
		}
		else if((keybuff[keyout][i]&0x0f) == 0x0f)
		{
			KeySta[keyout][i] = 1; //连续4次扫描值都是1,即4*4ms内都是松开状态,认为按键已稳定弹起
		}
	}
	//执行下一次的扫描输出
	keyout++;
	keyout = keyout&0x03;	//索引加到4就归0
	
	//根据索引 轮流拉低P3的P3^0 P3^1 P3^2 P3^3 IO口
	P3 = _crol_(0xfe,keyout)&0xff;
}

/**
*@brief     按键处理函数 检测按下的是哪个按键 计算出键值 需要在主循环中调用
*@param[in] none
*@return    返回键值
**/
void Key_Drive()
{
	u8 i,j;
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{                                           /*    键值      */
			if(Key_backup[i][j] != KeySta[i][j])  	/*1   2   3   4 */
			{										/*5   6   7   8 */		
				if(Key_backup != 0)				    /*9   10  11  12*/	
				{                                 	/*13  14  15  16*/ 
 					Key_Action(Keycode[i][j]);				
				}
				Key_backup[i][j] = KeySta[i][j];//更新前一次的键值
			}			
		}
	}
}
/**
*@brief     矩阵按键动作函数  矩阵键盘动作函数  根据键值执行相应程序 
*@param[in] 键值 keycode
*@return    none
**/
void Key_Action(u8 Keycode)
{
	paomaled(Keycode);
}

led.c

#include "sys.h"

void paomaled(u8 num)
{
	P2 = (P2&0x1f)|0x80;
	if(num == 0)
	{
		P0 = 0xff;
	}else
	{
		P0 = _crol_(0xfe,num-1);
	}
}

main.c

#include "sys.h"

void main()
{
	ALL_Init();
	Time0_Init();
	while(1)
	{
		Key_Drive();
	}
}	
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值