【星闪】Hi2821 | KEYSCAN矩阵按键扫描
1. 简介
在键盘相关的应用中,通常需要驱动数十个按键,如果每个按键使用一个管脚来驱动那就太浪费了,而且一般也没有那么多管脚可用,因此使用矩阵按键会是一个不错的选择。
上面是一个 2×4 的键盘矩阵,一共 8 个按键,但仅需 6 根管脚即可驱动。
一般来说,矩阵键盘采用行扫描的方式,即行方向的管脚配置为输出,列方向的管脚配置为输入。空闲时,行输出高电平。扫描时,行依次拉低电平,每次拉低后读取列管脚的值,如果某一列的按键被按下,那么该按键列管脚将会读到低电平;没按下即保持高电平。
根据管脚模式和上下拉电阻的配置,矩阵键盘会有 4 种驱动方式:
行扫描 | 列扫描 | |
---|---|---|
上拉电阻 | 行输出,空闲高电平,列输入接上拉,按下时低电平 | 列输出,空闲高电平,行输入接上拉,按下时低电平 |
下拉电阻 | 行输出,空闲低电平,列输入接上拉,按下时高电平 | 列输出,空闲低电平,行输入接下拉,按下时高电平 |
2. 硬件特性
Hi2821 在硬件上原生支持矩阵键盘,最大支持 16×8 的矩阵键盘,支持芯片算法防鬼键,支持 IO 端口可旁路的防抖设计。
3. 例程
例程将以上面的 2×4 矩阵键盘为例进行驱动。
3.1 Kconfig
比较重点的配置,第一行这个用来设置同时能上报多少个按键,默认为 6 个。KEYSCAN use software to detect ghost key:软件防鬼键。KEYSCAN avoids the tiny jitter:软件防抖。Select keyscan type:一般选择最后一个用户自定义。number of columns to start:列数。number of rows to start:行数。
3.2 代码
#include "key_scan.h"#include <stdint.h>
#include <string.h>#include "keyscan.h"
#include "soc_osal.h"/* 键位映射(先行再列) */
static uint8_t g_key_gpio_map[CONFIG_KEYSCAN_ENABLE_ROW + CONFIG_KEYSCAN_ENABLE_COL] = {11, 12, 13, 14
};
static uint8_t g_key_map[CONFIG_KEYSCAN_ENABLE_ROW][CONFIG_KEYSCAN_ENABLE_COL] = {{ 0x01, 0x02 },{ 0x03, 0x04 }
};static int key_scan_report_callback(int key_nums, uint8_t key_values[])
{osal_printk("keys: ");for (int i = 0; i < key_nums; i++) {osal_printk("%02X ", key_values[i]);}osal_printk("\r\n");return 0;
}int key_scan_init(void)
{int ret = ERRCODE_SUCC;ret = keyscan_porting_set_gpio(g_key_gpio_map);if (ret) {osal_printk("set key gpio failed\r\n");return ret;}ret = uapi_set_keyscan_value_map((uint8_t **) g_key_map, CONFIG_KEYSCAN_ENABLE_ROW, CONFIG_KEYSCAN_ENABLE_COL);if (ret != ERRCODE_SUCC) {osal_printk("set key map failed\r\n");return ret;}uapi_keyscan_init(EVERY_ROW_PULSE_40_US, HAL_KEYSCAN_MODE_0, KEYSCAN_INT_VALUE_RDY);ret = uapi_keyscan_register_callback(key_scan_report_callback);if (ret != ERRCODE_SUCC) {osal_printk("set irq callback failed\r\n");return ret;}if (uapi_keyscan_enable() != ERRCODE_KEYSCAN_POWER_ON) {osal_printk("key scan enable start failed\r\n");return -1;}return ret;
}
1. 注册按键管脚
如果使用的是用户自定义按键模式,那么就需要先调用 keyscan_porting_set_gpio 函数去注册管脚,传入一个数组,行管脚在前,列管脚在后。
2. 注册按键映射
调用 uapi_set_keyscan_value_map 函数,传入一个二维数组、行数和列数,按键上报时会根据该数组的键值进行上报。
3. 初始化矩阵键盘
调用 uapi_keyscan_init 函数,参数一为扫描时长,可选值如下:
typedef enum {EVERY_ROW_PULSE_10_US, /*!< KEYSCAN every row pulse 10 us */EVERY_ROW_PULSE_20_US, /*!< KEYSCAN every row pulse 20 us */EVERY_ROW_PULSE_30_US, /*!< KEYSCAN every row pulse 30 us */EVERY_ROW_PULSE_40_US /*!< KEYSCAN every row pulse 40 us */
} keyscan_pulse_time_t;
参数二为扫描模式,可选值如下:
typedef enum {HAL_KEYSCAN_MODE_0, /*!< Input=0 Output=1 Pull down Output while scanning, scan high level */HAL_KEYSCAN_MODE_1, /*!< Input=1 Output=0 Pull up Output while scanning, scan low level */HAL_KEYSCAN_MODE_2, /*!< Input=0 Output=1 Pull up Input while scanning, scan low level */HAL_KEYSCAN_MODE_3 /*!< Input=1 Output=0 Pull down Input while scanning, scan high level */
} keyscan_mode_t;
参数三为回调触发事件,可选值如下:
typedef enum {KEYSCAN_INT_FIFO_FULL, /*!< Fifo full interrupt */KEYSCAN_INT_PRESS, /*!< Button press interrupt */KEYSCAN_INT_RELEASE, /*!< Key release interrupt */KEYSCAN_INT_VALUE_RDY, /*!< Key value report interrupt */KEYSCAN_INT_STOPPED, /*!< Stop interrupt interrupt */KEYSCAN_INT_SCAN_ONE, /*!< Completes a scan interrupt */KEYSCAN_INT_AFIFO_FULL, /*!< Fifo will be full interrupt */KEYSCAN_INT_PRESS_AON, /*!< Keyboard matrix key interrupt */KEYSCAN_INT_MAX_NUM, /*!< KEYSCAN num */
} keyscan_int_t;
4. 注册回调函数
调用 uapi_keyscan_register_callback 函数注册,回调中会返回扫描到的键值。
5. 使能键值按键
调用 uapi_keyscan_enable 函数使能。