Manual Mode

This sample application note describes KeyScan manual scan mode to implement matrix keyboard scanning.

Key press debounce and key release debounce are realized by a software timer.

When the single scan is completed, the Keyscan scan end interrupt is entered and the key information is read within the interrupt handler function.

Here should be KEYSCAN manual diagram

KeyScan Manual Mode Diagram

Requirements

For requirements, please refer to the Requirements.

Wiring

The EVB is connected to a matrix keyboard module, connect P2_3 to ROW0, P2_4 to ROW1, P4_0 to COLUMN0, and P4_1 to COLUMN1.

External matrix keyboard is shown below:

Here should be a picture of the external matrix keyboard

External Matrix keyboard

Configurations

  1. The following macro can be configured to modify the pin definitions.

    #define KEYBOARD_ROW_0                  P2_3
    #define KEYBOARD_ROW_1                  P2_4
    #define KEYBOARD_COLUMN_0               P4_0
    #define KEYBOARD_COLUMN_1               P4_1
    
  2. The following macros can be configured to modify the timing duration of the software timer.

    #define KEYSCAN_SW_INTERVAL             (200)/* 200ms */
    #define KEYSCAN_SW_RELEASE_TIMEOUT      (10)/* 10ms */
    

Building and Downloading

For building and downloading, please refer to the Building and Downloading.

Experimental Verification

  1. When the EVB starts, observe the following log within the Debug Analyzer.

    Start keyscan manual test!
    
  2. Initialize the keyscan software timer, print log.

    [io_keyscan] timer_keyscan_init: keyscan timer init
    
  3. When a key is pressed, Keyscan starts scan. After the scan ends, it enters the Keyscan interrupt and prints the key information. If a key remains pressed continuously, it will keep printing the key information.

    1. If a single key press is detected, print the following log.

      [io_keyscan] io_keyscan_handle_keys: Single key press. key: (x, x)
      
    2. If two key presses are detected, print the following log.

      [io_keyscan] io_keyscan_handle_keys: Two key press. key0: (x, x), key1: (x, x)
      
  4. After all the keys are released, print the following log within the callback function of the software timer.

    [io_keyscan] io_keyscan_handle_keys: All keys released.
    

Code Overview

This section introduces the code and process description for initialization and corresponding function implementation in the sample.

Source Code Directory

The directory for project file and source code are as follows:

  • Project directory: sdk\samples\peripheral\keyscan\keyscan_manual\proj

  • Source code directory: sdk\samples\peripheral\keyscan\keyscan_manual\src

Initialization

The initialization flow for peripherals can refer to Initialization Flow in General Introduction.

  1. Call Pad_Config() and Pinmux_Config() to configure the PAD and PINMUX of the corresponding pins.

    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);
    }
    
  2. Call RCC_PeriphClockCmd() to enable the KeyScan clock.

  3. Initialize the KeyScan peripheral:

    1. Define the KEYSCAN_InitTypeDef type KEYSCAN_InitStruct, and call KEYSCAN_StructInit to pre-fill KEYSCAN_InitStruct with default values.

    2. Modify the KEYSCAN_InitStruct parameters as needed. The KeyScan initialization parameter configuration is shown in the table below.

    3. Call KeyScan_Init() to initialize the KeyScan peripheral.

KeyScan Initialization Parameters

KeyScan Hardware Parameters

Setting in the KEYSCAN_InitStruct

KeyScan

Row Size

KEYSCAN_InitTypeDef::rowSize

2

Column Size

KEYSCAN_InitTypeDef::colSize

2

Scan Mode

KEYSCAN_InitTypeDef::scanmode

KeyScan_Manual_Scan_Mode

Debounce Enable

KEYSCAN_InitTypeDef::debounceEn

ENABLE

  1. Configure the KeyScan scan end interrupt: KEYSCAN_INT_SCAN_END, and configure the NVIC. For NVIC-related configuration, refer to Interrupt Configuration.

  2. Call KeyScan_Cmd() to enable KeyScan scanning.

  3. Call the os_timer_create function to create a software timer with a timing interval of KEYSCAN_SW_INTERVAL to simulate the interval between two scans.

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

Functional Implementation

The KeyScan scanning flow in this sample is shown in the figure:

Here should be KEYSCAN scan flow

KeyScan scanning flow

In this sample, KeyScan is configured in manual scan mode. The interval time and release time are simulated using a software timer to mimic the process of KeyScan automatic scan mode.

  1. After initialization is complete, the os_sched_start function is executed to begin task scheduling.

  2. When a key is pressed, KeyScan triggers a manual scan. After a single scan is completed, it triggers a KeyScan single scan completion interrupt.

  3. In the KeyScan interrupt function, during KeyScan interrupt handling, the KeyScan_GetFifoDataNum() and KeyScan_Read() functions are called to read data from the KeyScan FIFO, and the Key_Pressed_Flag is set to true to indicate that a key has been pressed.

  4. The os_timer_restart function is called to start the software timer, with the timer set to KEYSCAN_SW_INTERVAL to simulate the interval time in the KeyScan automatic scan process.

    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);
        }
    }
    
  5. When the software timer reaches the set time (KEYSCAN_SW_INTERVAL time), check the status of Key_Pressed_Flag in the callback function.

    1. If Key_Pressed_Flag is true, it indicates that a key was pressed during the last scan and the scan interval time has elapsed. In the callback function, set Key_Pressed_Flag to false, reinitialize KeyScan for scanning, and start the timer for KEYSCAN_SW_RELEASE_TIMEOUT to simulate the all release time.

    2. If Key_Pressed_Flag is false, it indicates that after the KEYSCAN_SW_RELEASE_TIMEOUT time has passed, the callback function is entered again, and no keys are pressed, meaning all keys have been released. Clear the key information, reinitialize KeyScan, and start a new round of scanning.

    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);
        }
    }
    
  6. After the KEYSCAN_SW_RELEASE_TIMEOUT timer is activated, if a key state is detected during this period, the KeyScan interrupt function will be triggered again. Within the interrupt function, the KEYSCAN_SW_INTERVAL timer will be restarted to override the KEYSCAN_SW_RELEASE_TIMEOUT timing. During the KEYSCAN_SW_INTERVAL timer period, since KeyScan is not reinitialized, a key press will not trigger a scan.