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:
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:
Open sample project file.
To build the target, follow the steps listed on the Generating App Image in Quick Start.
After a successful compilation, the app bin
app_MP_xxx.bin
will be generated in the directorymdk\bin
orgcc\bin
.To download app bin into EVB board, follow the steps listed on the MP Tool Download in Quick Start.
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:
Peripheral initialization will be introduced in chapter Initialization.
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:
In
board_init
, executeboard_qdec_init
, which is a function for PAD/PINMUX settings and includes the following processes:Configure PAD: Set the pin, PINMUX mode, PowerOn, internal pull-up, output disable.
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); }
After executing
os_sched_start()
to start task scheduling, in theapp_main_task
main task, executedriver_init
to initialize the peripheral drivers.In
driver_init
, executedriver_qdec_init
, which is the initialization function for the QDEC peripheral, and includes the following steps:Enable RCC clock.
Enable Y-axis function.
Enable debounce function.
Configure QDEC Y-axis new data interrupt
QDEC_Y_INT_NEW_DATA
.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); }
Execute
pwr_mgr_init
, this function sets the voltage mode for DLPS, and includes the following process:Register the user-entering DLPS callback function
app_enter_dlps_config
, register the user-exiting DLPS callback functionapp_exit_dlps_config
.Execute the
io_qdec_dlps_enter
function withinapp_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); }
Execute the
io_qdec_dlps_exit
function withinapp_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); }
Register hardware control callback functions
DLPS_IO_EnterDlpsCb
andDLPS_IO_ExitDlpsCb
. Entering DLPS will save CPU, PINMUX, Peripheral, etc., and exiting DLPS will restore CPU, PINMUX, Peripheral, etc.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
Execute
os_sched_start()
in the main function to start task scheduling. When the stack is ready, executeapp_handle_dev_state_evt
, then executeQDEC_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); } } ... }
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 handlerQdecode_Handler
.Mask the QDEC Y-axis new data interrupt.
Read the Y-axis movement direction and count value.
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 variableIO_QDEC_DLPS_Enter_Allowed
toPM_CHECK_PASS
, indicating that it can enter DLPS state.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; }