Phase Detection - DLPS

This example uses QDEC to wake up DLPS.

When the system is in IDLE state, it will automatically enter DLPS state.

When QDEC detects new data, it will wake the system from DLPS and simultaneously trigger a QDEC interrupt. Within the interrupt function, it reads the motion information of the sensor device.

For detailed information about QDEC and the detection sensor module, please refer to Phase Detection .

Requirements

The sample supports the following development kits:

Development Kits

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

For more requirements, please refer to Quick Start.

Wiring

Connect the rotation sensor module on the EVB, connect P2_3 to PhaseA, P2_4 to PhaseB.

Building and Downloading

This sample can be found in the SDK folder:

Project file: board\evb\io_sample\QDEC\QDEC_DLPS\mdk

Project file: board\evb\io_sample\QDEC\QDEC_DLPS\gcc

Please follow these steps to build and run the example:

  1. Open sample project file.

  2. To build the target, follow the steps listed on the Generating App Image in Quick Start.

  3. After a successful compilation, the app bin app_MP_xxx.bin will be generated in the directory mdk\bin or gcc\bin.

  4. To download app bin into EVB board, follow the steps listed on the MP Tool Download in Quick Start.

  5. Press reset button on EVB board and it will start running.

Experimental Verification

When the QDEC Y-axis detects new data, it wakes up the system and triggers an interrupt. Inside the interrupt function, it prints the direction and number of scrolls of the wheel.

Qdecode_Handler: Y_Axis_Direction = xx, Y_Axis_Count = xx

Code Overview

This chapter will be introduced according to the following several parts:

  1. Source Code Directory.

  2. Peripheral initialization will be introduced in chapter Initialization.

  3. Functional implementation after initialization will be introduced in chapter Function Implementation.

Source Code Directory

  • Project directory: sdk\board\evb\io_sample\QDEC\QDEC_DLPS

  • Source code directory: sdk\src\sample\io_sample\QDEC\QDEC_DLPS

Source files are currently categorized into several groups as below.

└── Project: qdec_dlps
    └── secure_only_app
        └── include
            ├── app_define.h
            └── rom_uuid.h
        ├── cmsis                    includes CMSIS header files and startup files
            ├── overlay_mgr.c
            ├── system_rtl876x.c
            └── startup_rtl876x.s
        ├── lib                      includes all binary symbol files that user application is built on
            ├── rtl8752h_sdk.lib
            ├── gap_utils.lib
            └── ROM.lib
        ├── peripheral               includes all peripheral drivers and module code used by the application
            ├── rtl876x_rcc.c
            ├── rtl876x_pinmux.c
            ├── rtl876x_nvic.c
            ├── rtl876x_io_dlps.c
            └── rtl876x_qdec.c
        ├── profile
        └── app                      includes the ble_peripheral user application implementation
            ├── main.c
            ├── ancs.c
            ├── app.c
            ├── app_task.c
            └── io_qdec.c

Initialization

When the EVB reset is initiated, the main() function is called, and the following process will be executed:

int main(void)
{
    extern uint32_t random_seed_value;
    srand(random_seed_value);
    global_data_init();
    board_init();
    le_gap_init(APP_MAX_LINKS);
    gap_lib_init();
    app_le_gap_init();
    app_le_profile_init();
    pwr_mgr_init();
    task_init();
    os_sched_start();

    return 0;
}

Note

le_gap_init(), gap_lib_init(), app_le_gap_init, and app_le_profile_init are related to the initialization of the privacy management module. Refer to the initialization process description in LE Peripheral Privacy.

The specific initialization process related to peripherals is as follows:

  1. In board_init, execute board_qdec_init, which is a function for PAD/PINMUX settings and includes the following processes:

    1. Configure PAD: Set the pin, PINMUX mode, PowerOn, internal pull-up, output disable.

    2. Configure PINMUX: Assign pins to qdec_phase_a_y and qdec_phase_b_y functions respectively.

    void board_qdec_init(void)
    {
        Pad_Config(QDEC_Y_PHA_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE,
               PAD_OUT_LOW);
        Pad_Config(QDEC_Y_PHB_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE,
               PAD_OUT_LOW);
        Pinmux_Config(QDEC_Y_PHA_PIN, qdec_phase_a_y);
        Pinmux_Config(QDEC_Y_PHB_PIN, qdec_phase_b_y);
    }
    
  2. After executing os_sched_start() to start task scheduling, in the app_main_task main task, execute driver_init to initialize the peripheral drivers.

  3. In driver_init, execute driver_qdec_init, which is the initialization function for the QDEC peripheral, and includes the following steps:

    1. Enable RCC clock.

    2. Enable Y-axis function.

    3. Enable debounce function.

    4. Configure QDEC Y-axis new data interrupt QDEC_Y_INT_NEW_DATA.

    5. Configure and enable the QDEC IRQ channel.

    void driver_qdec_init(void)
    {
        QDEC_DeInit(QDEC);
        RCC_PeriphClockCmd(APBPeriph_QDEC, APBPeriph_QDEC_CLOCK, ENABLE);
    
        QDEC_InitTypeDef QDEC_InitStruct;
        QDEC_StructInit(&QDEC_InitStruct);
        QDEC_InitStruct.axisConfigY       = ENABLE;
        QDEC_InitStruct.debounceEnableY   = Debounce_Enable;
        QDEC_Init(QDEC, &QDEC_InitStruct);
        QDEC_INTConfig(QDEC, QDEC_Y_INT_NEW_DATA, ENABLE);
    
        /** To debug, enable QDEC when the GAP stack is ready.
        * In app.c->app_handle_dev_state_evt()
        */
    //    QDEC_Cmd(QDEC, QDEC_AXIS_Y, ENABLE);
    
        NVIC_InitTypeDef nvic_init_struct;
        nvic_init_struct.NVIC_IRQChannel         = Qdecode_IRQn;
        nvic_init_struct.NVIC_IRQChannelCmd      = (FunctionalState)ENABLE;
        nvic_init_struct.NVIC_IRQChannelPriority = 3;
        NVIC_Init(&nvic_init_struct);
    
    }
    
  4. Execute pwr_mgr_init, this function sets the voltage mode for DLPS, and includes the following process:

    1. Register the user-entering DLPS callback function app_enter_dlps_config, register the user-exiting DLPS callback function app_exit_dlps_config.

      1. Execute the io_qdec_dlps_enter function within app_enter_dlps_config, set the pin to SW mode, and set the DLPS wake-up method.

        void io_qdec_dlps_enter(void)
        {
            /* Switch pad to Software mode */
            Pad_ControlSelectValue(QDEC_Y_PHA_PIN, PAD_SW_MODE);
            Pad_ControlSelectValue(QDEC_Y_PHB_PIN, PAD_SW_MODE);
        
            System_WakeUpPinEnable(QDEC_Y_PHA_PIN, PAD_WAKEUP_POL_LOW, 0, 0);
        }
        
      2. Execute the io_qdec_dlps_exit function within app_exit_dlps_config to set the pin to PINMUX mode.

        void io_qdec_dlps_exit(void)
        {
            /* Switch pad to Pinmux mode */
            Pad_ControlSelectValue(QDEC_Y_PHA_PIN, PAD_PINMUX_MODE);
            Pad_ControlSelectValue(QDEC_Y_PHB_PIN, PAD_PINMUX_MODE);
        }
        
    2. Register hardware control callback functions DLPS_IO_EnterDlpsCb and DLPS_IO_ExitDlpsCb. Entering DLPS will save CPU, PINMUX, Peripheral, etc., and exiting DLPS will restore CPU, PINMUX, Peripheral, etc.

    3. Set the power mode to DLPS mode.

    void pwr_mgr_init(void)
    {
        if (false == dlps_check_cb_reg(app_dlps_check_cb))
        {
            APP_PRINT_ERROR0("Error: dlps_check_cb_reg(app_dlps_check_cb) failed!");
        }
        DLPS_IORegUserDlpsEnterCb(app_enter_dlps_config);
        DLPS_IORegUserDlpsExitCb(app_exit_dlps_config);
        DLPS_IORegister();
        lps_mode_set(PLATFORM_DLPS_PFM);
    
        /* Config WakeUp pin */
        System_WakeUpPinEnable(QDEC_Y_PHA_PIN, PAD_WAKEUP_POL_LOW, 0, 0);
    }
    

Functional Implementation

  1. Execute os_sched_start() in the main function to start task scheduling. When the stack is ready, execute app_handle_dev_state_evt, then execute QDEC_Cmd() to enable the QDEC Y-axis.

    void app_handle_dev_state_evt(T_GAP_DEV_STATE new_state, uint16_t cause)
    {
        ...
        if (gap_dev_state.gap_init_state != new_state.gap_init_state)
        {
            if (new_state.gap_init_state == GAP_INIT_STATE_STACK_READY)
            {
                APP_PRINT_INFO0("GAP stack ready");
                /*stack ready*/
                QDEC_Cmd(QDEC, QDEC_AXIS_Y, ENABLE);
            }
        }
        ...
    }
    
  2. When the QDEC Y-axis detects new data, it triggers DLPS wake-up and simultaneously triggers the QDEC_Y_INT_NEW_DATA interrupt, entering the interrupt handler Qdecode_Handler.

    1. Mask the QDEC Y-axis new data interrupt.

    2. Read the Y-axis movement direction and count value.

    3. Send a message to the app task. After the main task detects the message, it executes io_handle_qdec_msg, prints the scroll direction and count, and sets the global variable IO_QDEC_DLPS_Enter_Allowed to PM_CHECK_PASS, indicating that it can enter DLPS state.

    4. Clear the interrupt flag and unmask the interrupt.

    void Qdecode_Handler(void)
    {
        if (QDEC_GetFlagState(QDEC, QDEC_FLAG_NEW_CT_STATUS_Y) == SET)
        {
            /* Mask qdec interrupt */
            QDEC_INTMask(QDEC, QDEC_Y_CT_INT_MASK, ENABLE);
    
            /* Read direction & count */
            Y_Axis_Data.AxisDirection = QDEC_GetAxisDirection(QDEC, QDEC_AXIS_Y);
            Y_Axis_Data.AxisCount = QDEC_GetAxisCount(QDEC, QDEC_AXIS_Y);
    
            T_IO_MSG int_qdec_msg;
    
            int_qdec_msg.type = IO_MSG_TYPE_QDECODE;
            int_qdec_msg.u.buf = (void *)&Y_Axis_Data;
            if (false == app_send_msg_to_apptask(&int_qdec_msg))
            {
                APP_PRINT_ERROR0("[io_qdec]Qdecode_Handler: Send int_qdec_msg failed!");
                //Add user code here!
                /* Clear qdec interrupt flags */
                QDEC_ClearINTPendingBit(QDEC, QDEC_CLR_NEW_CT_Y);
                return;
            }
            /* Clear qdec interrupt flags */
            QDEC_ClearINTPendingBit(QDEC, QDEC_CLR_NEW_CT_Y);
            /* Unmask qdec interrupt */
            QDEC_INTMask(QDEC, QDEC_Y_CT_INT_MASK, DISABLE);
        }
    }
    
    void io_handle_qdec_msg(T_IO_MSG *io_qdec_msg)
    {
        QDEC_Data_TypeDef *p_buf = io_qdec_msg->u.buf;
        APP_PRINT_INFO2("[io_qdec]io_handle_qdec_msg: Y_Axis_Direction = %d,Y_Axis_Count = %d,",
                        p_buf->AxisDirection, p_buf->AxisCount);
        global_data_qdec_init();
    
        IO_QDEC_DLPS_Enter_Allowed = PM_CHECK_PASS;
    }