前言

使用了一种新的写法先单次扫描所有行列IO口,然后定时调用此函数,每次将此次IO口结果与上次对比,有变化则被视为按键按下或者释放。

代码

//此次使用的为三行四列,(相同IO口输出的为同一行,相同IO口输入的为同一列)

//键盘扫描函数需要先安装gpio.ko驱动模块
int gpioValuePrev[3][4] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};    //存储上次GPIO值
int key_scan(void)
{

	int gpioValue[3][4];    //存储本次GPIO值
	int gpioValueTemp1[3][4];    //临时存储GPIO值,用于防抖
	int gpioValueTemp2[3][4];
	int i, j;
	for(i = 0; i < 3; i++)
	{
		switch (i)
		{
			case 0:    //第一行
				GpioWriteBit(5, 30, 0);
				GpioWriteBit(5, 31, 1);
				GpioWriteBit(6, 0, 1);
				break;
			case 1:
				GpioWriteBit(5, 30, 1);
				GpioWriteBit(5, 31, 0);
				GpioWriteBit(6, 0, 1);
				break;
			case 2:
				GpioWriteBit(5, 30, 1);
				GpioWriteBit(5, 31, 1);
				GpioWriteBit(6, 0, 0);
				break;
			default:
				break;
		}
		/* 一个尝试
		//usleep(100);
		//COL_WRITE(0);
		//COL_WRITE(1);
		//COL_WRITE(2);
		//COL_WRITE(3);
		//COL_READ(0);
		//COL_READ(1);
		//COL_READ(2);
		//COL_READ(3);
		//usleep(500);
		*/
		delay_ms(15);
		gpioValueTemp1[i][0] = COL_READ(0);
		gpioValueTemp1[i][1] = COL_READ(1);
		gpioValueTemp1[i][2] = COL_READ(2);
		gpioValueTemp1[i][3] = COL_READ(3);
		delay_ms(1);
		gpioValueTemp2[i][0] = COL_READ(0);
		gpioValueTemp2[i][1] = COL_READ(1);
		gpioValueTemp2[i][2] = COL_READ(2);
		gpioValueTemp2[i][3] = COL_READ(3);

		for(j = 0; j < 4; j++)
		{
			if(gpioValueTemp1[i][j] == gpioValueTemp2[i][j])    //防抖判断
			{
				gpioValue[i][j] = gpioValueTemp1[i][j];
			}
			else
			{
				gpioValue[i][j] = gpioValuePrev[i][j];
			}
		}
	}

	for(i = 0; i < 3; i++)
	{
		for(j = 0; j < 4; j++)
		{
			if(gpioValue[i][j] != gpioValuePrev[i][j])    //与上次对比,判断按键变化状态
			{
				if(1 == gpioValuePrev[i][j])     //key down
				{
					(*event_function)((i * 4 + j) * 2 + 1);    //功能函数,传入按键值
				}
				else
				{
					(*event_function)((i * 4 + j) * 2 + 2);
				}
			}
		}
	}
	memcpy(gpioValuePrev, gpioValue, sizeof(int)* 3 * 4);
	return 0;
}

然后每10ms调用一次本函数。

问题

1 尾迹

当某按键,在其电平检测为0时恰好释放,此时该输入IO口相当于对外界空悬,会缓慢充电。
表现为,按下某一按键时,一定概率会错误显示其他异行同列按键被按下抬起。

尾迹图片

通过观察尾迹,原因就很明显:
我们是依次拉低某一行的IO口电平,此时若该行某按键被按下,此时该行IO就会与该按键对应列输入IO口短路,此时列输入IO口就会检测到低电平。

注意:IO口为输入状态时,空悬时大概有一个3V电压。
而3.3VIO口, 输入高电平低门限为2.3V ,输入低电平高门限为1.0V;

但是,由于尾迹的影响,低电平被延展得很长,当我们未按下另一行按键时,该输入IO口仍未空闲,会缓慢升压至3V,我们若在此时检测输入,明显会检测到低电平,就会误认为下一行同列按钮被按下,随着输入IO口缓慢升压至3V,又会误以为其被抬起。

尾迹延时精确测量

经过测量,尾迹的延时,在15ms时,可确定可跳过高电平低门限。因此,每次行扫描时,延时15ms即可解决。

2 异行同列按键之间无法同时按下

表现为,异行同列按键当同时按下时,会错误返回第一次按下的按键被抬起。。
原因很明显,当同时按下时,本该是低电平的IO口输入电压电压变高了。

单个按键被按下

异行同列两个按键同时按下

原因是,我们是分时扫描不同行,而同一时刻,不同行的输出IO口电平,应该有一个0,其他为1。当我们同时按下两个按键,则两个输出IO口(一个为0,一个为1)与输入IO口被同时短接了,因此不0不1,“高不成,低不就”。
因此,异行同列按键同时被按下是不可行的。

感悟

矩阵键盘,以前一直以为很简单。但是若深究其用法,想要实现检测多个按键等功能,其中的问题细节也很丰富。
学不可以已。
学者不可以不深思,而慎取之也