国产MCU兆易GD32实现矩阵按键扫描
一、矩阵键盘
?? ?为了减少I/O口的占用,通常将按键排列成矩阵形式。在矩阵式键盘中,每条水平线和垂直线在交叉处不直接连通,而是通过一个按键加以连接。使用8个io口来进行16个按键的控制读取,可以减小io口的使用,用4条I/O线作为行线,4条I/O线作为列线组成的键盘。矩阵键盘检测方法主要有两种,一种是逐行扫描、一种是行列扫描。
1、逐行扫描
?? ?通过在矩阵按键的每一条行线上轮流输出低(高)电平,检测矩阵按键的列线,当检测到的列线不全为高(低)电平的时候,说明有按键按下。然后,根据当前输出低电平的行号和检测到低电平的列号组合,判断是哪一个按键被按下。
2、行列扫描
?? ?首先,在全部行线上输出低电平,检测矩阵按键的列线,当检测到的列线不全为高电平的时候,说明有按键按下,并判断是哪一列有按键按下。然后,反过来,在全部列线上输出低电平,检测矩阵按键的行线,当检测到的行线不全为高电平的时候,说明有按键按下,并判断是哪一行有按键按下。最后,根据检测到的行号和检测的列号组合,以判断是哪一个按键被按下。
二、程序设计
实现效果:逐行扫描矩阵键盘并打印出键值。
思路:保持一列输出高,重复扫描行。
key.c
#include "key.h"
uint8_t i=0,j=0;//行、列号
/************************************************
*@Function :key_Init
*@brief :按键GPIO初始化函数
*@param :void
*@retval : void
*************************************************/
void Key_Init(void)
{
//使能GPIO时钟
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
//四列 PB1 PB2 PB10 PB11 作为输出
gpio_init(GPIOB,GPIO_MODE_OUT_PP,GPIO_OSPEED_10MHZ,GPIO_PIN_1);
gpio_init(GPIOB,GPIO_MODE_OUT_PP,GPIO_OSPEED_10MHZ,GPIO_PIN_2);
gpio_init(GPIOB,GPIO_MODE_OUT_PP,GPIO_OSPEED_10MHZ,GPIO_PIN_10);
gpio_init(GPIOB,GPIO_MODE_OUT_PP,GPIO_OSPEED_10MHZ,GPIO_PIN_11);
//四行 PA3 PA4 PA5 PA6 下拉输入 默认为低电平
gpio_init(GPIOA,GPIO_MODE_IPD,GPIO_OSPEED_10MHZ,GPIO_PIN_3);
gpio_init(GPIOA,GPIO_MODE_IPD,GPIO_OSPEED_10MHZ,GPIO_PIN_4);
gpio_init(GPIOA,GPIO_MODE_IPD,GPIO_OSPEED_10MHZ,GPIO_PIN_5);
gpio_init(GPIOA,GPIO_MODE_IPD,GPIO_OSPEED_10MHZ,GPIO_PIN_6);
//列初始化为低电平
gpio_bit_reset(GPIOB, GPIO_PIN_1);
gpio_bit_reset(GPIOB, GPIO_PIN_2);
gpio_bit_reset(GPIOB, GPIO_PIN_10);
gpio_bit_reset(GPIOB, GPIO_PIN_11);
}
/************************************************
*@Function :Rank1_Scan(void)
*@brief :第一行扫描函数
*@param :void
*@retval : 0(可以返回行号i)
*************************************************/
uint8_t Rank1_Scan(void)
{
//第一列输出高,其它三列输出低
gpio_bit_set(GPIOB, GPIO_PIN_1);
gpio_bit_reset(GPIOB, GPIO_PIN_2);
gpio_bit_reset(GPIOB, GPIO_PIN_10);
gpio_bit_reset(GPIOB, GPIO_PIN_11);
//检测第一行按键状态,为高则按下
if(gpio_input_bit_get(GPIOA, GPIO_PIN_3)==1)
{
delay_1ms(10);//消抖
while(gpio_input_bit_get(GPIOA, GPIO_PIN_3)==1)
delay_1ms(10);
printf("KeyNumber:%d\r\n",KEY1);
}
//检测第二行按键状态,为高则按下
if(gpio_input_bit_get(GPIOA, GPIO_PIN_4)==1)
{
delay_1ms(10);;
while(gpio_input_bit_get(GPIOA, GPIO_PIN_4)==1)
delay_1ms(10);;
printf("KeyNumber:%d\r\n",KEY5);
}
//检测第三行按键状态
if(gpio_input_bit_get(GPIOA, GPIO_PIN_5)==1)
{
delay_1ms(10);;
while(gpio_input_bit_get(GPIOA, GPIO_PIN_5)==1)
delay_1ms(10);
printf("KeyNumber:%d\r\n",KEY9);
}
//检测第四行按键状态
if(gpio_input_bit_get(GPIOA, GPIO_PIN_6)==1)
{
delay_1ms(10);
while(gpio_input_bit_get(GPIOA, GPIO_PIN_6)==1)
delay_1ms(10);
printf("KeyNumber:%d\r\n",KEY13);
}
gpio_bit_reset(GPIOB, GPIO_PIN_1);//将第一列拉低回原状态
return 0;
}
主函数
#include "gd32f10x.h"
#include "gd32f103c_eval.h"
#include "systick.h"
#include "key.h"
#include "usart.h"
int main(void)
{
systick_config();//系统时钟
//USART相关配置
Usart_Init();
Key_Init();
printf("Init OK!\r\n");
while(1)
{
Rank1_Scan();
}
}
以上是行扫描实现思路,剩下的按键只需将剩下的三列依次保持一列输出高,重复扫描行即可。
三、实验现象
实际测试均能准确打印出键值。
四、程序优化
?? ? 行扫描程序有大量重复代码,可以使用循环语句嵌套条件选择语句将四列依次置高,这样就只需要一段通用的行扫描语句,降低重复率。