按键扫描(KEYSCAN)

概述

按键扫描模块支持矩阵式行列式 2 种扫描模式。用户可根据实际需求对按键的行列数量、消抖时间、中断类型进行配置。在不同的扫描模式下,单按键和组合按键有一定的使用条件,详见按键功能设计

主要特性

  • 支持最大的矩阵式按键为 12 X 20,支持最大的行列式按键为 31 X 31,模式可配置

  • 按键扫描时钟可配置 (10kzh - 128khz)

  • 按键扫描时间间隔可配置

  • 按键消抖时间可配置

  • 按键结束超时时间可配置

  • 按键FIFO阈值可配置

  • 按键中断类型使能可配置

  • 按键行列的使用数量可配置

功能设计

矩阵式

12 X 20 矩阵式原理图

使用要求

  • 行列对应关系:ROW1 ~ ROW12 对应 P0 ~ P11COL1 ~ COL20 对应 P12 ~ P31

  • 所有按键可以作为单按键使用;

  • 组合按键不能形成直角三角形,否则会出现幻影键;

行列式

31 X 31 行列式原理图

使用要求

  • 行列对应关系:ROW0 ~ ROW31 对应 P0 ~ P31COL1~ COL31 对应 P0 ~ P30

  • COL1 列的每个按键都可以作为单按键使用,其他按键不能够作为单按键使用,必须与 COL1 的任何一个按键一起组合使用,才能够被识别响应;

功能说明

KEYSCAN 设备数据结构

/**
 * @brief KEYSCAN设备句柄
 */
typedef struct {
    uint32_t regs;                            /*!< 基地址               */
    uint32_t frequency;                       /*!< keyscan时钟频率               */
    uint16_t wait_time;                       /*!< 扫描时间间隔, 单位(ms) */
    uint16_t deb_time;                        /*!< 消除抖动时间, 单位(ms) */
    uint32_t time_out;                        /*!< 超时时间, 单位(ms) */
    uint8_t fifo_threshold;                   /*!< fifo 阈值   */
    uint16_t fifo_th_timeout;                 /*!< fifo 阈值超时时间  , 单位(ms)   */
    uint8_t empty_frm_flag;                   /*!< 空帧不触发中断 , 如果设置不上报,在timeout时会一起上报,会影响按键单击和双击事件的延时触发       */
    GX_HAL_KEYSCAN_SCAN_MODE ksc_mode;       /*!< 扫描模式     */
    uint8_t row_num;                          /*!< 按键行数     */
    uint8_t col_num;                          /*!< 按键列数     */
    GX_HAL_KEYSCAN_CALLBACK cb;               /*!< 按键中断回调函数 >*/
} GX_HAL_KEYSCAN;

KEYSCAN 驱动配置初始化

矩阵式配置初始化

  1. 配置模式为矩阵模式 \ref GX_HAL_KEYSCAN_SCAN_MODE,配置基本按键时间参数,使用的行列数量等;

  2. 配置按键中断回调函数;

  3. 调用驱动初始化接口 \ref gx_hal_keyscan_init;

  4. 注册中断服务函数接口 \ref gx_request_irq;

  5. 使能模块驱动 gx_hal_keyscan_enable

代码配置示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
        .frequency = 32000,
        .wait_time = 50,
        .deb_time = 10,
        .time_out = 1000,
        .fifo_th_timeout = 200,
        .empty_frm_flag = 1,
        .fifo_threshold = 3,
        .ksc_mode = GX_HAL_KEYSCAN_SCAN_MODE_MATRIX,
        .row_num = 4,
        .col_num = 4,
        .cb = keyscan_process_keycode_callback;
    };

    static int keyscan_process_keycode_callback(void *pdata, unsigned int int_type)
    {
        uint32_t fifo_off = 0;
        uint32_t key_value = 0, end_flag = 0;

        GX_HAL_KEYSCAN *dev = (GX_HAL_KEYSCAN*)pdata;
        if(dev == NULL)
            return -1;

        if(int_type == GX_HAL_KEYSCAN_INT_STATUS_FIFO_NOT_EMPTY ||
        int_type == GX_HAL_KEYSCAN_INT_STATUS_FIFO_TH_OVERFLOW ||
        int_type == GX_HAL_KEYSCAN_INT_STATUS_FIFO_TH_TIME_OUT ||
        int_type == GX_HAL_KEYSCAN_INT_STATUS_TIMEOUT) {
            fifo_off = gx_hal_keyscan_get_fifo_data_num(dev);
            for(int i=0; i<fifo_off; i++) {
                key_value = gx_hal_keyscan_read_fifo_data(dev);
            }
        }
    }

    static int keyscan_isr(int irq, void *pdata)
    {
        gx_hal_keyscan_irq_handler(irq, &dev);
        return 0;
    }

    gx_hal_keyscan_init(&dev);
    gx_request_irq(IRQ_NUM_KEYSCAN, keyscan_isr, NULL);
    gx_hal_keyscan_enable(&dev);

行列式配置初始化

  1. 配置模式为行列模式 \ref GX_HAL_KEYSCAN_SCAN_MODE,配置基本按键时间参数,使用的行列数量等;

  2. 配置按键中断回调函数;

  3. 调用驱动初始化接口 \ref gx_hal_keyscan_init;

  4. 注册中断服务函数接口 \ref gx_request_irq;

  5. 使能模块驱动 gx_hal_keyscan_enable

代码配置示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
        .frequency = 32000,
        .wait_time = 50,
        .deb_time = 10,
        .time_out = 1000,
        .fifo_th_timeout = 200,
        .empty_frm_flag = 1,
        .fifo_threshold = 3,
        .ksc_mode = GX_HAL_KEYSCAN_SCAN_MODE_DETERMINANT,
        .row_num = 7,
        .col_num = 7,
        .cb = keyscan_process_keycode_callback;
    };

    static int keyscan_process_keycode_callback(void *pdata, unsigned int int_type)
    {
        uint32_t fifo_off = 0;
        uint32_t key_value = 0, end_flag = 0;

        GX_HAL_KEYSCAN *dev = (GX_HAL_KEYSCAN*)pdata;
        if(dev == NULL)
            return -1;

        if(int_type == GX_HAL_KEYSCAN_INT_STATUS_FIFO_NOT_EMPTY ||
        int_type == GX_HAL_KEYSCAN_INT_STATUS_FIFO_TH_OVERFLOW ||
        int_type == GX_HAL_KEYSCAN_INT_STATUS_FIFO_TH_TIME_OUT ||
        int_type == GX_HAL_KEYSCAN_INT_STATUS_TIMEOUT) {
            fifo_off = gx_hal_keyscan_get_fifo_data_num(dev);
            for(int i=0; i<fifo_off; i++) {
                key_value = gx_hal_keyscan_read_fifo_data(dev);
            }
        }
    }
    static int keyscan_isr(int irq, void *pdata)
    {
        gx_hal_keyscan_irq_handler(&dev);
        return 0;
    }

    gx_hal_keyscan_init(&dev);
    gx_request_irq(IRQ_NUM_KEYSCAN, keyscan_isr, NULL);
    gx_hal_keyscan_enable(&dev);

按键使用

配置完成后,可以进行按键操作,查看按键功能回调是否有对应键值上报。

接口使用说明

设置按键扫描时间间隔

通过 gx_hal_keyscan_set_wait_time 函数设置每轮按键扫描的时间间隔。代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    int time_ms = 50;
    gx_hal_keyscan_set_wait_time(&dev, time_ms);

备注

间隔越短,扫描键值越灵敏。

设置按键消抖时间

通过 gx_hal_keyscan_set_deb_time 函数设置每轮按键扫描的消抖时间,消除在按键按下和抬起瞬间产生的毛刺。代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    int time_ms = 10;
    gx_hal_keyscan_set_deb_time(&dev, time_ms);

重要

为避免被识别成键值,消抖时间要小于扫描时间间隔。

设置按键扫描结束超时时间

通过 gx_hal_keyscan_set_timeout 函数设置每轮按键扫描结束后的无键按下的超时时间,超时后认为一轮按键的结束。代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    int time_ms = 1000;
    gx_hal_keyscan_set_timeout(&dev, time_ms);

设置按键 FIFO 阈值

通过 gx_hal_keyscan_set_fifo_threshold 函数设置按键 FIFO 阈值,当按键键值数量超过阈值时会上报按键阈值中断。代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    int fifo_th = 8;
    gx_hal_keyscan_set_fifo_threshold(&dev, fifo_th);

设置按键 FIFO 阈值超时时间

通过 gx_hal_keyscan_set_fifo_th_timeout 函数设置按键 FIFO 阈值中断响应超时时间,当在该时间内按键阈值中断没有被响应时上报阈值超时中断。代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    int time_ms = 1000;
    gx_hal_keyscan_set_fifo_th_timeout(&dev, time_ms);

设置按键管脚使能

通过 gx_hal_keyscan_pin_enable 函数设置启用按键的 row 和 col 管脚数。代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    int row = 4; // 表示使用 row1 - row4
    int col = 4; // 表示使用 col1 - col4
    gx_hal_keyscan_pin_enable(&dev, row, col);

设置按键管脚失能

通过 gx_hal_keyscan_pin_disable 函数设置关闭按键的 row 和 col 管脚数。代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    int row = 4; // 表示使用 row1 - row4
    int col = 4; // 表示使用 col1 - col4
    gx_hal_keyscan_pin_disable(&dev, row, col);

设置按键中断使能

通过 gx_hal_keyscan_interrupt_enable 函数使能按键中断。有 5 种类型的中断 \ref GX_HAL_KEYSCAN_INT_TYPE:

  • 按键 FIFO 有值中断 1

  • 按键 FIFO 阈值中断 2

  • 按键 FIFO 阈值超时中断 3

  • 按键 FIFO 溢出中断 4

  • 按键超时中断 5

代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    GX_HAL_KEYSCAN_INT_TYPE type = GX_HAL_KEYSCAN_INT_STATUS_FIFO_NOT_EMPTY; /*!< FIFO 有值中断 1 */
    gx_hal_keyscan_interrupt_enable(&dev, type);

设置按键中断失能

通过 gx_hal_keyscan_interrupt_disable 函数失能按键中断。代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    GX_HAL_KEYSCAN_INT_TYPE type = GX_HAL_KEYSCAN_INT_STATUS_FIFO_NOT_EMPTY; /*!< FIFO 有值中断 1 */
    gx_hal_keyscan_interrupt_disable(&dev, type);

读取按键 FIFO 中存储的数据个数

通过 gx_hal_keyscan_get_fifo_data_num 函数读取 FIFO 中数据个数。

代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    int num = gx_hal_keyscan_get_fifo_data_num(&dev);

每次读取按键 FIFO 中一个位宽的数据

通过 \ref gx_hal_keyscan_get_fifo_data 函数每次读取 FIFO 中一个位宽的数据。

代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    unsigned int data = gx_hal_keyscan_get_fifo_data(&dev);

清除按键 FIFO 中所有数据

通过 gx_hal_keyscan_clear_fifo 函数来清空 FIFO 。

代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    gx_hal_keyscan_clear_fifo(&dev);

按键卡键时屏蔽该行按键响应

通过 gx_hal_keyscan_set_stuck_flag 函数来设置需要屏蔽按键的行号。

备注

  • 设置后,该行上所有的按键将都不会响应。

  • 该行的按键扫描唤醒会继续触发,只是不会产生该行键值;如果需要屏蔽该行的按键扫描唤醒,则需要通过 PMU 来屏蔽掉该行的 IO 唤醒功能;

  • row 表示卡键的行号(行索引)

代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    int row = 1;
    gx_hal_keyscan_set_stuck_flag(&dev, row);

设置按键空帧上报模式

通过 gx_hal_keyscan_set_empty_frm_flag 函数设置按键空帧上报模式,是否触发中断 1 来上报。

  • 1 表示不触发中断 1 上报

  • 0 表示会触发中断 1 上报

代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };
    int flag = 1; // 表示空帧键值不会触发中断1进行上报
    gx_hal_keyscan_set_empty_frm_flag(&dev, flag);

设置按键模块使能

通过 gx_hal_keyscan_enable 函数使能按键模块。代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };

    gx_hal_keyscan_enable(&dev);

设置按键模块失能

通过 gx_hal_keyscan_disable 函数失能按键模块。代码使用示例:

    static GX_HAL_KEYSCAN dev = {
        .regs = GX_REG_BASE_KEYSCAN,
    };

    gx_hal_keyscan_disable(&dev);

参考代码

示例代码参考 keyscan.c