Phase Detection - DLPS
当系统处于 IDLE 状态时,会自动进入 DLPS 状态。
当 QDEC 检测到新数据时会将系统从 DLPS 唤醒,同时触发 QDEC 中断,在中断函数内读取传感器装置运动信息。
QDEC 与检测传感器模块的相关介绍请参考 Phase Detection 。
环境需求
该示例支持以下开发套件:
Hardware Platforms |
Board Name |
---|---|
RTL8752H HDK |
RTL8752H EVB |
更多信息请参考 快速入门。
硬件连线
EVB 外接转动传感器模块,连接 P2_3 和 PhaseA,P2_4 和 PhaseB。
编译和下载
该示例的工程路径如下:
Project file: board\evb\io_sample\QDEC\QDEC_DLPS\mdk
Project file: board\evb\io_sample\QDEC\QDEC_DLPS\gcc
请按照以下步骤操作构建并运行该示例:
打开工程文件。
按照 快速入门 中 编译 APP Image 给出的步骤构建目标文件。
编译成功后,在路径
mdk\bin
或gcc\bin
下会生成 app binapp_MP_xxx.bin
文件。按下 reset 按键,开始运行。
测试验证
当 QDEC Y 轴检测到新数据时,唤醒系统,并触发中断,在中断函数内打印滚轮滚动方向和次数。
Qdecode_Handler: Y_Axis_Direction = xx, Y_Axis_Count = xx
代码介绍
该章节分为以下几个部分:
源码路径
工程路径:
sdk\board\evb\io_sample\QDEC\QDEC_DLPS
源码路径:
sdk\src\sample\io_sample\QDEC\QDEC_DLPS
该工程的工程文件代码结构如下:
└── 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
初始化
当 EVB 复位启动时,调用 main()
函数,将执行以下流程:
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;
}
备注
le_gap_init()
,gap_lib_init()
,app_le_gap_init
,app_le_profile_init
等为 privacy 管理模块相关的初始化,参考 LE Peripheral Privacy 中的初始化流程介绍。
与外设相关的初始化流程具体如下:
在
board_init
中,执行board_qdec_init
,该函数为 QDEC 相关引脚的 PAD/PINMUX 设置,包含如下流程:配置 PAD:设置引脚、PINMUX 模式、PowerOn、内部上拉、输出失能。
配置 PINMUX:分配引脚分别为 qdec_phase_a_y 和 qdec_phase_b_y 功能。
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); }
在执行
os_sched_start()
开启任务调度后,在app_main_task
主任务内,执行driver_init
对外设驱动进行初始化配置。在
driver_init
中执行driver_qdec_init
,该函数为 QDEC 外设的初始化,包含如下流程:使能 RCC 时钟。
使能 Y 轴功能。
使能去抖动功能。
配置 QDEC Y 轴新数据产生中断
QDEC_Y_INT_NEW_DATA
。配置并使能 QDEC 的 IRQ 通道。
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); }
执行
pwr_mgr_init
,该函数为 DLPS 的电压模式设置,包含如下流程:注册用户进入 DLPS 回调函数
app_enter_dlps_config
,注册用户退出 DLPS 回调函数app_exit_dlps_config
。在
app_enter_dlps_config
内执行io_qdec_dlps_enter
,设置引脚为 SW 模式,设置 DLPS 唤醒方式。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); }
在
app_exit_dlps_config
内执行io_qdec_dlps_exit
,设置引脚为 PINMUX 模式。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); }
注册硬件控制回调函数
DLPS_IO_EnterDlpsCb
和DLPS_IO_ExitDlpsCb
,进入 DLPS 会保存 CPU、PINMUX、Peripheral 等,退出 DLPS 会恢复 CPU、PINMUX、Peripheral 等。设置电源模式为 DLPS 模式。
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); }
功能实现
在主函数中执行
os_sched_start()
,开启任务调度。当 stack 准备好时,执行app_handle_dev_state_evt
,执行QDEC_Cmd()
使能 QDEC Y 轴。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); } } ... }
当 QDEC Y 轴检测到新数据时,触发 DLPS 唤醒,同时触发
QDEC_Y_INT_NEW_DATA
中断,进入中断处理函数Qdecode_Handler
。屏蔽 QDEC Y 轴新数据产生中断。
读 Y 轴运动方向、计数值。
发送消息给 app task。主 task 检测到消息后,执行
io_handle_qdec_msg
,打印滚轮滚动方向和次数,设置全局变量IO_QDEC_DLPS_Enter_Allowed
为PM_CHECK_PASS
,代表可进入 DLPS 状态。清除中断标志位,取消屏蔽中断。
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; }