DLPS

该示例通过使用不同外设进行深度低功耗状态唤醒(DLPS)。

示例中包含的外设有 ADCGPIOUARTLPCRTCAON QDEC

其中 ADC、GPIO 和 UART 的例子属于 PAD 唤醒,LPC、RTC、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 :电压比较满足条件唤醒。

硬件连线

  1. 若开启 CONFIG_ADC_GPIO_DLPS_EN

    P4_0 连接外部输入。

  2. 若开启 CONFIG_UART_DLPS_EN

    EVB 外接 FT232 模块,连接 P3_0 和 FT232 的 RX,P3_1 和 FT232 的 TX。

  3. 若开启 CONFIG_AON_QDEC_DLPS_EN

    EVB 外接转动传感器模块,连接 P1_3 和 PhaseA,P1_4 和 PhaseB。

  4. 若开启 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. 按照 快速入门MP Tool 给出的步骤将 app bin 烧录至 EVB 内。

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

测试验证

  1. 若开启 CONFIG_ADC_GPIO_DLPS_EN

    当引脚 P4_0 输入低电平时,唤醒系统,进入 GPIO 中断。在 GPIO 中断内使能 ADC 采样,打印 VBAT 电压。

  2. 若开启 CONFIG_UART_DLPS_EN

    串口助手给 MCU 发送消息,唤醒系统。再次发送消息,MCU 会回复相同的消息至串口助手。

  3. 若开启 CONFIG_RTC_DLPS_EN

    RTC 每计时 0.1s 退出 DLPS,进入 RTC TICK 中断,打印中断信息,再次进入 DLPS。20 次后,不再进入 DLPS 状态。

  4. 若开启 CONFIG_AON_QDEC_DLPS_EN

    滚动 Mouse 滚轮,唤醒系统,打印中断信息,滚轮滚动方向和次数。20 次后,不再进入 DLPS 状态。

  5. 若开启 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_init()board_init()driver_init()pwr_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_EnterDlpsCb()DLPS_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 中断,进入中断处理函数 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_FLAG_LPCOMP 的标志。

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