1、矩阵按键
通过将4x4矩阵按键的每一行依次设为低电平,同时保持其它行为高电平,然后读取所有列的电平状态,可以检测到哪个按键被按下。如果某列变为低电平,说明对应行和列的按键被按下。这样逐行扫描即可确定按键的位置。
2、代码片段分析
/*********************************************************************
@Function : 矩阵键盘行列读写操作
@Parameter : ReadIo :读输入的IO
WirteIo :写输出的IO
@Return : 行列IO输出状态
**********************************************************************/
uint8_t GPIO_KEY_RW(uint16_t ReadIo,uint16_t WirteIo)
{
uint16_t Wdata=0,Rdata=0;
/* 写操作 */
KeyBordSetOut(KEY_ALL); //设置IO
if(WirteIo==0x0f00)
GPIO_SetBits(GPIOE,KEY_LINE); //写行
else
GPIO_ResetBits(GPIOE,KEY_LIST); //写列
Wdata = GPIO_ReadOutputData(GPIOE);//读输出
Wdata &= WirteIo; //取有效区域
/* 读操作 */
KeyBordSetIn(ReadIo); //设置IO
Rdata = GPIO_ReadInputData(GPIOE); //读输入
Rdata &= ReadIo; //取有效区域
/* 状态返回 */
Rdata |= Wdata; //合并两次读取的数据
return (uint8_t)(Rdata>>8); //移位返回
}
初始化:
Wdata 和 Rdata 初始化为 0。
写操作:
检查 WirteIo 是否为 0x0f00(二进制:0000111100000000):
如果是,则设置 GPIOE 中对应 KEY_LINE 的位(具体位取决于 KEY_LINE 的定义)。
如果不是,则复位 GPIOE 中对应 KEY_LIST 的位(具体位取决于 KEY_LIST 的定义)。
读取 GPIOE 的输出数据到 Wdata。
Wdata 与 WirteIo 进行与操作,保留有效区域的数据。
读操作:
使用 ReadIo 设置IO方向。
读取 GPIOE 的输入数据到 Rdata。
Rdata 与 ReadIo 进行与操作,保留有效区域的数据。
状态返回:
将 Wdata 和 Rdata 进行或操作,合并两次读取的数据。
返回 Rdata 右移8位后的值。
假设按下第一个按键
假设按下的是第一个按键,具体的步骤如下:
初始化:
Wdata = 0
Rdata = 0
写操作:
WirteIo == 0x0f00 假设为真,则设置 GPIOE 对应 KEY_LINE 的位。
读取 GPIOE 输出数据:
假设 GPIOE 输出数据为 0000111100000000(二进制)。
Wdata &= WirteIo:
Wdata = 0000111100000000 & 0000111100000000 = 0000111100000000
读操作:
设置IO方向,具体操作取决于 KeyBordSetIn 函数。
读取 GPIOE 输入数据:
假设按下第一个按键时,GPIOE 输入数据为 0000000100000000(二进制)。
Rdata &= ReadIo:
Rdata = 0000000100000000 & ReadIo(假设 ReadIo 为 0000000100000000),结果 Rdata = 0000000100000000
状态返回:
Rdata |= Wdata:
Rdata = 0000000100000000 | 0000111100000000 = 0000111100000000
返回值:
将 Rdata 右移8位后返回:Rdata >> 8 = 00001111
所以函数返回值为 0x0F(二进制:00001111)
/*********************************************************************
@Function : 矩阵键盘键值扫描
@Parameter : N/A
@Return : 键值
**********************************************************************/
uint8_t KeyBoardScan(void)
{
uint8_t KeyValue=0,Key=0;
uint8_t a = 0;
/* 检测键盘是否有按键按下,0x0f表示所有列都未按下 */
if(GPIO_KEY_RW(KEY_LIST,KEY_LINE)!=0x0f)
{
/* 测试列状态 */
Key = GPIO_KEY_RW(KEY_LIST,KEY_LINE); // 读取列的状态
/* 判断列状态并映射为按键值 */
switch(Key)
{
case(0x1F): // 第一列所有行都按下
KeyValue = 1; // 对应键值为1
break;
case(0x2F): // 第二列所有行都按下
KeyValue = 2; // 对应键值为2
break;
case(0x4F): // 第三列所有行都按下
KeyValue = 3; // 对应键值为3
break;
case(0x8F): // 第四列所有行都按下
KeyValue = 4; // 对应键值为4
break;
}
/* 测试行状态 */
Key = GPIO_KEY_RW(KEY_LINE,KEY_LIST); // 读取行的状态
/* 判断行状态并映射为按键值 */
switch(Key)
{
case(0x0E): // 第一行所有列都按下
KeyValue = KeyValue; // 保持当前列的键值不变
break;
case(0x0D): // 第二行所有列都按下
KeyValue = KeyValue + 4; // 当前列的键值加4,对应第二行
break;
case(0x0B): // 第三行所有列都按下
KeyValue = KeyValue + 8; // 当前列的键值加8,对应第三行
break;
case(0x07): // 第四行所有列都按下
KeyValue = KeyValue + 12; // 当前列的键值加12,对应第四行
break;
}
/* 按键松手检测 */
while((a < 50) && (Key != 0x00)) // 循环检测按键是否松手,最多检测50次
{
delay_ms(5); // 延时5毫秒
Key = GPIO_KEY_RW(KEY_LINE,KEY_LIST); // 再次读取行的状态
a += 1; // 计数器加1
}
}
/* 返回键值 */
return KeyValue;
}