KeyScan
Sample List
This chapter introduces the details of the KeyScan sample code. The SDK provides the following samples for the KeyScan peripheral.
Functional Overview
KeyScan needs to be used with an external key matrix. In the idle state, row[x] is input, pull high is required, column[y] is output low, and output mode must be open-drain. When any key is pressed, the level of the corresponding row changes from high to low, triggering KeyScan hardware scan action.
After the debounce is completed, the first scan starts. When scanning, the column[y] being scanned is output low, and the other columns output high. If row[x] is low at this time, it is considered that row[x] column[y] key is pressed.
Scan order is first column[0] fixed, scan from row[0] to row[x], then column[1] fixed, scan from row[0] to row[x], until column[y].
Feature List
Support 12 (row) * 20 (column) matrix scan.
Configurable number of rows (1-12), columns (1-20).
Support hardware debounce, configurable debounce time.
Configurable scan clock.
Support multi-key detection, up to 26 keys can be pressed at the same time.
Support periodic scan, configurable scan period.
26 bytes FIFO depth.
Support auto scan and manual scan.
Support all key release hardware detection.
Block Diagram
Here is the overall IPs block diagram for KeyScan IP¹, which including ‘PAD/PINMUX’ for IO function configuration, ‘KeyScan IP’ for KeyScan protocol.

System Block Diagram of KeyScan
Scanning Process
The scanning process of KeyScan is as shown in the diagram:

KeyScan Scanning Diagram
In idle state, all row[x] serve as input, in pull-up state, while all column[y] serve as output, outputting low level.
When any key is pressed, a low level will be detected in a certain row, triggering the KeyScan hardware scanning action.
After waiting for the debounce timer, the first scan begins.
First, pull column[0] low, while the other columns are pulled high, and sequentially detect row[0], row[1], …, row[N].
If row[0] is detected to be pulled low, it can be confirmed that (row[0], column[0]) is pressed; otherwise, the key is not pressed. Continue scanning other rows in this manner.
Switch to pulling column[1] low, the other columns are pulled high, sequentially detect row[0], row[1], …, row[N], until the last column[N] is pulled low and the other columns are pulled high, scanning is complete.
At this point, one scan is complete, and the scanning result will be filled into the KeyScan FIFO. If the
KEYSCAN_INT_SCAN_END
interrupt is enabled, it will trigger this interrupt.After the scan interval time, a second global scan will be conducted, and the same operation will be performed after the scan ends.
If no key press is detected during a scan, the all release timer will start counting, and all columns will be pulled low. If no row is detected to be pulled low after the all release timer finishes counting, it indicates that all keys are released at this time. If the
KEYSCAN_INT_ALL_RELEASE
interrupt is enabled, it will trigger this interrupt.
KeyScan scanning is divided into manual scanning mode and automatic scanning mode.
Manual Scan Mode
Configure KEYSCAN_InitTypeDef::scanmode
as KeyScan_Manual_Scan_Mode
during KeyScan initialization to set KeyScan to manual scan mode.
In manual scan mode, KeyScan will perform only one scan, and upon completion, it will trigger the KEYSCAN_INT_SCAN_END
interrupt.
Auto Scan Mode
Configure KEYSCAN_InitTypeDef::scanmode
as KeyScan_Auto_Scan_Mode
during KeyScan initialization to set KeyScan to automatic scan mode.
In automatic scan mode, KeyScan is triggered by external key presses.
The trigger mode can be modified to edge-triggered (KeyScan_Detect_Mode_Edge
) or level-triggered (KeyScan_Detect_Mode_Level
) by setting KEYSCAN_InitTypeDef::detectMode
.
In automatic scan mode, KeyScan will continuously scan until no keys are pressed, which triggers the KEYSCAN_INT_ALL_RELEASE
interrupt.
The KEYSCAN_INT_SCAN_END
interrupt can be triggered multiple times.
The workflow of automatic scanning is shown in the figure below.

Auto Scan Flow Chart
Clock Divider
IO clock will be divided into 2 channels.
The KeyScan scan clock is generated by the frequency division of keyscan_clk_div
(11 bits). Scan clock = KeyScan clock source (5MHz) / (KEYSCAN_InitTypeDef::clockdiv
+ 1).
The low-level delay clock is generated by the frequency division of keyscan_delay_div
(6 bits) for use by the relevant timer. Delay clock = Scan clock / (KEYSCAN_InitTypeDef::delayclk
+ 1).
key_scan_deb_timer
is used for debounce timing, where debounce time = Delay clock * KEYSCAN_InitTypeDef::debouncecnt
.
key_scan_interval_timer
is used for scan interval timing, where scan interval time = Delay clock * KEYSCAN_InitTypeDef::scanInterval
.
key_scan_release_timer
is used for release time timing, where release time = Delay clock * KEYSCAN_InitTypeDef::releasecnt
.

Schematic Diagram of KeyScan Clock Divider
Interrupt
-
When there is no data in the FIFO, reading the FIFO will trigger this interrupt to prevent over-reading.
-
Whether the key value is scanned or not, the interrupt will be triggered as long as the scanning action is completed.
-
If there is data in the FIFO, the interrupt will be triggered.
-
This interrupt is triggered when data in the FIFO reaches the threshold level.
-
When the release time count reaches the set value, if no key is pressed, the interrupt is triggered.
Power Manager
The KeyScan peripheral is located in the core domain and will be powered off in low power mode.
KeyScan peripheral store/restore will be automatically performed based on whether the KeyScan clock is active.
Users can call the API io_dlps_register()
to initialize KeyScan peripheral store/restore and do not need to worry about KeyScan peripheral requiring specific handling.