低功耗模式

概述

RTL87x2G支持四种功耗模式。

  • CPU Active
    在Power active的模式下,如果程序没有进入到idle task,CPU处于Active状态,CPU Clock会处于高速状态,默认是40M Clock。
  • CPU Sleep
    在Power active的模式下,程序进入idle task,CPU进入sleep状态,此时CPU clock会自动降速。如果CPU工作模式下处于40M Clock,CPU sleep时clock降低至625KHz。如果有使用 PLL clock,slow clock与PLL Clock source的选择有关。
  • DLPS
    DLPS 为系统休眠模式,较低的功耗,以及较快的进出时间,同时RAM内容保持。
  • Power Down
    极致低功耗模式。此模式下RAM内容不保持,从Power Down模式退出执行重启流程,较DLPS模式耗时更长。只有LPC和PAD(关闭debounce)可以唤醒Power Down。

不同功耗模式下各个模块使用情况如下,此文档将详细介绍DLPS模式。

不同功耗模式下各个模块使用情况

Mode

PAD

RAM

Bluetooth

32K clock

RTC

Peripheral

CPU

CPU Clock

CPU Active

40MHz

CPU Sleep

625KHz

DLPS

×

×

×

PowerDown

×

×

×

×

×

×

特性与限制

RTL87x2G DLPS模式有如下特点与限制:

  1. 系统可以快速进出DLPS模式。

    • 进入DLPS的时间小于1ms。

    • 蓝牙唤醒事件:退出DLPS的时间大约4ms。

    • 其他唤醒事件:退出DLPS的时间大约3ms。

  2. DLPS唤醒事件 可以将系统从DLPS状态唤醒。

  3. CPU断电会导致SWD断开连接,因此在使用JLink进行在线debug时,需要禁用DLPS模式。

原理

系统在大部分时间处于空闲状态,此时可以让系统进入DLPS模式以降低功耗。在DLPS模式下高速clock,CPU,Peripherals等模块会掉电,掉电前需要保存必要的数据用以恢复系统。当有事件需要处理时,系统会退出DLPS模式,并将高速clock,CPU,Peripherals等模块重新上电并恢复到进DLPS前的状态,然后响应唤醒事件。上述过程在系统注册的Callback函数里面自动处理,用户无须自行实现。

DLPS模式的进入与退出

DLPS进入条件

只有当同时满足以下条件时,系统才会进入DLPS模式。

  1. 系统执行在idle task,其余task均处在block状态或suspend状态,且没有中断发生。

  2. 蓝牙模块检查是否满足进入DLPS的条件:

    • Non-Link mode (当没有连接时,BT存在以下三种状态)

      状态

      说明

      Standby State

      系统不会收发数据,蓝牙会进入sleep状态,且不会将系统从DLPS状态唤醒。

      Advertising State

      (connectable or un-connectable)

      若Adv_Interval * 0.625ms >= 15ms,允许进入DLPS,否则不允许进入。

      若Advertising Type为Direct Advertising (High duty cycle),不允许进入DLPS。

      对于多广播,依然要满足条件Adv_Interval * 0.625ms >= 15ms后才能进入DLPS。

      Scanning State

      若(Scan Interval-Scan Window) * 0.625ms >= 15ms,允许进入DLPS,否则不允许进入。

    • Link mode (有以下两种角色)

      角色

      说明

      Master Role

      若Connection Interval * 1.25ms >= 15ms,允许进入DLPS

      Slave Role

      若Connection Interval * ( 1 + Slave Latency ) * 1.25ms >= 15ms,允许进入DLPS

      备注

      对于Multi-link mode,设备同时广播和连接,只有当当前时间到下次广播或者连接到期时间隔大于或者等于15ms,才允许进入DLPS。

  3. 平台模块检查是否满足条件:

    1. Peripheral,APP注册的check callback执行后均返回true。

    2. OS中的SW timer周期或者task delay时间大于或者等于20ms。

备注

  • 如果蓝牙模块允许进入DLPS模式,但平台模块不允许,则蓝牙模块进入DLPS模式,而平台模块保持active状态。

  • 如果蓝牙模块不允许进入DLPS模式,则整个系统将仍然处于active状态。

DLPS唤醒事件

系统可以由以下事件唤醒退出DLPS模式。

BT 唤醒

BT事件发生:
  1. BT处于advertising状态,且advertising anchor到来。

  2. BT处于connection状态,且connection anchor到来。

  3. BT处于scanning状态,且scaning anchor到来。

Platform 唤醒

  1. PAD唤醒信号

    1. PAD具有DLPS唤醒功能,可以调用 System_WakeUpPinEnable() 使能某个Pin的唤醒功能。当该Pin的电平与唤醒电平相同时,会将系统从DLPS状态唤醒。

    2. 例如希望P3_2为高电平时唤醒系统,可以做以下配置。

      System_WakeUpPinEnable(P3_2, PAD_WAKEUP_POL_HIGH, PAD_WAKEUP_DEB_DISABLE);
      
    3. 调用 System_WakeUpPinEnable() 时,将DebounceEn设置为1可以使能debounce唤醒功能。如果使能了debounce唤醒功能,则可以调用 System_WakeUpDebounceTime() 设置debounce time。如下设置debounce time为8ms(默认没有配置时,default value是0ms)。

      System_WakeUpDebounceTime(P3_2, 8);
      System_WakeUpPinEnable(P3_2, PAD_WAKEUP_POL_HIGH, PAD_WAKEUP_DEB_ENABLE);
      
    4. System irq是一个系统中断,其触发条件是 System_WakeUpPinEnable() 使能PAD唤醒,且当前pin脚的电平状态与唤醒电平一致。系统默认在进入DLPS后关闭system中断,退出DLPS后恢复system中断。因此如果是PAD唤醒DLPS,系统会在system中断恢复后,进入system中断的handler。

      1. 当debounce唤醒被关闭时,系统从DLPS醒来后,会触发system中断,用户可以在system中断handler里面调用 System_WakeUpInterruptValue() 来查询是哪个Pin唤醒了系统。之后需要调用 Pad_ClearWakeupINTPendingBit() 来清除该Pin的唤醒状态。

      2. 当debounce唤醒被打开时,系统从DLPS醒来后,会触发system中断。但是用户无法确定是哪个Pin唤醒的DLPS,只能在System中断handler里面调用 System_WakeupDebounceStatus() 获取debounce唤醒状态,之后调用 System_WakeupDebounceClear() 清除掉debounce唤醒状态。Debounce唤醒开启后,只有pin维持在唤醒电平状态的时间超过debounce time,才会唤醒DLPS,可以防止意外唤醒。但是缺点是,开启debounce唤醒后,无法检测具体是哪个pin在唤醒DLPS。

        备注

        虽然debounce相关的API里面的参数有pin num,但这里只是为了确保不同IC系列的接口兼容性,RTL87x2G并未支持debounce唤醒使能下对不同pin唤醒的检测。具体的使用示例,参考 DLPS使用示例 , 上面涉及到的相关接口的具体说明,详见 DLPS Mode APIs

  2. RTC中断(demo工程:samples\io_sample\rtc\rtc_dlps\proj\rtl87x2g\mdk

    Hardware唤醒需要额外调用以下接口:

    RTC_WKConfig(RTC_COMP_WK_INDEX, ENABLE) // enable Comparator wake up
    RTC_SystemWakeupConfig(ENABLE);
    

    Software唤醒实现方式参考:RTC唤醒的SW实现

  3. SW Timer timeout或者task delay事件。

  4. LPC中断(demo工程:samples\io_sample\lpc\voltage_detection_dlps\proj\rtl87x2g\mdk

    LPC唤醒需要调用以下API使能。

    LPC_WKCmd(ENABLE);
    
  5. AON Q-Decoder中断(demo工程:samples\io_sample\aon_qdec\aon_qdec_dlps\proj\rtl87x2g\mdk

    AON Q-Decoder唤醒需要调用以下API使能。

    AON_QDEC_INITMask(AON_QDEC, AON_QDEC_X_WAKE_AON_MASK, DISABLE);
    

DLPS模式进入流程

前置条件

某个模块如果希望进入DLPS前能够询问自己,需要预先向Power Manager注册Callback函数。当Power Manager执行Callback函数时,各模块再根据Callback函数的结果告知Power Manager是否允许进入DLPS。 蓝牙模块是基于平台模块的,因此只有蓝牙模块允许进入DLPS,才会继续检查平台模块。

进入流程
  1. 系统进入到idle task。

  2. 询问蓝牙模块是否能进入DLPS模式。

  3. 蓝牙模块状态保存和进入DLPS模式。

  4. 询问平台模块是否能进入DLPS模式。

  5. 平台模块状态保存和进入DLPS模式。

DLPS模式退出流程

系统从DLPS模式醒来后,首先会恢复power和高速clock,随后恢复CPU NVIC和Peripherals。只有平台模块完全退出DLPS后,才会检查其他模块是否需要退出DLPS。

  1. 系统恢复
    系统退出DLPS模式后,会触发Reset异常进入Reset Handler。在Reset Handler中会检查重启的原因。如果重新上电,系统会走重启流程。如果从DLPS模式唤醒,系统会走DLPS恢复流程,Platform首先恢复power和高速clock,最后进行OS的restore。
  2. 平台完全退出DLPS
    Platform恢复的最后阶段是在timer task中完成的,此时Platform完全退出DLPS,会完成CPU NVIC,peripherals的恢复及用户自定义退出callback函数的执行。
  3. 检查其他系统模块是否需要从DLPS中退出和恢复
    判断是否蓝牙事件唤醒DLPS。如果是,蓝牙模块退出DLPS并恢复状态;否则蓝牙模块继续保持低功耗状态。

硬件状态保存与恢复

CPU NVIC

系统进入DLPS后CPU会掉电,因此需要在进入DLPS前保存NVIC寄存器,并在退出DLPS后恢复NVIC寄存器。这样可以确保中断配置可以得到保存,并在DLPS退出后及时恢复中断配置。

SDK中默认CPU_DLPS_Enter和CPU_DLPS_Exit自动实现了CPU NVIC寄存器的保存和恢复,用户无须自行实现。

PAD

PAD在DLPS模式下不会掉电,因此不需要保存其状态。但是为了防止漏电,在进DLPS时需要对PAD做如下设置。

  1. 系统没有使用到的PAD,包括package没有出引脚的PAD必须设为{SW mode, Input mode, Pull Down}。这些是PAD的默认设置,因此用户无须更改。

  2. 系统使用到的PAD必须设为{SW mode, Input mode, Pull Up/Pull Down}。Pull Up还是Pull Down取决于外围电路。电压是VDD,需要Pull Up;电压是GND,需要Pull Down。

  3. 对于PAD的外围电路电压介于VDD和GND之间,需要把PAD设为{SW mode,Shut down mode,Pull None}。这样配置可以确保进入DLPS后,引脚与外围电路隔离以最小化漏电。

  4. 设置了唤醒功能的PAD需要设定为{SW mode, Input mode, Pull Up/Pull Down}。Pull Up/Pull Down状态要与唤醒信号的极性相反。如果一个引脚设置为拉低唤醒,则需要在进入DLPS前Pull Up。

  5. 退出DLPS时要把PAD设置恢复成原来的状态,确保PAD实现应用所需的功能,以避免产生问题。

外设

外设进DLPS时会掉电,所以进出DLPS时需要保存和恢复相关设定。退出DLPS时需要先重新使能外设模块并打开其时钟,再恢复相关设定。

外挂Sensor

外挂Sensor在进出DLPS时的处理分两种情况。

  1. 如果Sensor不掉电,则不用恢复。

  2. 如果Sensor掉电,则需要注册application callback函数,并在其中重新初始化Sensor。

状态保存流程

CPU,PINMUX和peripherals的状态会由系统自动保存。PAD状态如何设定取决于APP,因此需要在APP中调用 DLPS_IORegUserDlpsEnterCb() 注册Vendor Callback函数,并在该Vendor Callback函数中根据需要设定PAD状态。如果有外挂sensors需要在进DLPS前进行保存操作,也应该放在同样的vendor Callback函数中实现。

../../../../_images/IO_Store_Flow.PNG

硬件状态保存流程

恢复流程

退出DLPS时,系统会自动恢复CPU,PINMUX和peripherals。出DLPS时,PAD如何设定取决于APP,因此需要在APP中调用 DLPS_IORegUserDlpsExitCb() 注册Vendor Callback函数,在Vendor Callback函数里恢复PAD设定。如果有外部sensor在出DLPS后需要恢复的操作,也应该放在同样的Vendor Callback函数里实现。

../../../../_images/IO_Restore_Flow.PNG

硬件设定恢复流程

外设DLPS相关设定

每个APP project会有一个独立的board.h文件,其中包含如下硬件相关的DLPS设定。

/* if use user define DLPS enter/DLPS exit callback function */
#define USE_USER_DEFINE_DLPS_EXIT_CB         1
#define USE_USER_DEFINE_DLPS_ENTER_CB        1

/* if use any peripherals below, #define it  1 */
#define USE_ADC_DLPS                         0
#define USE_GPIOA_DLPS                       0
#define USE_I2C0_DLPS                        0
#define USE_I2C1_DLPS                        0
#define USE_IR_DLPS                          0
#define USE_KEYSCAN_DLPS                     0
#define USE_SPI0_DLPS                        0
#define USE_SPI1_DLPS                        0
#define USE_UART0_DLPS                       0
#define USE_UART1_DLPS                       0
#define USE_ENHTIM_DLPS                      0

如果APP中使用了某个外设,且需要在进出DLPS时,系统能自动保存、恢复其状态,则需要将该外设对应的USE_XXX_DLPS宏设定为‘1’。对于部分外设比如GDMA,需要在DLPS exit callback function里重新调用init函数。

APP还需要在PwrMgr_Init()调用如下API来注册IO DLPS Callback函数,系统会在该Callback函数里自动完成相关外设的保存和恢复。

DLPS_IORegister();

如果需要在进出DLPS时做一些APP自定义的操作,则需要设定以下两处:

  1. 在board.h中将USE_USER_DEFINE_DLPS_EXIT_CB与USE_USER_DEFINE_DLPS_ENTER_CB配置为1。

  2. 在APP中调用如下API来注册并实现vendor callback函数。

    void DlpsExitCallback(void)
    {
        //do something here
    }
    
    void DlpsEnterCallback(void)
    {
        //do something here
    }
    
    DLPS_IORegUserDlpsExitCb(DlpsExitCallback);
    DLPS_IORegUserDlpsEnterCb(DlpsEnterCallback);
    

在上面的例子中,在进入和退出DLPS的过程中会分别执行DlpsEnterCallback()和DlpsExitCallback(),APP可以在这两个函数中完成自定义操作,如PAD设置、外设设备的操作等。

DLPS使用示例

关闭Debounce下的PAD唤醒

注册DLPS check, enter and exit vendor callback函数,拉低P3_2以唤醒DLPS。

bool DLPS_Check(void)
{
    return true;
}

void EnterDlpsSet(void)  //DLPS Enter
{
    Pad_Config(P3_2, PAD_SW_MODE, PAD_IS_PWRON,
      PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW);
    System_WakeUpPinEnable(P3_2, PAD_WAKEUP_POL_LOW,
      PAD_WAKEUP_DEB_DISABLE);
}

void ExitDlpsInit(void)  //DLPS Exit
{
    Pad_Config(P3_2, PAD_PINMUX_MODE, PAD_IS_PWRON,
      PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW);
}

void pwr_mgr_init(void)
{
#if DLPS_EN
    power_check_cb_register (DLPS_Check);
    DLPS_IORegUserDlpsEnterCb(EnterDlpsSet);  //DLPS Enter CB
    DLPS_IORegUserDlpsExitCb(ExitDlpsInit);  //DLPS Exit CB
    DLPS_IORegister();
    bt_power_mode_set(BTPOWER_DEEP_SLEEP);
    power_mode_set(POWER_DLPS_MODE);
#endif
}

定义System中断handler以检测哪一个pin唤醒DLPS。

void System_Handler(void)
{
    APP_PRINT_INFO0("System_Handler");
    NVIC_DisableIRQ(System_IRQn);

    if (System_WakeUpInterruptValue(P3_2) == SET)
    {
        APP_PRINT_INFO0("P3_2 Wake up");
        Pad_ClearWakeupINTPendingBit(P3_2);
        System_WakeUpPinDisable(P3_2);
    }

    NVIC_ClearPendingIRQ(System_IRQn);
}

打开Debounce下的PAD唤醒

注册DLPS check, enter 和exit vendor callback函数,拉低P3_2以唤醒DLPS,设置8ms debounce。

bool DLPS_Check(void)
{
    return true;
}

void EnterDlpsSet(void)
{
    Pad_Config(P3_2, PAD_SW_MODE, PAD_IS_PWRON,
      PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW);
    System_WakeUpPinDisable(P3_2);
    System_WakeUpDebounceTime(P3_2, 8);
    System_WakeUpPinEnable(P3_2, PAD_WAKEUP_POL_LOW, PAD_WAKEUP_DEB_ENABLE);
}

void ExitDlpsInit(void)
{
    Pad_Config(P3_2, PAD_PINMUX_MODE, PAD_IS_PWRON,
      PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW);
}

void pwr_mgr_init(void)
{
#if DLPS_EN
    power_check_cb_register(DLPS_Check);
    DLPS_IORegUserDlpsEnterCb(EnterDlpsSet);
    DLPS_IORegUserDlpsExitCb(ExitDlpsInit);
    DLPS_IORegister();
    bt_power_mode_set(BTPOWER_DEEP_SLEEP);
    power_mode_set(POWER_DLPS_MODE);
#endif
}

当debounce唤醒DLPS使能后,System中断handler不能判断具体是哪个pin唤醒的DLPS,只能判断是否存在debounce唤醒DLPS。因此只需要clear debounce status。

void System_Handler(void)
{
    APP_PRINT_INFO0("System_Handler");
    NVIC_DisableIRQ(System_IRQn);

    if(System_WakeupDebounceStatus(P3_2) == SET)
    {
      System_WakeupDebounceClear(P3_2);
        DBG_DIRECT("debounce Wake up");
    }
    NVIC_ClearPendingIRQ(System_IRQn);
}

RTC唤醒的SW实现

RTC HW唤醒DLPS,需要RTC到期后,系统从DLPS退出,执行上电后restore动作,最后再触发RTC中断。这样导致RTC中断产生的时间被延迟了,其时间为DLPS退出的时间。

如果需要更高精度的RTC中断,可以采用SW唤醒DLPS,退出DLPS后,RTC到期执行中断。因为SW唤醒DLPS,会考虑到DLPS退出的延迟而提前唤醒DLPS,因此RTC中断会更为准确。

具体做法如下。

  1. 取消调用RTC唤醒使能的接口,以关闭RTC中断HW唤醒。

  2. 在callback中增加next_wake_up_time指针参数,计算出下次唤醒的时间(单位32.15us),将计算得到的值更新到next_wake_up_time中,由platform触发下次唤醒。

uint32_t RTC_tick; // unit: 32.15us
POWER_CheckResult RTC_Check_GT(uint32_t *next_wake_up_time)  //unit 31.25us
{
    uint32_t wake_up_count = RTC_GetCompValue(RTC_COMP_INDEX) - RTC_GetCounter();
    if(wake_up_count > 0)
    {
        *next_wake_up_time = wake_up_count * RTC_tick;
        return POWER_CHECK_PASS;
    }
    else
    {
        return POWER_CHECK_FAIL;
    }
}
  1. 注册DLPS check callback。

RTC_tick = (RTC_PRESCALER_VALUE + 1);
power_check_cb_register(RTC_Check_GT);

DLPS Mode APIs