蓝桥杯——定时扫描矩阵键盘

一、原理部分

蓝桥杯——定时扫描矩阵键盘

注:P36转P42,P37转P44,跳线帽连接1和2

按键状态扫描

按键有两个状态,一个是弹起态,一个是按下态。通过判断按键的状态持续的时间来确定按键是否按下或者弹起。设置一定的时间扫描一次按键的状态,如果按键的状态和之前的状态不一样就说明按键发生了状态的改变,过一段时间后,按键状态再次发生改变,则说明按键完成了一个按下又弹起的操作,此时就可以执行此按键对应的功能。用keysta[4][4]来储存按键的当前,用keyback[4][4]来储存按键上一个状态。通过扫描的方式判断keysta[i][j] != keyback[i][j],如果不相等则说明按键的状态发生了改变,此时再次判断keysta[i][j] != 0,若不为零则说明按键弹起,即完成了一次按下又弹起的操作,即可执行对应的按键功能。注意,在判断完按键状态发生改变后应当把keysta的值赋值给keyback方便下一次的判断即keyback[i][j] = keysta[i][j];

unsigned char keysta[4][4] = {{1, 1, 1, 1},      //4*4矩阵按键的状态,1为抬起,0为按下
					  		  {1, 1, 1, 1},
					 		  {1, 1, 1, 1},
							  {1, 1, 1, 1}};
							  
static unsigned char keyback[4][4] = {{1, 1, 1, 1},     
								     {1, 1, 1, 1},
								     {1, 1, 1, 1},
								     {1, 1, 1, 1}};

行扫描和列扫描获取键值

定义数组keybuff[4][4]全部为0xff相当于是给矩阵按键初始态。使用变量keyout从0到3的循环进行行扫描,通过switch语句实现每一个时刻只有一行是低电平。即完成了行扫描。后面的四行即列扫描。当keyout行被拉低的时候,此时按键按下,则此时对应的有且仅有一个列扫描的IO口会变为低电平,把此时列扫描获得的低电平左移到keybuff对应的位置。
假设理想状态下(无抖动)按键按下了1ms,扫描间隔也为1ms时,则对应keybuff对应的值会变为0xfe。同理如果按键按下了8ms或者更长,扫描间隔还是1ms时,则对应的keybuff对应的值就会变为0x00。实际情况中,按键有抖动,但是抖动的时间比较短,只需要判断在连续的某一段时间内,按键的状态是持续按下的即可完成消抖的动作。
假设现在有一个按键按下,设置的扫描间隔为1ms,设置消抖的时间为8ms,那么就需要对keybuff中的每一个数字进行判断是否等于0x00即keybuff[keyout][i] == 0x00如果等于则对应的按键的确按下,如果不等于则说明按键没有按下。同理如果设置消抖时间为4ms则说明按键至少要按下4ms才是有效状态,即可以用语句(keybuff[keyout][i]&0x0f) == 0x00来进行判断。与上0x0f是为了清除高四位的影响,因为4ms只需要判断第四位是否为0即可,如果按键按下的时间多于4ms,单纯的判断keybuff[keyout][i] == 0xf0肯定是不行的。
确定按键按下后,即可以对keysta中对应的数字赋0,即说明按键按下。
同理判断按键弹起后,对keysta中对应的数字赋,即说明按键弹起。

sbit key_out_1 = P3^0;//行扫描
sbit key_out_2 = P3^1;
sbit key_out_3 = P3^2;
sbit key_out_4 = P3^3;

sbit key_in_1 = P4^4;//列扫描
sbit key_in_2 = P4^2;
sbit key_in_3 = P3^5;
sbit key_in_4 = P3^4;

void keyscan()
{
	uchar i;
	static uchar keyout = 0;
	static uchar keybuff[4][4] = {{0xff,0xff,0xff,0xff},
								  {0xff,0xff,0xff,0xff},
							  	  {0xff,0xff,0xff,0xff},
								  {0xff,0xff,0xff,0xff}};
	switch(keyout)
	{
		case 0:key_out_1 = 0;key_out_4 = 1;break;
		case 1:key_out_2 = 0;key_out_1 = 1;break;
		case 2:key_out_3 = 0;key_out_2 = 1;break;
		case 3:key_out_4 = 0;key_out_3 = 1;break;
	}

	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;

	for(i=0;i<4;i++)
	{
		if((keybuff[keyout][i]&0x0f) == 0x00)
		{
			keysta[keyout][i] = 0;
		}

		else if((keybuff[keyout][i]&0x0f) == 0x0f)
		{
			keysta[keyout][i] = 1;
		}
	}
   keyout++;
   keyout &=0x03;
}

二、代码部分

实验平台:CT107D
实验芯片:stc15f2k60s2
实验现象:按下对应按键分别显示1-16。
代码如下

#include<stc15f2k60s2.h>

#define uchar unsigned char
#define uint unsigned int

sbit buzz = P0^6;

sbit key_out_1 = P3^0;//行扫描
sbit key_out_2 = P3^1;
sbit key_out_3 = P3^2;
sbit key_out_4 = P3^3;

sbit key_in_1 = P4^4;//列扫描
sbit key_in_2 = P4^2;
sbit key_in_3 = P3^5;
sbit key_in_4 = P3^4;


uchar code duan[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00};//定义段码数组
uchar disbuff[8];//定义显示数字数字

uchar keysta[4][4] = {{1, 1, 1, 1},      //4*4矩阵按键的状态,1为抬起,0为按下
					  {1, 1, 1, 1},
					  {1, 1, 1, 1},
					  {1, 1, 1, 1}};

uchar keymap[4][4] = {{1,2,3,4},	   //按键值数组
					  {5,6,7,8},
					  {9,10,11,12},
					  {13,14,15,16}};

uchar index,num = 0; 
uchar count = 0;

void keyscan()//循环扫描按键
{
	uchar i;
	static uchar keyout = 0;
	static uchar keybuff[4][4] = {{0xff,0xff,0xff,0xff}, //按键初始态
								  {0xff,0xff,0xff,0xff},
							  	  {0xff,0xff,0xff,0xff},
								  {0xff,0xff,0xff,0xff}};
	switch(keyout)//行扫描,保证每一个时刻只有一行为低电平
	{
		case 0:key_out_1 = 0;key_out_4 = 1;break;
		case 1:key_out_2 = 0;key_out_1 = 1;break;
		case 2:key_out_3 = 0;key_out_2 = 1;break;
		case 3:key_out_4 = 0;key_out_3 = 1;break;
	}

	keybuff[keyout][0]=keybuff[keyout][0]<<1|key_in_1;//把读取到的行扫描的IO口电平移位到keybuff对应的数中
	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;

	for(i=0;i<4;i++)
	{
		if((keybuff[keyout][i]&0x0f) == 0x00)//中断为1ms,此处为4ms消抖,判断按键是否按下
		{
			keysta[keyout][i] = 0;
		}

		else if((keybuff[keyout][i]&0x0f) == 0x0f)//中断为1ms,此处为4ms消抖,判断按键是否弹起
		{
			keysta[keyout][i] = 1;
		}
	}
   keyout++;
   keyout &=0x03;//对keyout进行清零
}

void keyfun(uchar key_val)//按键功能函数,可以用switch语句选择
{
	num = key_val;
}
void keydrive()//放在主函数不断的判断按键状态是否改变
{
	uchar i,j;
	static uchar keyback[4][4] = {{1, 1, 1, 1},//备用状态数组     
								  {1, 1, 1, 1},
								  {1, 1, 1, 1},
								  {1, 1, 1, 1}};
	for(i=0;i<4;i++)
		for(j=0;j<4;j++)
		{
			   if(keysta[i][j] != keyback[i][j])
			   {
			   		if(keysta[i][j] != 0)
					{
						keyfun(keymap[i][j]);//获取按键值传递给keyfun
					}
			   } keyback[i][j] = keysta[i][j]; //更新状态,方便下一次判断
		}
}


void closebuzz()
{
	P2 = 0xa0;
	buzz = 0;
	P2 = 0x00;
}


void shownum()//显示数字函数
{
	disbuff[0]=num/10;
	disbuff[1]=num%10;
	disbuff[2]=10;
	disbuff[3]=10;
	disbuff[4]=10;
	disbuff[5]=10;
	disbuff[6]=10;
	disbuff[7]=10;
}

void display()//数码管扫描函数
{
	P2 = 0xe0;
	P0 = 0xff;
	P2 = 0x00;

	P2 = 0xc0;
	P0 = 1<<index;
	P2 = 0x00;

	P2 = 0xe0;
	P0 = ~duan[disbuff[index]];
	P2 = 0x00;

	index++;
	index &= 0x07;

}

void Timer0Init(void)		//1毫秒@11.0592MHz
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x9A;		//设置定时初值
	TH0 = 0xA9;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;
	EA = 1 ; 
}

void time0() interrupt 1
{
	keyscan();
	display();		
}


void main()
{
	closebuzz();
	Timer0Init();
	while(1)
	{
		shownum();
		keydrive();
	}

}


蓝桥杯——定时扫描矩阵键盘蓝桥杯——定时扫描矩阵键盘 天地神仙 发布了10 篇原创文章 · 获赞 11 · 访问量 3631 私信 关注
上一篇:Java> 有趣的byte,byte位运算


下一篇:用python opencv 调用摄像头之if cv2.waitKey(1) & 0xFF == ord('q')分析