最近做了一个项目,有个部分用到了矩阵扫描的原理,要检测按键是否按下去执行一个动作。一开始硬件工程师的原理图实现是设计成传统矩阵按键方式,大致原理如下所示: 这种硬件实现方式当去检测单个按下、一整行按下或一整列按下时是没有问题,但是我们同时按下s0、s1和s4的时候,问题就来了。 当我们按下s0和s1的时候相应的第一行就会导通,结合列的上拉电阻便可检测出s0和s1按键被按下。但是!!!当我们s4按下时第二行也被导通了,并且随着s1按键的按下,第二列也是被导通。所以当我们按下s0、s1和s4按键时,s5按键就会被误检测成被按下,所以这是错误的。经过不懈的努力和查阅相关资料,对以上的电路进行了修改。 在每一个按键前面加上一个二极管进行阻隔,使得各按键不受列电流的影响,有了电路原理,代码按键扫描检测就简单多了。 先对按键GPIO进行定义,方便后续编写代码。
#define KEY_ROW_APB2PERIPH (RCC_APB2Periph_GPIOA)
#define KEY_ROW_PORT GPIOA
#define KEY_ROW1_PIN GPIO_Pin_10
#define KEY_ROW2_PIN GPIO_Pin_11
#define KEY_ROW3_PIN GPIO_Pin_12
#define KEY_ROW4_PIN GPIO_Pin_15
#define KEY_COL_APB2PERIPH (RCC_APB2Periph_GPIOB)
#define KEY_COL_PORT GPIOB
#define KEY_COL1_PIN GPIO_Pin_3
#define KEY_COL2_PIN GPIO_Pin_4
#define KEY_COL3_PIN GPIO_Pin_5
#define KEY_COL4_PIN GPIO_Pin_6
typedef struct gpio_group {
GPIO_TypeDef *port;
uint16_t pin;
} GPIO_GROUP;
检测流程大致思路:将行的四个引脚作为输出引脚设置成开漏输出,列引脚直接设置成浮空状态。将输出引脚轮流拉低去再去检测每个按键输入引脚的状态。就是说,先将ROW1输出为0,然后当ROW1的某个按键被按下时,浮空引脚便会跟着被拉低就可以检测到输入引脚的高低电平。
static GPIO_GROUP GPIO_LKEY_ROW[4] = {
{KEY_ROW_PORT, KEY_ROW1_PIN},
{KEY_ROW_PORT, KEY_ROW2_PIN},
{KEY_ROW_PORT, KEY_ROW3_PIN},
{KEY_ROW_PORT, KEY_ROW4_PIN},
};
static GPIO_GROUP GPIO_KEY_COL[4] = {
{KEY_COL_PORT, KEY_COL1_PIN},
{KEY_COL_PORT, KEY_COL2_PIN},
{KEY_COL_PORT, KEY_COL3_PIN},
{KEY_COL_PORT, KEY_COL4_PIN},
};
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(KEY_ROW_APB2PERIPH,ENABLE);
GPIO_InitStructure.GPIO_Pin = KEY_ROW1_PIN | KEY_ROW2_PIN |
KEY_ROW3_PIN | KEY_ROW4_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(KEY_ROW_PORT, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(KEY_COL_APB2PERIPH,ENABLE);
GPIO_InitStructure.GPIO_Pin = KEY_COL1_PIN | KEY_COL2_PIN|
KEY_COL3_PIN | KEY_COL4_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(KEY_COL_PORT, &GPIO_InitStructure);
}
static void activate_all_row(uint8_t status)
{
int row;
for (row = 0; row < 4; row++)
{
if (status)
{
GPIO_SetBits(GPIO_LKEY_ROW[row].port, GPIO_LKEY_ROW[row].pin);
}
else
{
GPIO_ResetBits(GPIO_LKEY_ROW[row].port, GPIO_LKEY_ROW[row].pin);
}
}
}
static void activate_row(uint8_t row, uint8_t status)
{
if (status)
{
GPIO_SetBits(GPIO_LKEY_ROW[row].port, GPIO_LKEY_ROW[row].pin);
}
else
{
GPIO_ResetBits(GPIO_LKEY_ROW[row].port, GPIO_LKEY_ROW[row].pin);
}
}
void KEY_Scan(void)
{
uint8_t row, col,keyScan[4][4]={0};
activate_all_row(1)
for(row = 0;row < 4; row ++)
{
activate_row(row, 0);
delay_US(1);
for(col = 0;col < 4;col ++)
{
if(GPIO_ReadInputDataBit(GPIO_KEY_COL[col].port,GPIO_KEY_COL[col].pin) == 0)
keyScan[row][col] = 1;
}
activate_row(row, 1);
}
activate_all_row(1)
for(row = 0;row < 4;row ++)
for(col = 0;col < 4;col ++)
{
if(keyScan[row][col] != 0)
printf("第%d个按键被按下\r\n",(row+1)*(col+1));
}
}
以上就是矩阵按键扫描的整个思路流程,代码因为是博客上临时修改写的,不知道能不能编译通过,但是思路就是这么个思路,如果有大佬有更好的解决方案也可一起讨论。
|