DLPS
该示例通过使用不同外设进行深度低功耗状态唤醒(DLPS)。
示例中包含的外设有 ADC 、GPIO 、UART 、 LPC 、RTC 、AON QDEC 。
其中ADC、GPIO和UART的例子属于PAD唤醒,LPC、RTC、AON QDEC都属于外设自身的唤醒源。关于低功耗模式的更多信息, 详见文档 低功耗模式。
环境需求
该示例支持以下开发套件:
Hardware Platforms |
Board Name |
---|---|
RTL87x2G HDK |
RTL87x2G EVB |
更多信息请参考 快速入门。
配置选项
该示例可通过如下宏配置不同的DLPS示例:
CONFIG_ADC_GPIO_DLPS_EN
:PAD唤醒,在GPIO中断内进行ADC采样。CONFIG_UART_DLPS_EN
:PAD唤醒,UART接收数据唤醒,唤醒后继续UART数据收发。CONFIG_RTC_DLPS_EN
:RTC定时时间到唤醒系统。CONFIG_AON_QDEC_DLPS_EN
:滚动Mouse滚轮唤醒系统。CONFIG_LPC_DLPS_EN
:电压比较满足条件唤醒。
硬件连线
若开启
CONFIG_ADC_GPIO_DLPS_EN
P4_0连接外部输入。
若开启
CONFIG_UART_DLPS_EN
EVB外接FT232模块,连接P3_0和FT232的RX,P3_1和FT232的TX。
若开启
CONFIG_AON_QDEC_DLPS_EN
EVB外接转动传感器模块,连接P1_3和PhaseA,P1_4和PhaseB。
若开启
CONFIG_LPC_DLPS_EN
P2_4连接外部输入电压。
编译和下载
该示例的工程路径如下:
Project file: samples\lowpower\dlps\proj\rtl87x2g\mdk
Project file: samples\lowpower\dlps\proj\rtl87x2g\gcc
请按照以下步骤操作构建并运行该示例:
打开工程文件。
按照 快速入门 中 编译APP Image 给出的步骤构建目标文件。
编译成功后,在路径
mdk\bin
或gcc\bin
下会生成 app binapp_MP_xxx.bin
文件。按下复位按键,开始运行。
测试验证
若开启
CONFIG_ADC_GPIO_DLPS_EN
当引脚P4_0输入低电平时,唤醒系统,进入GPIO中断。在GPIO中断内使能ADC采样,打印VBAT电压。
若开启
CONFIG_UART_DLPS_EN
串口助手给MCU发送消息,唤醒系统。再次发送消息,MCU会回复相同的消息至串口助手。
若开启
CONFIG_RTC_DLPS_EN
RTC每计时0.1s退出DLPS,进入RTC_INT_TICK中断,打印中断信息,再次进入DLPS。20次后,不再进入DLPS状态。
若开启
CONFIG_AON_QDEC_DLPS_EN
滚动Mouse滚轮,唤醒系统,打印中断信息,滚轮滚动方向和次数。20次后,不再进入DLPS状态。
若开启
CONFIG_LPC_DLPS_EN
引脚P2_4输入电压满足设定阈值条件时,会唤醒系统,触发LPC中断。
代码介绍
该章节分为以下几个部分:
源码路径
工程路径:
sdk\samples\lowpower\dlps\proj
源码路径:
sdk\samples\lowpower\dlps\src
该工程的工程文件代码结构如下:
└── Project: dlps
└── secure_only_app
└── Device includes startup code
├── startup_rtl.c
└── system_rtl.c
├── CMSIS includes CMSIS header files
├── CMSE Library Non-secure callable lib
├── lib includes all binary symbol files that user application is built on
└── rtl87x2g_io.lib
├── peripheral includes all peripheral drivers and module code used by the application
├── rtl_gpio.c
├── rtl_adc.c
├── rtl_rtc.c
├── rtl_lpc.c
├── rtl_uart.c
├── rtl_aon_qdec.c
├── rtl_pinmux.c
├── rtl_nvic.c
└── rtl_rcc.c
└── APP includes the ble_peripheral user application implementation
├── io_adc.c
├── io_dlps.c
├── io_uart.c
├── io_rtc.c
├── io_aon_qdec.c
├── io_lpc.c
└── main_ns.c
初始化
初始化流程包括了 global_data_init
, board_init
, driver_init
和 pwr_mgr_init
。
global_data_init
内包含了对全局变量的初始化。
在
global_data_uart_init
内,将PowerCheck标志位设置为PASS,允许进入DLPS;重置UART接收长度,重置接收数组。在
global_data_adc_gpio_init
内,将PowerCheck标志位设置为PASS,允许进入DLPS。void global_data_adc_gpio_init(void) { IO_ADC_GPIO_DLPS_Enter_Allowed = POWER_CHECK_PASS; } void global_data_uart_init(void) { IO_UART_DLPS_Enter_Allowed = POWER_CHECK_PASS; UART_RX_Count = 0; memset(UART_RX_Buffer, 0, sizeof(UART_RX_Buffer)); }
board_init
内包含了各外设的PAD与PINMUX设置。
driver_init
包含了对各外设的初始化,包括ADC,GPIO,UART,RTC,QDEC,LPC。根据宏配置去初始化对应的driver。
ADC初始化
通道0配置为VBAT采样,对应的Bitmap为0x01。
配置ADC单次采样完成中断
ADC_INT_ONE_SHOT_DONE
。ADC_InitStruct.ADC_SchIndex[0] = INTERNAL_VBAT_MODE; ADC_InitStruct.ADC_Bitmap = 0x01; ... ADC_INTConfig(ADC, ADC_INT_ONE_SHOT_DONE, ENABLE);
GPIO初始化
配置GPIO为输入模式。
使能GPIO中断,配置下降沿触发。
配置GPIO Debounce功能。
GPIO_InitStruct.GPIO_Pin = GPIO_PIN_INPUT; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStruct.GPIO_ITCmd = ENABLE; GPIO_InitStruct.GPIO_ITTrigger = GPIO_INT_Trigger_EDGE; GPIO_InitStruct.GPIO_ITPolarity = GPIO_INT_POLARITY_ACTIVE_LOW; GPIO_InitStruct.GPIO_ITDebounce = GPIO_INT_DEBOUNCE_ENABLE; ...
UART初始化
采用默认设置。
配置接收空闲中断
UART_INT_RX_IDLE
和线状态中断UART_INT_RD_AVA
。UART_INTConfig(UART0, UART_INT_RD_AVA, ENABLE); UART_INTConfig(UART0, UART_INT_RX_IDLE, ENABLE);
RTC初始化
设置RTC分频系数,时钟频率为10Hz。
使能RTC滴答中断。
使能RTC唤醒系统功能。
复位RTC计数值,启动RTC外设。
RTC_SetPrescaler(RTC_PRESCALER_VALUE); RTC_INTConfig(RTC_INT_TICK, ENABLE); ... RTC_SystemWakeupConfig(ENABLE); RTC_ResetCounter(); RTC_Cmd(ENABLE);
LPC初始化
设定LPC比较器输出极性。
设置电压阈值。
使能LPC功能。
使能LPC电压比较极性中断。
使能LPC唤醒。
LPC_InitStruct.LPC_Channel = LPC_CAPTURE_CHANNEL; LPC_InitStruct.LPC_Edge = LPC_VOLTAGE_DETECT_EDGE; LPC_InitStruct.LPC_Threshold = LPC_1000_mV; LPC_Init(LPC0, &LPC_InitStruct); LPC_INTConfig(LPC0, LPC_INT_LPCOMP, ENABLE); LPC_Cmd(LPC0, ENABLE); LPC_WKCmd(LPC0, ENABLE);
AON QDEC初始化
使能X轴功能,使能去抖动功能。
使能AON QDEC X轴新数据产生中断
AON_QDEC_X_INT_NEW_DATA
和X轴数据异常中断AON_QDEC_X_INT_ILLEAGE
。使能QDEC X轴通道。
QDEC_InitStruct.axisConfigX = ENABLE; QDEC_InitStruct.debounceEnableX = ENABLE; QDEC_InitStruct.manualLoadInitPhase = DISABLE; ... AON_QDEC_INTConfig(AON_QDEC, AON_QDEC_X_INT_NEW_DATA, ENABLE); AON_QDEC_INTConfig(AON_QDEC, AON_QDEC_X_INT_ILLEAGE, ENABLE); AON_QDEC_Cmd(AON_QDEC, AON_QDEC_AXIS_X, ENABLE); ...
pwr_mgr_init
包含了电源管理的初始化。
注册用户进入DLPS回调函数
app_enter_dlps_config
,注册用户退出DLPS回调函数app_exit_dlps_config
。注册硬件控制回调函数
DLPS_IO_EnterDlpsCb
和DLPS_IO_ExitDlpsCb
,进入DLPS会保存CPU、PINMUX、Peripheral等,退出DLPS会恢复CPU、PINMUX、Peripheral等。设置电源模式为DLPS模式。
power_check_cb_register(app_dlps_check_cb); DLPS_IORegUserDlpsEnterCb(app_enter_dlps_config); DLPS_IORegUserDlpsExitCb(app_exit_dlps_config); DLPS_IORegister(); bt_power_mode_set(BTPOWER_DEEP_SLEEP); power_mode_set(POWER_DLPS_MODE);
app_enter_dlps_config
中,执行不同外设的进入DLPS回调函数。
在配置DLPS唤醒为PAD唤醒时(ADC/GPIO、UART):设置引脚为SW Mode,设置DLPS唤醒方式。
Pad_ControlSelectValue(ADC_GPIO_DLPS_WAKEUP_PIN, PAD_SW_MODE); System_WakeUpPinEnable(ADC_GPIO_DLPS_WAKEUP_PIN, PAD_WAKEUP_POL_LOW, 0);
app_exit_dlps_config
中,执行不同外设的退出DLPS回调函数。
在配置DLPS唤醒为PAD唤醒时(ADC/GPIO、UART):设置引脚为PINMUX Mode。
在配置DLPS唤醒为外设唤醒时(AON QDEC、LPC、RTC):执行各外设应用逻辑(计数值+1等)。
void io_adc_gpio_dlps_exit(void) { /* Switch pad to Pinmux mode */ Pad_ControlSelectValue(ADC_GPIO_DLPS_WAKEUP_PIN, PAD_PINMUX_MODE); ... } void io_rtc_dlps_exit(void) { allow_count ++; DBG_DIRECT("DLPS EXIT, wake up reason 0x%x", power_get_wakeup_reason()); }
功能实现
若开启 CONFIG_ADC_GPIO_DLPS_EN
:
当引脚P4_0输入低电平时,系统退出DLPS状态,当系统被唤醒时,进入
System_Handler
。清除P4_0的唤醒中断挂起位。
失能P4_0的唤醒功能。
IO_ADC_DLPS_Enter_Allowed值设置为PM_CHECK_FAIL。
if (System_WakeUpInterruptValue(ADC_GPIO_DLPS_WAKEUP_PIN) == SET) { Pad_ClearWakeupINTPendingBit(ADC_GPIO_DLPS_WAKEUP_PIN); System_WakeUpPinDisable(ADC_GPIO_DLPS_WAKEUP_PIN); IO_ADC_GPIO_DLPS_Enter_Allowed = POWER_CHECK_FAIL; }
当引脚P4_0输入低电平时,触发
GPIO_PIN_INPUT
中断,进入中断处理函数GPIO_Input_Handler
。关GPIO中断,屏蔽GPIO中断。
使能ADC单次采样模式。
清除GPIO中断挂起位,取消屏蔽GPIO中断,使能GPIO中断。
GPIO_INTConfig(GPIO_PORT, GPIO_PIN_INPUT, DISABLE); GPIO_MaskINTConfig(GPIO_PORT, GPIO_PIN_INPUT, ENABLE); ADC_Cmd(ADC, ADC_ONE_SHOT_MODE, ENABLE); GPIO_ClearINTPendingBit(GPIO_PORT, GPIO_PIN_INPUT); GPIO_MaskINTConfig(GPIO_PORT, GPIO_PIN_INPUT, DISABLE); GPIO_INTConfig(GPIO_PORT, GPIO_PIN_INPUT, ENABLE);
当ADC单次转换完成时,触发
ADC_INT_ONE_SHOT_DONE
中断,进入中断处理函数SAR_ADC_Handler
。清除
ADC_INT_ONE_SHOT_DONE
中断挂起位。读取采样数据,计算电压值。
ADC_ClearINTPendingBit(ADC, ADC_INT_ONE_SHOT_DONE); sample_data = ADC_ReadRawData(ADC, ADC_Schedule_Index_0); sample_voltage = ADC_GetVoltage(DIVIDE_SINGLE_MODE, (int32_t)sample_data, &error_status);
若开启 CONFIG_UART_DLPS_EN
:
通过串口调试助手发送数据给IC时,UART_RX_PIN出现低电平,唤醒DLPS,进入
System_Handler
。清除UART_RX_PIN的唤醒中断挂起位。
失能UART_RX_PIN的唤醒功能。
IO_UART_DLPS_Enter_Allowed值设置为PM_CHECK_FAIL。
if (System_WakeUpInterruptValue(UART_RX_PIN) == SET) { System_WakeUpPinDisable(UART_RX_PIN); Pad_ClearWakeupINTPendingBit(UART_RX_PIN); IO_UART_DLPS_Enter_Allowed = POWER_CHECK_FAIL; }
当UART收到数据时触发
UART_INT_RD_AVA
中断,或当UART空闲时触发UART_INT_RX_IDLE
中断,进入中断处理函数UART0_Handler
。
失能
UART_INT_RX_IDLE
中断。将UART_RX_Buffer中的数据通过UART发送到PC端。
初始化UART全局数据。
判断UART发送缓冲区为空的状态为0时,即发送完成,IO_UART_DLPS_Enter_Allowed置位。
UART_INTConfig(UART0, UART_INT_RX_IDLE, DISABLE); DBG_DIRECT("UART IDLE"); /* Send msg to app task */ uart_senddata_continuous(UART0, UART_RX_Buffer, UART_RX_Count); global_data_uart_init(); while (UART_GetFlagStatus(UART0, UART_FLAG_TX_FIFO_EMPTY) == 0) { IO_UART_DLPS_Enter_Allowed = POWER_CHECK_PASS; } UART_ClearRxFIFO(UART0); UART_INTConfig(UART0, UART_INT_RX_IDLE, ENABLE);
若开启 CONFIG_RTC_DLPS_EN
:
RTC设定时间到,唤醒DLPS。同时触发中断,进入中断处理函数
RTC_Handler
,打印信息,清除滴答中断。if (RTC_GetINTStatus(RTC_INT_TICK) == SET) { DBG_DIRECT("RTC_INT_TICK"); RTC_ClearINTPendingBit(RTC_INT_TICK); }
每退出一次DLPS,allow_count会自增1。当allow_count自增到20时,将不会再进入DLPS状态。
POWER_CheckResult io_rtc_dlps_check(void) { if (allow_count >= 20) { IO_RTC_DLPS_Enter_Allowed = POWER_CHECK_FAIL; } return IO_RTC_DLPS_Enter_Allowed; }
若开启 CONFIG_AON_QDEC_DLPS_EN
:
当AON QDEC X轴检测到新数据时,唤醒系统,同时触发
AON_QDEC_X_INT_NEW_DATA
中断,进入中断处理函数AON_QDEC_Handler
。读X轴运动方向、计数值。
打印中断信息,滚轮滚动方向和次数。
清除
AON_QDEC_CLR_NEW_CT_X
中断挂起位。
X_Axis_Data.AxisDirection = AON_QDEC_GetAxisDirection(AON_QDEC, AON_QDEC_AXIS_X); X_Axis_Data.AxisCount = AON_QDEC_GetAxisCount(AON_QDEC, AON_QDEC_AXIS_X); DBG_DIRECT(...); AON_QDEC_ClearINTPendingBit(AON_QDEC, AON_QDEC_CLR_NEW_CT_X);
每退出一次DLPS,allow_count会自增1。当allow_count自增到20时,将不会再进入DLPS状态。
POWER_CheckResult io_aon_qdec_dlps_check(void) { if (allow_count >= 20) { IO_AON_QDEC_DLPS_Enter_Allowed = POWER_CHECK_FAIL; } return IO_AON_QDEC_DLPS_Enter_Allowed; }
若开启 CONFIG_LPC_DLPS_EN
:
调节直流稳压源电压值,当P2_4检测电压高于1000mV时,触发
LPC_INT_LPCOMP
中断,进入中断处理函数LPCOMP_Handler
。失能
LPC_INT_LPCOMP
中断。清除
LPC_FLAG_LPCOMP
的标志。判断是否为
LPC_INT_LPCOMP_AON
中断。
if (LPC_GetFlagStatus(LPC0, LPC_FLAG_LPCOMP) == SET) { DBG_DIRECT("LPC COMP"); LPC_ClearFlag(LPC0, LPC_FLAG_LPCOMP); } if (LPC_GetFlagStatus(LPC0, LPC_FLAG_LPCOMP_AON) == SET) { DBG_DIRECT("LPC AON"); }