DLPS

概述

该示例通过使用不同外设进行深度低功耗状态唤醒(DLPS)。示例中包含的外设有 ADC / GPIOUARTLPCRTC 、AON_QDEC。

环境需求

该示例支持以下开发套件:

开发套件

Hardware Platforms

Board Name

RTL87x2G HDK

RTL87x2G EVB

更多信息请参考快速入门

配置选项

该示例可通过如下宏配置不同的DLPS示例:

  1. CONFIG_ADC_GPIO_DLPS_EN :PAD唤醒,在GPIO中断内进行ADC采样。

  2. CONFIG_UART_DLPS_EN :PAD唤醒,UART接收数据唤醒,唤醒后继续UART数据收发。

  3. CONFIG_RTC_DLPS_EN :RTC定时时间到唤醒系统。

  4. CONFIG_AON_QDEC_DLPS_EN :滚动Mouse滚轮唤醒系统。

  5. 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

请按照以下步骤操作构建并运行该示例:

  1. 打开工程文件。

  2. 按照 快速入门编译APP Image 给出的步骤构建目标文件。

  3. 编译成功后,在路径 mdk\bingcc\bin 下会生成 app bin app_MP_xxx.bin 文件。

  4. 按照 快速入门MPTool 给出的步骤将app bin烧录至EVB内。

  5. 按下复位按键,开始运行。

测试验证

  • 若开启 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中断。

代码介绍

该章节分为以下几个部分:

  1. 源码路径

  2. 初始化函数将在 初始化 章节介绍。

  3. 初始化后的功能实现将在 功能实现 章节介绍。

源码路径

  • 工程路径: 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_initboard_initdriver_initpwr_mgr_init


global_data_init 内包含了对全局变量的初始化。

  1. global_data_uart_init 内,将PowerCheck标志位设置为PASS,允许进入DLPS;重置UART接收长度,重置接收数组。

  2. 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初始化

  1. 通道0配置为VBAT采样,对应的Bitmap为0x01。

  2. 配置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初始化

  1. 配置GPIO为输入模式。

  2. 使能GPIO中断,配置下降沿触发。

  3. 配置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初始化

  1. 采用默认设置。

  2. 配置接收空闲中断 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初始化

  1. 设置RTC分频系数,时钟频率为10Hz。

  2. 使能RTC滴答中断。

  3. 使能RTC唤醒系统功能。

  4. 复位RTC计数值,启动RTC外设。

    RTC_SetPrescaler(RTC_PRESCALER_VALUE);
    RTC_INTConfig(RTC_INT_TICK, ENABLE);
    ...
    RTC_SystemWakeupConfig(ENABLE);
    RTC_ResetCounter();
    RTC_Cmd(ENABLE);
    

LPC初始化

  1. 设定LPC比较器输出极性。

  2. 设置电压阈值。

  3. 使能LPC功能。

  4. 使能LPC电压比较极性中断。

  5. 使能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初始化

  1. 使能X轴功能,使能去抖动功能。

  2. 使能AON_QDEC X轴新数据产生中断 AON_QDEC_X_INT_NEW_DATA 和X轴数据异常中断 AON_QDEC_X_INT_ILLEAGE

  3. 使能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 包含了电源管理的初始化。

  1. 注册用户进入DLPS回调函数 app_enter_dlps_config,注册用户退出DLPS回调函数 app_exit_dlps_config

  2. 注册硬件控制回调函数 DLPS_IO_EnterDlpsCbDLPS_IO_ExitDlpsCb ,进入DLPS会保存CPU、PINMUX、Peripheral等,退出DLPS会恢复CPU、PINMUX、Peripheral等。

  3. 设置电源模式为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回调函数。

  1. 在配置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回调函数。

  1. 在配置DLPS唤醒为PAD唤醒时(ADC/GPIO、UART):设置引脚为PINMUX Mode。

  2. 在配置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

  1. 当引脚P4_0输入低电平时,系统退出DLPS状态,当系统被唤醒时,进入 System_Handler

    1. 清除P4_0的唤醒中断挂起位。

    2. 失能P4_0的唤醒功能。

    3. 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;
    }
    
  2. 当引脚P4_0输入低电平时,触发 GPIO_PIN_INPUT 中断,进入中断处理函数 GPIO_Input_Handler

    1. 关GPIO中断,屏蔽GPIO中断。

    2. 使能ADC单次采样模式。

    3. 清除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);
    
  3. 当ADC单次转换完成时,触发 ADC_INT_ONE_SHOT_DONE 中断,进入中断处理函数 SAR_ADC_Handler

    1. 清除 ADC_INT_ONE_SHOT_DONE 中断挂起位。

    2. 读取采样数据,计算电压值。

    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

  1. 通过串口调试助手发送数据给IC时,UART_RX_PIN出现低电平,唤醒DLPS,进入 System_Handler

    1. 清除UART_RX_PIN的唤醒中断挂起位。

    2. 失能UART_RX_PIN的唤醒功能。

    3. 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;
    }
    
  2. 当UART收到数据时触发 UART_INT_RD_AVA 中断,或当UART空闲时触发 UART_INT_RX_IDLE 中断,进入中断处理函数 UART0_Handler

  1. 失能 UART_INT_RX_IDLE 中断。

  2. 将UART_RX_Buffer中的数据通过UART发送到PC端。

  3. 初始化UART全局数据。

  4. 判断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

  1. RTC设定时间到,唤醒DLPS。同时触发中断,进入中断处理函数 RTC_Handler ,打印信息,清除滴答中断。

    if (RTC_GetINTStatus(RTC_INT_TICK) == SET)
    {
       DBG_DIRECT("RTC_INT_TICK");
       RTC_ClearINTPendingBit(RTC_INT_TICK);
    }
    
  2. 每退出一次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

  1. 当AON_QDEC X轴检测到新数据时,唤醒系统,同时触发 AON_QDEC_X_INT_NEW_DATA 中断,进入中断处理函数 AON_QDEC_Handler

    1. 读X轴运动方向、计数值。

    2. 打印中断信息,滚轮滚动方向和次数。

    3. 清除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);
    
  2. 每退出一次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

  1. 调节直流稳压源电压值,当P2_4检测电压高于1000mV时,触发 LPC_INT_LPCOMP 中断,进入中断处理函数 LPCOMP_Handler

    1. 失能LPC_INT_LPCOMP中断。

    2. 清除LPC_INT_LPCOMP的标志。

    3. 判断是否为LPC_FLAG_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");
    }