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
请按照以下步骤操作构建并运行该示例:
测试验证
-
若开启
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 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 中断,进入中断处理函数
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"); }