Manual Mode
该示例展示了使用 KeyScan 的手动扫描模式来实现矩阵键盘扫描的过程。
当检测到有按键按下时,Keyscan 开始扫描键盘矩阵。在单次扫描完成后触发 KeyScan 单次扫描完成中断,在中断处理函数内进行按键信息读取。
在示例中,通过软件定时器来模拟扫描间隔(scan interval)和所有按键释放(all release)时间检测的流程,以模拟 KeyScan 自动扫描模式(Auto Mode)的扫描流程。

KeyScan 使用DMA 主设备发送从设备接收框图
环境需求
该示例的环境需求,可参考 环境需求。
硬件连线
EVB 外接矩阵键盘,连接 P2_3 和 ROW0,P2_4 和 ROW1,P4_0 和 COLUMN0,P4_1 和 COLUMN1。
外接矩阵键盘的原理图如下图所示:

外接矩阵键盘
配置选项
可配置如下宏修改引脚定义。
#define KEYBOARD_ROW_0 P2_3 #define KEYBOARD_ROW_1 P2_4 #define KEYBOARD_COLUMN_0 P4_0 #define KEYBOARD_COLUMN_1 P4_1
可配置如下宏修改软件定时器的定时时间。
#define KEYSCAN_SW_INTERVAL (200)/* 200ms */ #define KEYSCAN_SW_RELEASE_TIMEOUT (10)/* 10ms */
编译和下载
该示例的编译和下载流程,可参考 编译和下载。
测试验证
EVB 启动后,在 Debug Analyzer 工具内观察 log。
Start keyscan manual test!
初始化软件定时器,打印 log。
[io_keyscan] timer_keyscan_init: keyscan timer init
当有按键按下时,Keyscan 开始单次扫描。单次扫描结束后,进入 Keyscan 中断,打印按键信息。如果一直有按键按下,则会一直打印按键信息。
若检测到单个按键按下,打印如下 log。
[io_keyscan] io_keyscan_handle_keys: Single key press. key: (x, x)
若检测到两个按键按下,打印如下 log。
[io_keyscan] io_keyscan_handle_keys: Two key press. key0: (x, x), key1: (x, x)
当按键全部松开后,在软件定时器的回调函数内打印如下 log。
[io_keyscan] io_keyscan_handle_keys: All keys release.
代码介绍
该章节主要介绍示例中的初始化和相应功能实现的代码和流程说明。
源码路径
工程文件和源码路径如下:
工程路径:
sdk\samples\peripheral\keyscan\keyscan_manual\proj
源码路径:
sdk\samples\peripheral\keyscan\keyscan_manual\src
初始化
外设的初始化流程可参考 General Introduction 中的 初始化流程 部分。
调用
Pad_Config()
与Pinmux_Config()
,配置对应引脚的 PAD 和 PINMUX。void board_keyboard_init(void) { /* Keypad pad config */ Pad_Config(KEYBOARD_ROW_0, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW); Pad_Config(KEYBOARD_ROW_1, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW); Pad_Config(KEYBOARD_COLUMN_0, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_LOW); Pad_Config(KEYBOARD_COLUMN_1, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_LOW); /* keypad pinmux config */ Pinmux_Config(KEYBOARD_ROW_0, KEY_ROW_0); Pinmux_Config(KEYBOARD_ROW_1, KEY_ROW_1); Pinmux_Config(KEYBOARD_COLUMN_0, KEY_COL_0); Pinmux_Config(KEYBOARD_COLUMN_1, KEY_COL_1); }
调用
RCC_PeriphClockCmd()
,开启 KeyScan 时钟。对 KeyScan 外设进行初始化:
定义
KEYSCAN_InitTypeDef
类型KEYSCAN_InitStruct
,调用KEYSCAN_StructInit
将KEYSCAN_InitStruct
预填默认值。根据需求修改
KEYSCAN_InitStruct
参数,KeyScan 的初始化参数配置如下表。调用
KeyScan_Init()
,初始化 KeyScan 外设。
KeyScan Hardware Parameters |
Setting in the |
KeyScan |
---|---|---|
Row Size |
2 |
|
Column Size |
2 |
|
Scan Mode |
||
Debounce Enable |
配置 KeyScan 单次扫描完成中断
KEYSCAN_INT_SCAN_END
,配置 NVIC。NVIC 相关配置可参考 中断配置。调用
KeyScan_Cmd()
,使能 KeyScan 扫描。调用
os_timer_create
函数,创建软件定时器,定时时间为KEYSCAN_SW_INTERVAL
,用于模拟两次扫描的间隔。os_timer_create(&KeyScan_Timer_Handle, "keyscan_timer", 1, KEYSCAN_SW_INTERVAL, false, timer_keyscan_callback);
功能实现
该示例中的 KeyScan 扫描流程如图所示:

KeyScan 扫描流程
在该示例中,KeyScan 配置为手动扫描模式,通过软件定时器模拟 interval time 和 release time,来模拟 KeyScan 的自动扫描模式流程。
初始化完毕之后,调用
os_sched_start
函数,开始任务调度。当有按键按下时,KeyScan 触发手动扫描。单次扫描完成后,触发 KeyScan 单次扫描完成中断。
在 KeyScan 中断函数内,在 KeyScan 中断处理中,调用
KeyScan_GetFifoDataNum()
和KeyScan_Read()
读取 KeyScan FIFO 中的数据,并将Key_Pressed_Flag
设为true
,以表明有按键按下。调用
os_timer_restart
,开启软件定时器,定时时间为KEYSCAN_SW_INTERVAL
,模拟 KeyScan 自动扫描流程中的 interval time。void KEYSCAN_Handler(void) { uint32_t fifo_length; if (KeyScan_GetFlagState(KEYSCAN, KEYSCAN_INT_FLAG_SCAN_END) == SET) { /* Read current keyscan interrupt status and mask interrupt */ KeyScan_INTMask(KEYSCAN, KEYSCAN_INT_SCAN_END, ENABLE); memset(&Current_Key_Data, 0, sizeof(KeyScan_Data_TypeDef)); /* KeyScan fifo not empty */ if (KeyScan_GetFlagState(KEYSCAN, KEYSCAN_FLAG_EMPTY) != SET) { fifo_length = (uint32_t)KeyScan_GetFifoDataNum(KEYSCAN); KeyScan_Read(KEYSCAN, (uint16_t *)&Current_Key_Data.key[0], fifo_length); Current_Key_Data.length = fifo_length; Key_Pressed_Flag = true; /* Start sw timer to check press status */ if (!os_timer_restart(&KeyScan_Timer_Handle, KEYSCAN_SW_INTERVAL)) { APP_PRINT_ERROR0("[io_keyscan] Keyscan_Handler: Restart keyscan_timer failed!"); /* Set flag to default status and reinit keyscan module with debounce enabled */ global_data_keyscan_init(); driver_keyboard_init(ENABLE); return; } ... } KeyScan_ClearINTPendingBit(KEYSCAN, KEYSCAN_INT_SCAN_END); KeyScan_INTMask(KEYSCAN, KEYSCAN_INT_SCAN_END, DISABLE); } }
当软件定时器定时时间到(
KEYSCAN_SW_INTERVAL
时间)后,在回调函数内判断Key_Pressed_Flag
的状态。如果
Key_Pressed_Flag
为true
,表明上次扫描有按键按下,并经过了 scan interval 的时间。在回调函数内将Key_Pressed_Flag
置为false
,重新初始化 KeyScan 进行扫描。并开启定时器定时KEYSCAN_SW_RELEASE_TIMEOUT
用于模拟 all release time。如果
Key_Pressed_Flag
为false
,表明经过了KEYSCAN_SW_RELEASE_TIMEOUT
时间之后,再次进入到了回调函数内,此时没有按键按下,代表按键全部松开。清空按键信息并重新初始化 KeyScan 重新开启一轮扫描。
void timer_keyscan_callback(void *p_xTimer) { 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 { global_data_keyscan_init(); driver_keyboard_init(ENABLE); } }
在开启
KEYSCAN_SW_RELEASE_TIMEOUT
定时时间后,如果期间内检测到了按键状态,会再次进入 KeyScan 中断函数,在中断函数内会重新启动KEYSCAN_SW_INTERVAL
时间定时器,以覆盖掉KEYSCAN_SW_RELEASE_TIMEOUT
定时。在KEYSCAN_SW_INTERVAL
定时时间内,由于没有重新初始化 KeyScan,因此有按键按下时也不会触发扫描。