DIY一把属于自己的机械键盘(四)

4 篇文章 1 订阅
4 篇文章 0 订阅

软件篇(一)—— 按键扫描


  回顾一下硬件篇矩阵键盘部分,为了方便软件扫描、提高扫描效率,我在每一行增加了一个上拉电阻,如图:
在这里插入图片描述
  首先分析一下原理,经过电阻上拉,每一行的初始状态为高电平,那么要检测到按键事件对应行 K_ROWx 就应该出现低电平或下降沿才能检测。从图中可以看出产生低电平或下降沿由 COLx 控制,对应的只有软件逐列拉低 COLx 即可。这样一来只需要循环拉低 COLx,获取 K_ROWx 状态,扫描一次即可实现键值读取。例如,拉低 COL3,此时 K_ROW4 检测到下降沿,说明坐标 (4,3) 的按键被按下。
  为了进一步提高扫描速率,我把 K_ROWx 和 COLx 分别接入一组 IO 口,这样就可以通过寄存器移位实现按键扫描,提升效率。弄清原理之后,写代码就轻松多了。下面看一下代码:

  1. 相关定义
#define         COL_NUM         	14
#define         ROW_NUM        	 	5

#define         KEY_PRESSED         0
#define         KEY_UNPRESSED       1

#define         KEY_ROW_GPIO_CLK                RCC_APB2Periph_GPIOA
#define         KEY_ROW_GPIO_APBxClkCmd         RCC_APB2PeriphClockCmd
#define         KEY_ROW_PORT                    GPIOA
#define         KEY_ROW_PIN                     0x001F

#define         KEY_COL_GPIO_CLK                RCC_APB2Periph_GPIOB
#define         KEY_COL_GPIO_APBxClkCmd         RCC_APB2PeriphClockCmd
#define         KEY_COL_PORT                    GPIOB
#define         KEY_COL_PIN                     0x3FFF

#define         KEY_ROW_READ                    GPIOA->IDR & 0x001F       // 取低5位
#define         KEY_COL_OUT                     GPIOB->ODR

const u16 COL_Scan_Table[COL_NUM] = {
0x3FFE, 0x3FFD, 0x3FFB, 0x3FF7,0x3FEF, 0x3FDF, 0x3FBF, 
0x3F7F, 0x3EFF, 0x3DFF, 0x3BFF, 0x37FF,0x2FFF, 0x1FFF,
};

u8 KeyBoard_KeyStatus_New[5][14] = {KEY_UNPRESSED};
  1. IO初始化
void Key_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    KEY_ROW_GPIO_APBxClkCmd(KEY_ROW_GPIO_CLK, ENABLE);
    KEY_COL_GPIO_APBxClkCmd(KEY_COL_GPIO_CLK, ENABLE);
    
    /* 这里用到了JTAG接口的IO,需要重定义 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
    
    /* 行输入,外部上拉,低电平有效 */
    GPIO_InitStructure.GPIO_Pin = KEY_ROW_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(KEY_ROW_PORT, &GPIO_InitStructure); 
    
    /* 列输出,低电平有效 */
    GPIO_InitStructure.GPIO_Pin = KEY_COL_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(KEY_COL_PORT, &GPIO_InitStructure); 
    
    KEY_COL_OUT = 0x3FFF;   // 全部列拉高
}

  这里重点说明一下列输出 IO 的配置。首先讨论一下推挽输出的情况:当按下某一行的一个按键,此时该行电平被拉低,能够正常检测。但当这一行同时按下两个及以上按键时,由于列扫描是逐列移位拉低(即同一时刻有且只有一列为低电平),那么其他列如果也按下去的话,该行就会被多次上拉,导致无法完全拉低该行电平,进而导致无法识别同一行的多个按键。
  该如何解决这个问题,从上面的分析可以得出同一行多个按键无法识别的根本原因是其他列的高电平导致了电平无法完全拉低。那么如果其他列也保持低电平呢? 显然是不行的,这样就无法检测到这一行到底是哪个按键被按下。因此,可以明确一点,同一时刻有且只能有一列为低电平。
  那么除了高、低电平两个状态还有什么? 就是高阻态。将 IO 配置为开漏输出,就能保证同一时刻有且只有一列为低电平,同时其他列为高阻态,不会影响按键行电平。

  1. 按键扫描
void Key_Scan(void)
{
    u8 i,j;
    u8 row_data;
    
    for(i=0;i<COL_NUM;i++)
    {
        KEY_COL_OUT = COL_Scan_Table[i];  // 列输出
        Delay_us(10);
        
        row_data = KEY_ROW_READ; // 获取行按键状态
        for(j=0;j<ROW_NUM;j++)
        {
            KeyBoard_KeyStatus_New[j][i] = row_data & 0x01; // 获取按键状态
            row_data = row_data >> 1; // 行切换
        }
    }
}

  程序中 i 的值为当前列,j 为当前行。每切换一次列输出读一组行,再将读出的行值按位更新到数组中。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值