Manual Mode

该示例通过使用Keyscan的手动扫描模式,实现矩阵键盘的扫描。

示例中Key press debounce及key release debounce使用软件定时器实现。

当单次扫描完成时,进入Keyscan单次扫描完成中断,在中断处理函数内进行按键信息读取。

环境需求

该示例支持以下开发套件:

开发套件

Hardware Platforms

Board Name

RTL87x2G HDK

RTL87x2G EVB

更多信息请参考快速入门

硬件连线

EVB外接矩阵键盘,连接P2_3和ROW0,P2_4和ROW1,P4_0和COLUMN0,P4_1和COLUMN1。

外接矩阵键盘的原理图如下图所示:

这里应该是外接矩阵键盘的图片

外接矩阵键盘

编译和下载

该示例的工程路径如下:

Project file: samples\peripheral\keyscan\keyscan_manual\proj\rtl87x2g\mdk

Project file: samples\peripheral\keyscan\keyscan_manual\proj\rtl87x2g\gcc

请按照以下步骤操作构建并运行该示例:

  1. 打开工程文件。

  2. 按照 快速入门编译APP Image 给出的步骤构建目标文件。

  3. 编译成功后,在路径 mdk\bingcc\bin 下会生成 app bin app_MP_xxx.bin 文件。

  4. 按照 快速入门MP Tool 给出的步骤将app bin烧录至EVB内。

  5. 按下复位按键,开始运行。

测试验证

  1. EVB启动后,在Debug Analyzer工具内观察log。

    Start keyscan manual test!
    
  2. 初始化软件定时器,打印log。

    [io_keyscan] timer_keyscan_init: keyscan timer init
    
  3. 当检测到按键按下后,打印按键信息。

    1. 若检测到单个按键按下,打印如下log。

      [io_keyscan] io_keyscan_handle_keys: Single key press. key: (x, x)
      
    2. 若检测到两个按键按下,打印如下log。

      [io_keyscan] io_keyscan_handle_keys: Two key press. key0: (x, x), key1: (x, x)
      
  4. 当按键全部松开后,打印如下log。

    [io_keyscan] io_keyscan_handle_keys: All keys release.
    

代码介绍

该章节分为以下几个部分:

  1. 源码路径

  2. 初始化函数将在 初始化 章节介绍。

  3. 初始化后的功能实现将在 功能实现 章节介绍。

源码路径

  • 工程路径: sdk\samples\peripheral\keyscan\keyscan_manual\proj

  • 源码路径: sdk\samples\peripheral\keyscan\keyscan_manual\src

该工程的工程文件代码结构如下:

└── Project: keyscan_manual
    └── secure_only_app
        └── Device                   includes startup code
            ├── startup_rtl.c
            └── system_rtl.c
        ├── CMSIS                    includes CMSIS header files
        ├── CMSE Library             Non-secure callable lib
        ├── Lib                      includes all binary symbol files that user application is built on
            └── rtl87x2g_io.lib
        ├── Peripheral               includes all peripheral drivers and module code used by the application
            ├── rtl_rcc.c
            ├── rtl_pinmux.c
            ├── rtl_nvic.c
            └── rtl_keyscan.c
        └── APP                      includes the ble_peripheral user application implementation
            ├── main_ns.c
            └── io_keyscan.c

初始化

初始化流程包括了 global_data_keyscan_init board_keyboard_init driver_keyboard_inittimer_keyscan_init


global_data_keyscan_init 中包含了对Keyscan按键信息存储数组的初始化。


board_keyboard_init 中包含了PAD与PINMIX设置。

  1. 配置PAD:设置引脚、PINMUX模式、PowerOn、内部上拉或无上拉。

  2. 配置PINMUX:分配引脚分别为KEY_ROW_0、KEY_ROW_1、KEY_COL_0、KEY_COL_1功能。


driver_keyboard_init 包含了对Keyscan外设的初始化。

  1. 使能PCC时钟。

  2. 配置Keyscan的rowSize和colSize为(2*2)。

  3. 配置扫描模式为Manual Mode。

  4. 使能Keyscan的硬件去抖功能。

  5. 配置Keyscan单次扫描结束中断 KEYSCAN_INT_SCAN_END

RCC_PeriphClockCmd(APBPeriph_KEYSCAN, APBPeriph_KEYSCAN_CLOCK, ENABLE);
...
KEYSCAN_InitStruct.rowSize  = KEYBOARD_ROW_SIZE;
KEYSCAN_InitStruct.colSize  = KEYBOARD_COLUMN_SIZE;
KEYSCAN_InitStruct.scanmode = KeyScan_Manual_Scan_Mode;
KEYSCAN_InitStruct.debounceEn = ENABLE;
...
KeyScan_INTConfig(KEYSCAN, KEYSCAN_INT_SCAN_END, ENABLE);
...

timer_keyscan_init 包含了对软件定时器的初始化。

  1. 执行 os_timer_create 函数,创建软件定时器,定时时间为200ms,定时结束后执行 timer_keyscan_callback 回调函数,用于模拟Keyscan按键按下的Debounce。

os_timer_create(&KeyScan_Timer_Handle, "keyscan_timer",  1, KEYSCAN_SW_INTERVAL, false, timer_keyscan_callback);

功能实现

程序开始会启动一个200ms的软件定时器并使能Keyscan外设进行一次手动扫描。扫描结束后会进入单次扫描结束中断。如果检测到有按键按下,则将Key_Pressed_Flag置1,重启200ms定时器,重新使能Keyscan开始下一次扫描。

重复上述过程,当200ms定时器到期后,重新启动10ms的软件定时器用于模拟按键松开去抖。10ms定时器到期后判断Key_Pressed_Flag,如果为0代表扫描到所有按键松开,反之则仍有按键按下。

  1. 初始化完毕之后,执行 os_sched_start 函数,开始任务调度。

  2. 当有按键按下时,开启手动扫描模式。当单次扫描完成后,进入Keyscan中断函数。

    1. 屏蔽Keyscan单次扫描结束中断。

    2. 执行 KeyScan_GetFifoDataNum() ,读取Keyscan FIFO中的数据长度。

    3. 执行 KeyScan_Read() ,读取Keyscan FIFO中的数据内容。

    4. Key_Pressed_Flag 置为 true ,表明当前有按键按下。

    5. 重启软件定时器,定时时间为200ms,继续检测按键按下信息,模拟按下Debounce功能。

    6. 打印Keyscan按键信息。

    7. 开启Keyscan单次扫描结束中断。

KeyScan_INTMask(KEYSCAN, KEYSCAN_INT_SCAN_END, ENABLE);
fifo_length = (uint32_t)KeyScan_GetFifoDataNum(KEYSCAN);
KeyScan_Read(KEYSCAN, (uint16_t *)&Current_Key_Data.key[0], fifo_length);
Key_Pressed_Flag = true;
if (!os_timer_restart(&KeyScan_Timer_Handle, KEYSCAN_SW_INTERVAL))
{
  ...
}
...
KeyScan_INTMask(KEYSCAN, KEYSCAN_INT_SCAN_END, DISABLE);
  1. 当200ms定时结束时,进入回调函数 timer_keyscan_callback ,检测flag标志位 Key_Pressed_Flag

    如果flag位为true,表明当前有按键按下。

    1. 将flag位置为false。

    2. 执行 driver_keyboard_init ,重新初始化Keyscan,使能Keyscan功能,重新启动键盘扫描。

    3. 重启软件定时器,修改定时时间为10ms,模拟按键松开的debounce。

    如果flag位为false,表明当前没有按键按下。

    1. 打印按键全部松开信息。

    2. 执行 global_data_keyscan_init ,初始化按键信息。

    3. 执行 driver_keyboard_init ,重新初始化Keyscan。

if (true == Key_Pressed_Flag)
{
    Key_Pressed_Flag = false;
    driver_keyboard_init(ENABLE);

    /* Start timer to check key status */
    os_timer_restart(&p_xTimer, KEYSCAN_SW_RELEASE_TIMEOUT);
}
else
{
    /* Keyscan release event detected */
    DBG_DIRECT("[io_keyscan] io_keyscan_handle_keys: All keys release.");

    global_data_keyscan_init();
    driver_keyboard_init(ENABLE);
}