Low Power Development Guide

1 Introduction

SiFli MCUs are dual‑core Cortex‑M33 STAR SoCs. The high‑performance core (HCPU) runs at 0–240 MHz and belongs to the HPSYS subsystem, suitable for graphics, audio, neural networks, and other compute‑intensive workloads. The low‑power core (LCPU) runs at 0–48 MHz and belongs to the LPSYS subsystem, suitable for Bluetooth, sensor data collection, and processing tasks.

Refer to the low‑power example at example\pm\classical.

2 Configure Low‑Power Mode

2.1 Enable Low‑Power Mode

Run sdk.py menuconfig in the project directory to open the configuration menu:

  1. Enable low‑power support (Enable Low power support):

    • Path: Sifli middleware → Enable Low power support

    • Toggle: Enable Low power support

      • Macro: BSP_USING_PM

      • Purpose: Enable low‑power features

../_images/enable_pm.png

Figure: Enable Low Power configuration menu

  1. Select low‑power mode (Enable Deep Mode):

    • Path: RTOS → RT‑Thread Components → Device Drivers → Using Power Management device drivers → Select PM Mode

    • Select: Enable Deep Mode

      • Macro: PM_DEEP_ENABLE

      • Purpose: Use Deep Sleep mode as the low‑power mode

../_images/deep.png

Figure: Deep Sleep configuration menu

  1. Enable PM debug to print low‑power logs (optional; printing logs consumes time and affects power) (Enable PM Debug):

    • Path: Sifli middleware → Enable Low power support → Enable PM Debug

      • Select: Enable PM Debug

        • Macro: BSP_PM_DEBUG

        • Purpose: Enable low‑power debug logging

../_images/low_power11.png

Figure: PM debug configuration menu

  1. After configuration, verify rtconfig.h includes the following:

#define RT_USING_PM 1           // Enable PM module
#define PM_DEEP_ENABLE 1        // Use DEEP sleep mode
#define BSP_USING_PM 1          // Enable PM module
#define BSP_PM_DEBUG 1          // Print PM[S], PM[W] logs (optional)

2.2 Disable Low‑Power Mode

Run sdk.py menuconfig and uncheck the same options used to enable low power.

3 Wake‑Up by Button Configuration

Refer to SDK\example\pm\classical for the configuration method.

Standby wake‑up configuration (for standby/deep wake‑up)

Below is an HAL‑level standby wake‑up API example. You can use HAL_LPAON_QueryWakeupPin() and HAL_HPAON_QueryWakeupPin() to get the wake‑up pin index. If the IO wake‑up needs event handling, also configure the GPIO interrupt:

In 52 series DeepSleep mode, no extra wake‑up pin configuration is required. Any pin can wake up, via WSR_GPIO1.

Power‑off wake‑up configuration (for hibernate wake‑up)

Post‑55 series MCUs: Allow two wake‑up sources PIN0 and PIN1 at the same time; each source can map to any HCPU/LCPU wake pin. See PMUC CR register in the User Manual.

// 58x/56x/52x configuration:
HAL_PMU_SelectWakeupPin(0, HAL_HPAON_QueryWakeupPin(hwp_gpio1, BSP_KEY1_PIN)); // select PA34 to wake_pin0
HAL_PMU_EnablePinWakeup(0, AON_PIN_MODE_HIGH);                                  // enable wake_pin0 
rt_kprintf("CR:0x%x,WER:0x%x\n", hwp_pmuc->CR, hwp_pmuc->WER);

After Hibernate power‑off, wake‑up is equivalent to a cold boot (but with the PM_HIBERNATE_BOOT flag), unlike Standby which resumes previous context. Wake‑up pin and level mode are controlled by PMU registers; you can print WER/CR to verify. If the same IO is used for both standby and hibernate wake‑up, both configurations must apply.

4 Low‑Power Debugging Methods

4.1 Low‑Power Modes

  • PM_SLEEP_MODE_IDLE: CPU enters idle (WFI/WFE). High‑speed clocks (HRC/HXT/DBLR/DLL) remain on; all peripherals can stay enabled and generate interrupts.

  • PM_SLEEP_MODE_LIGHT: CPU enters light sleep (WFI). High‑speed clocks are turned off. CPU‑related peripherals stop but remain powered. The system clock switches to 32K. Wake‑up sources: LPTIM, RTC, BLE MAC (LCPU Only), Mailbox (other CPU), or pin. Wake‑up latency: 30–100 µs. Execution resumes at the instruction after WFI.

  • PM_SLEEP_MODE_DEEP: Same as LIGHT, but system power switches to RET_LDO; wake‑up latency increases to 100 µs–1 ms. Execution resumes at the instruction after WFI.

  • PM_SLEEP_MODE_STANDBY: CPU enters standby. High‑speed clocks are off; CPU‑related peripherals lose power; RAM power is removed except for configured retention parts; pin states are retained; system power switches to RET_LDO. Wake‑up sources: LPTIM, RTC, BLE MAC (LCPU only), Mailbox (other CPU), or specific wake‑up pins. Wake‑up latency: 1–2 ms. After wake‑up the system restarts; software checks AON registers to determine standby boot vs. cold boot.

Typical currents for each mode are shown in Table 4‑1. If PSRAM exists, HCPU backs up data that must be retained from powered‑down RAM to PSRAM and restores it after wake‑up; otherwise it backs up to 64 KB Retention RAM. Unless otherwise stated, “enter sleep” refers to entering low‑power modes other than IDLE, and “wake‑up” refers to exiting low‑power modes other than IDLE.

Table 4‑1: Low‑power modes

Low‑Power Mode

CPU State

Peripheral State

SRAM

Wake‑up Source

Wake‑up Latency

PM_SLEEP_MODE_IDLE

stop

run

Accessible

Any interrupt

<1 µs

PM_SLEEP_MODE_DEEP

stop

stop

LPSYS: inaccessible, fully retained
HPSYS: inaccessible, fully retained

RTC, wake pin, IO(PA),
LPTIM1, BLE

~250 µs

PM_SLEEP_MODE_STANDBY

reset

reset

LPSYS: inaccessible, fully retained
HPSYS: inaccessible, 384 KB retained only

RTC, wake pin,
LPTIM1, BLE

~1 ms

4.2 Power‑Off Modes

In addition to the four low‑power modes per subsystem, the chip provides two system‑level power‑off modes:

  • Hibernate: All subsystems powered down; system clock switches to 32K crystal; wake‑up by PIN and RTC (RTC wake‑up time accurate). API: HAL_PMU_EnterHibernate.

  • Shutdown: All subsystems powered down; system clock switches to RC10K; wake‑up by PIN and RTC (RTC wake‑up time not accurate). API: HAL_PMU_EnterShutdown.

Table 4‑2: Power‑off modes

Mode

CPU State

Peripheral State

SRAM

IO

Wake‑up Source

Wake‑up Time

Hibernate

reset

reset

Not retained

Hi‑Z

RTC and PIN

>2 ms

Shutdown

reset

reset

Not retained

Hi‑Z

RTC and PIN

>2 ms

Note: Current values are for reference only; actual numbers vary with enabled peripherals and IO settings. “stop” means halted but resumes without reconfiguration; “reset” means CPU resets (starts from ROM) and peripherals need reinitialization.

If sleep is not possible but further power reduction is needed, see WFI auto down‑clocking and scenario‑based DVFS.

4.3 WFI Auto Down‑Clocking

When the IDLE thread runs but sleep conditions are not met, reduce HCPU frequency to lower current during WFI. Prerequisite: all high‑speed peripherals are idle. High‑speed peripherals include:

  • EPIC

  • EZIP

  • LCDC

  • USB

  • SD

Notes: Busy detection for EPIC/EZIP is built into the SDK’s LVGL implementation. If using a custom implementation, call rt_pm_hw_device_start/rt_pm_hw_device_stop when peripherals start/stop to avoid down‑clocking during busy periods. Busy detection for LCDC/USB/SD is integrated in RT‑Thread’s LCD Device driver.

The WFI frequency after down‑clocking is configured by HAL_RCC_HCPU_SetDeepWFIDiv. When audio peripherals are active, it can only drop to 48 MHz; otherwise down to 4 MHz. Also set HPSYS_RCC_DBGR_FORCE_HP in hwp_hpsys_rcc->DBGR to 1.

4.4 Scenario‑Based DVFS

In low‑performance scenarios, the high‑performance core can reduce frequency/voltage to cut active power (e.g., after screen off, wrist‑raise algorithm only could run at 48 MHz). Although lower frequency increases execution time, total energy (current × time) may be lower. Measure and pick optimal modes per scenario.

Use rt_pm_run_enter to set the current run mode. HCPU supports four modes (app default is PM_RUN_MODE_HIGH_SPEED); switching to higher speed takes effect immediately, switching to lower speed is deferred until the IDLE thread:

Mode

System Clock (MHz)

PM_RUN_MODE_HIGH_SPEED

240

PM_RUN_MODE_NORMAL_SPEED

144

PM_RUN_MODE_MEDIUM_SPEED

48

PM_RUN_MODE_LOW_SPEED

24

SDK also provides pm_scenario_start/pm_scenario_stop for scenario toggles. Currently supports UI and Audio:

  • Either on → use HIGH_SPEED

  • Both off → use MEDIUM_SPEED

4.5 Low‑Power Flow

In this solution, HPSYS can enter sleep only after the screen turns off. With screen on, when HCPU is idle, HPSYS can only enter IDLE. LPSYS can enter sleep only after HPSYS sleeps; if HPSYS is awake, LPSYS stays in IDLE even if LCPU is idle (52 series exception: HCPU/LCPU can sleep independently). When HPSYS is asleep, LPSYS can freely enter/exit sleep without waking HPSYS.

4.5.1 Screen‑Off

Lock‑screen timeout is configurable in settings. When no interaction exceeds the timeout, the screen turns off; the IDLE thread checks sleep conditions. If met, HPSYS sleeps, and LPSYS can then sleep as well.

../_images/low_power_english13.png

Figure 4.1 Screen‑off flow

4.5.2 HPSYS Wake‑Up

HPSYS can be woken by LPTIM, RTC, BLE MAC (LCPU only), Mailbox (other CPU), or any pin in DeepSleep. For example, press a button to wake HPSYS.

For button wake to turn the screen on, see Figure 4.2. After the screen turns on, a new screen‑off cycle begins. A wake‑up flow triggered by a smartphone app “setting” event is shown in Figure 4.3; after handling the request, the IDLE thread can immediately re‑enter sleep.

../_images/low_power_english14.png

Figure 4.2 Button wake and screen‑off cycle

../_images/low_power_english25.png

Figure 4.3 Wake‑up flow on phone Setting event

Context retention in DEEPSLEEP/STANDBY:

  • HCPU: In DEEPSLEEP, all RAM retained; restore directly from RAM after wake‑up.

  • LCPU: Keeps all RAM powered; saves CPU registers/context in RAM; restores directly from RAM on wake‑up.

4.6 Log Interpretation

HCPU and LCPU print logs via console. After enabling low‑power debug in Section 2.1, search the following keywords to analyze flows.

Table 4‑3: Log keywords

Log

Meaning

gui_suspend

Screen off

gui_resume

Screen on

[pm]S: mode,gtime

Enter sleep; mode=2 means LIGHT, 4 means STANDBY; gtime unit is 32768 Hz

[pm]W: gtime

Exit sleep; gtime unit is 32768 Hz

[pm]WSR:0xXXX

Wake‑up reason (decode by register bitfields)

gtime is synchronized between HCPU and LCPU. Example: sleep at 2136602 and wake at 2142330 → sleep_time=(2142330-2136602)/32768=175 ms. If WSR=0x200, wake‑up was by LPSYS mailbox interrupt. See the datasheet for bit meanings.

../_images/low_power15.png

Figure 4.4 Low‑power log example

  • HPSYS WSR bits

Bit

Description

[0]

RTC wake‑up

[1]

GPIO1 wake‑up

[2]

LPTIM1 wake‑up

[3]

PMUC wake‑up

[6]

LPSYS manual wake‑up HPSYS

[7]

LPSYS wake HPSYS via Mailbox

[8]

PIN0 wake‑up

[9]

PIN1 wake‑up

[10]

PIN2 wake‑up

[11]

PIN3 wake‑up

[18]

PIN10 wake‑up

[19]

PIN11 wake‑up

[20]

PIN12 wake‑up

[21]

PIN13 wake‑up

[22]

PIN14 wake‑up

[23]

PIN15 wake‑up

[24]

PIN16 wake‑up

[25]

PIN17 wake‑up

[26]

PIN18 wake‑up

[27]

PIN19 wake‑up

[28]

PIN20 wake‑up

Table 4‑4: 52 series HPSYS wake‑up PIN mapping (partial)

Wake

PIN

PIN0

PA24

PIN1

PA25

PIN10

PA34

PIN11

PA35

PIN19

PA43

4.7 Common Issues Analysis

Since SWD cannot connect in sleep, use UART console logs to analyze issues.

4.7.1 Whether Sleep Was Entered

Likely HPSYS slept if any of:

  • SWD cannot connect

  • HCPU console no response

  • HCPU logs show “S: mode, gtime”

Likely LPSYS slept if any of:

  • LCPU console no response

  • LCPU logs show “S: mode, gtime”

Ensure the finsh shell option is enabled in LCPU Command shell.

You can also check chip power pins to determine current low‑power mode:

  • HPSYS active/sleep/deepsleep: LDO1_VOUT ≈ 1.1 V; HPSYS standby: LDO1_VOUT gradually drops to 0 V.

  • LPSYS active/sleep/deepsleep: LDO2_VOUT or BUCK2_VOUT ≈ 0.9 V; LPSYS standby: the voltage gradually drops to 0 V.

  • Hibernate: LDO1_VOUT/LDO2_VOUT/BUCK2_VOUT/VDD_RET all drop to 0 V.

4.7.2 Why Sleep Didn’t Happen

Sleep entry conditions

For applications, sleep/work switching is transparently controlled by the lowest priority IDLE thread: when all higher‑priority threads are idle, IDLE runs and checks if all the following are met before entering sleep:

  • Sleep not forbidden (no outstanding rt_pm_request(PM_SLEEP_MODE_IDLE))

  • The soonest OS timer expiration > threshold (default 100 ms)

  • No wake condition currently satisfied (e.g., enabled wake source not active)

  • Data sent to the other core has been consumed (no unread data in IPC queues)

Timed wake configuration before sleep

Before sleep, configure LPTIM to interrupt at the soonest OS timer expiration so the timer callback fires on time during sleep. Example: if the next timer expires in 200 ms, set LPTIM to interrupt in 200 ms.

Forbid/release sleep APIs

Apps can explicitly suppress sleep in critical sections; the driver framework also suppresses sleep while peripherals are active to avoid sleeping during interrupt operations:

// Forbid sleep until release
rt_pm_request(PM_SLEEP_MODE_IDLE);
// ... critical section/peripheral operations ...
rt_pm_release(PM_SLEEP_MODE_IDLE);

Timer thresholds and policy

Refer to Power Management configuration for enabling low‑power support and selecting policies to enter different low‑power modes. The current default policy is:

Default policy example:

static const pm_policy_t default_pm_policy[] =
{
    {15, PM_SLEEP_MODE_LIGHT},                  // Idle > 15 ms → Light sleep
#ifdef PM_STANDBY_ENABLE
    {10000, PM_SLEEP_MODE_STANDBY},             // Idle > 10 s → Standby
#endif /* PM_STANDBY_ENABLE */
};

Common checks (HCPU/LCPU similar):

  1. Enable PM and verify macros:

#define RT_USING_PM 1
#define BSP_USING_PM 1          // Enable low‑power
#define PM_DEEP_ENABLE 1        // Use Deep low‑power
#define BSP_PM_DEBUG 1          // Enable low‑power debug logs
  1. Confirm CPU is idle and in the idle thread:

  • Use finsh list_thread to check threads; except tshell and tidle ready, others should be suspend, otherwise IDLE cannot run.

../_images/low_power_english17.png

Figure 4.6 Output of list_thread

  1. Confirm sleep is not forbidden:

  • In console, run pm_dump. If “Idle Mode Counter” > 0, some module called rt_pm_request(PM_SLEEP_MODE_IDLE) to forbid sleep; call rt_pm_release(PM_SLEEP_MODE_IDLE) to release.

../_images/low_power18.png

Figure 4.7 Output of pm_dump

  1. Confirm OS timer expirations exceed the sleep threshold: In console, run list_timer to show created timers. Compare the timeout of timers with flag activated against the sleep threshold; if smaller, that timer prevents sleep. Timeout unit is ms.

../_images/low_power19.png

Figure 4.8 Output of list_timer

Default thresholds: HPSYS sleep threshold 100 ms; LPSYS sleep threshold 10 ms.

RT_WEAK const pm_policy_t pm_policy[] =
{
#ifdef PM_STANDBY_ENABLE
#ifdef SOC_BF0_HCPU
    {100, PM_SLEEP_MODE_STANDBY}, // HCPU: no timer wake within 100 ms → Standby
#else
    {10, PM_SLEEP_MODE_STANDBY},  // LCPU: no timer wake within 10 ms → Standby
#endif /* SOC_BF0_HCPU */
#elif defined(PM_DEEP_ENABLE)
#ifdef SOC_BF0_HCPU
    {100, PM_SLEEP_MODE_DEEP},    // HCPU: no timer wake within 100 ms → Deep
#else
    {10, PM_SLEEP_MODE_DEEP},     // LCPU: no timer wake within 10 ms → Deep
#endif /* SOC_BF0_HCPU */
#else
#ifdef SOC_BF0_HCPU
    {100, PM_SLEEP_MODE_LIGHT},
#else
}

If HCPU code uses a 90 ms periodic delay, the system will never enter sleep:

while (1)
{
    rt_thread_delay(90); // 90ms delay
}

Differences between delay functions:

  • HAL layer (no thread switch during delay):

HAL_Delay(10);     // 10ms
HAL_Delay_us(10);  // 10us
  • RT‑Thread interface (switches to other threads; may trigger idle→sleep):

rt_thread_delay(100); // 100ms
  1. Ensure no pending wake‑up sources:

  • Read registers via console commands to check WER/WSR:

52 series WSR addresses:

regop unlock 0000
regop read 40040024 1   # LPSYS WSR
regop read 500c0024 1   # HPSYS WSR
  • You can also use Jlink/SifliUsartServer to read registers or print via logs:

rt_kprintf("wsr:0x%x,wer:0x%x,\n", hwp_hpsys_aon->WSR, hwp_hpsys_aon->WER); // hcpu
rt_kprintf("wsr:0x%x,wer:0x%x,\n", hwp_lpsys_aon->WSR, hwp_lpsys_aon->WER); // lcpu

Common issue: wake‑up pin level misconfigured (e.g., configured for low‑level wake‑up but the pin remains low).

  1. Ensure data sent to the other core has been consumed:

  • Use Ozone, memory dump with trace32, or log ipc_ctx queue read_idx_mirror/write_idx_mirror; inequality indicates unread data that blocks sleep.

../_images/low_power_english20.png

Figure 4.9 Non‑empty ring buffer

../_images/low_power_english21.png

Figure 4.10 Empty ring buffer

Print activation queue index:

for (i = 0; i < IPC_LOGICAL_QUEUE_NUM; i++)
{
    if (ipc_ctx.queues[i].active == true)
    {
        if (ipc_ctx.queues[i].rx_ring_buffer && ipc_ctx.queues[i].tx_ring_buffer)
        {
            LOG_I("ipc_ctx.queues[%d].tx read_idx_mirror=0x%x, write_idx_mirror=0x%x\n",
                  i,
                  ipc_ctx.queues[i].tx_ring_buffer->read_idx_mirror,
                  ipc_ctx.queues[i].tx_ring_buffer->write_idx_mirror);
        }
    }
}
../_images/low_power_english22.png

Figure 4.11 Example: LCPU data service disabled causing missing channel

5 Power Optimization Methods

5.1 Standby Leakage Analysis

When both HPSYS and LPSYS are in sleep, focus on system power optimization:

  • Remove detachable components (display, sensors, charging IC) and measure the minimum system current.

  • Misconfigured software IO levels can cause voltage differences/floating leakage.

  • On‑chip PSRAM/Flash and external NAND/Flash/eMMC may not have entered sleep.

If hardware allows measuring current per rail, identify the leaking rail (VSYS/ VLDO2/ VLDO3/ VDD_SIP/ VDDIOA) to narrow the scope.

5.1.1 Peripheral Leakage

Common causes:

  1. Board‑level components are not powered off.

  2. Board‑level components are powered off, but chip pin configuration causes back‑powering from the chip pins.

For case 2), avoid: driving high or enabling pull‑up on pins connected to powered‑off devices. Recommended pin settings for common peripherals in active/sleep are below (no change needed if external circuits stay powered; switch to low‑power configuration if external circuits are powered off).

Table 5‑1: Recommended Pin Settings

Peripheral

Pin

Dir

Active

Sleep (external on)

Sleep (external off)

PSRAM

PSRAM_CLK

O

Digital out

Digital out

GPIO mode output low

PSRAM

PSRAM_CLKB

O

Digital out

Digital out

GPIO mode output low

PSRAM

PSRAM_CS

O

Digital out

Digital out

GPIO mode output low

PSRAM

PSRAM_DM0

O

Digital out

Digital out

GPIO mode output low

PSRAM

PSRAM_DM1

O

Digital out

Digital out

GPIO mode output low

PSRAM

PSRAM_DQS0

I/O

Digital in‑pd

Digital in‑pd

Digital in‑pd

PSRAM

PSRAM_DQS1

I/O

Digital in‑pd

Digital in‑pd

Digital in‑pd

PSRAM

PSRAM_DQx

I/O

Digital in‑pd

Digital in‑pd

Digital in‑pd

QSPI

QSPIx_CLK

O

Digital out

Digital out

GPIO mode output low

QSPI

QSPIx_CS

O

Digital out

Digital out

GPIO mode output low

QSPI

QSPIx_DIO0

I/O

Digital in‑pd

Digital in‑pd

Digital in‑pd

QSPI

QSPIx_DIO1

I/O

Digital in‑pd

Digital in‑pd

Digital in‑pd

QSPI

QSPIx_DIO2

I/O

Digital in‑pu

Digital in‑pu

Digital in‑pd

QSPI

QSPIx_DIO3

I/O

Digital in‑pu

Digital in‑pu

Digital in‑pd

QSPI

QSPIx_DIO4

I/O

Digital in‑pd

Digital in‑pd

Digital in‑pd

QSPI

QSPIx_DIO5

I/O

Digital in‑pd

Digital in‑pd

Digital in‑pd

QSPI

QSPIx_DIO6

I/O

Digital in‑pu

Digital in‑pu

Digital in‑pd

QSPI

QSPIx_DIO7

I/O

Digital in‑pu

Digital in‑pu

Digital in‑pd

USART

USARTx_RXD

I

Digital in‑pu

Digital in‑pu

Digital in‑pd

USART

USARTx_TXD

O

Digital out

Digital out

Digital out

USART

USARTx_CTS

I

Digital in‑pu

Digital in‑pu

Digital in‑pd

USART

USARTx_RTS

O

Digital out

Digital out

Digital out

I2C

I2Cx_SCL

I/O

Digital in

Digital in

Digital in‑pd

I2C

I2Cx_SDA

I/O

Digital in

Digital in

Digital in‑pd

SPI M

SPIx_CLK

O

Digital out

Digital out

GPIO mode output low

SPI M

SPIx_CS

O

Digital out

Digital out

GPIO mode output low

SPI M

SPIx_DI

I

Digital in‑pd

Digital in‑pd

Digital in‑pd

SPI M

SPIx_DO

O

Digital out

Digital out

GPIO mode output low

SPI M

SPIx_DIO

I/O

Digital in‑pd

Digital in‑pd

Digital in‑pd

LCDC SPI

LCDCx_SPI_CS

O

Digital out

Digital out

GPIO mode input pull‑down

LCDC SPI

LCDCx_SPI_CLK

O

Digital out

Digital out

GPIO mode input pull‑down

LCDC SPI

LCDCx_SPI_DIO0

I/O

Digital in‑pd

Digital in‑pd

GPIO mode input pull‑down

LCDC SPI

LCDCx_SPI_DIO1

O

Digital out

Digital out

GPIO mode input pull‑down

LCDC SPI

LCDCx_SPI_DIO2

O

Digital out

Digital out

GPIO mode input pull‑down

LCDC SPI

LCDCx_SPI_DIO3

O

Digital out

Digital out

GPIO mode input pull‑down

LCDC SPI

LCDCx_SPI_RSTB

O

Digital out

Digital out

GPIO output low

LCDC SPI

LCDCx_SPI_TE

I

Digital in

Digital in

GPIO mode input pull‑down

SDIO

SD_CLK

O

Digital out

Digital out

GPIO mode output low

SDIO

SD_CMD

I/O

Digital in‑pu

Digital in‑pu

Digital in‑pd

SDIO

SD_DIOx

I/O

Digital in‑pu

Digital in‑pu

Digital in‑pd

I2S

I2S1_BCK

O

Digital out

Digital out

GPIO mode output low

I2S

I2S1_LRCK

O

Digital out

Digital out

GPIO mode output low

I2S

I2S1_SDI

I

Digital in‑pd

Digital in‑pd

Digital in‑pd

I2S

I2S2_BCK

O

Digital out

Digital out

GPIO mode output low

I2S

I2S2_LRCK

O

Digital out

Digital out

GPIO mode output low

I2S

I2S2_SDI

I

Digital in‑pd

Digital in‑pd

Digital in‑pd

I2S

I2S2_SDO

O

Digital out

Digital out

GPIO mode output low

PDM

PDM_CLK

O

Digital out

Digital out

GPIO mode output low

PDM

PDM_DATA

I

Digital in‑pd

Digital in‑pd

Digital in‑pd

GPTIM Out

GPTIMx_CHx

O

Digital out

Digital out

GPIO mode output low

GPTIM In

GPTIMx_CHx

I

Digital in‑pd

Digital in‑pd

Digital in‑pd

GPTIM

GPTIMx_ETR

I

Digital in‑pd

Digital in‑pd

Digital in‑pd

GPIO In

GPIO

I

Digital in

Digital in

GPIO output low or digital in‑pd

GPIO Out

GPIO

O

Digital out

Digital out

GPIO mode output low

5.1.2 On‑Chip IO Internal Leakage

Common patterns (see FAQ 8.7/8.8):

  1. Input pins floating (peer device powered off equals floating), causing undefined levels.

  2. IO output levels mismatch with internal/external pull‑ups or pull‑downs.

Below shows the pin internal structure (functional blocks: DS/OE/O/IE/PE/PS, etc.).

../_images/low_power23.png

Figure 5.1 Pin internal structure

5.1.3 On‑chip/External Memory Leakage

PSRAM half‑sleep entry/exit example:

void BSP_Power_Up(bool is_deep_sleep)
{
#ifdef SOC_BF0_HCPU
    if (!is_deep_sleep)
    {
#if defined(BSP_USING_PSRAM1)
        rt_psram_exit_low_power("psram1"); // exit half_sleep
#endif
    }
    // ...
}

void BSP_IO_Power_Down(int coreid, bool is_deep_sleep)
{
#ifdef SOC_BF0_HCPU
    if (coreid == CORE_ID_HCPU)
    {
#if defined(BSP_USING_PSRAM1)
        rt_psram_enter_low_power("psram1");  // enter half_sleep
#endif
    }
#else
    // ...
#endif
}

NOR Flash power‑off and Deep Sleep example:

HAL_RAM_RET_CODE_SECT(BSP_PowerDownCustom, void BSP_PowerDownCustom(int coreid, bool is_deep_sleep))
{
#ifdef SOC_BF0_HCPU
#ifdef BSP_USING_NOR_FLASH2
    HAL_PMU_ConfigPeriLdo(PMU_PERI_LDO2_3V3, false, true); // turn off NOR flash power

    HAL_PIN_Set(PAD_PA16, GPIO_A16, PIN_PULLDOWN, 1); // after power off, set IO to pull‑down
    HAL_PIN_Set(PAD_PA12, GPIO_A12, PIN_PULLDOWN, 1);
    HAL_PIN_Set(PAD_PA15, GPIO_A15, PIN_PULLDOWN, 1);
    HAL_PIN_Set(PAD_PA13, GPIO_A13, PIN_PULLDOWN, 1);
    HAL_PIN_Set(PAD_PA14, GPIO_A14, PIN_PULLDOWN, 1);
    HAL_PIN_Set(PAD_PA17, GPIO_A17, PIN_PULLDOWN, 1);

    HAL_PIN_Set(PAD_PA35, GPIO_A35, PIN_PULLDOWN, 1);
    HAL_PIN_Set(PAD_PA36, GPIO_A36, PIN_PULLDOWN, 1);
#elif defined(BSP_USING_NOR_FLASH1)
    FLASH_HandleTypeDef *flash_handle;
    flash_handle = (FLASH_HandleTypeDef *)rt_flash_get_handle_by_addr(MPI1_MEM_BASE);
    HAL_FLASH_DEEP_PWRDOWN(flash_handle); // NOR flash enters deep sleep; IO state unchanged
    HAL_Delay_us(3);
#endif /* BSP_USING_NOR_FLASH2 */
#else
    { ; }
#endif
}

HAL_RAM_RET_CODE_SECT(BSP_PowerUpCustom, void BSP_PowerUpCustom(bool is_deep_sleep))
{
#ifdef SOC_BF0_HCPU
    if (!is_deep_sleep)
    {
#ifdef BSP_USING_NOR_FLASH2
        HAL_PIN_Set(PAD_PA16, MPI2_CLK,  PIN_NOPULL,   1); // restore IO to working state before power on
        HAL_PIN_Set(PAD_PA12, MPI2_CS,   PIN_NOPULL,   1);
        HAL_PIN_Set(PAD_PA15, MPI2_DIO0, PIN_PULLDOWN, 1);
        HAL_PIN_Set(PAD_PA13, MPI2_DIO1, PIN_PULLDOWN, 1);
        HAL_PIN_Set(PAD_PA14, MPI2_DIO2, PIN_PULLUP,   1);
        HAL_PIN_Set(PAD_PA17, MPI2_DIO3, PIN_PULLUP,   1);

        HAL_PIN_Set(PAD_PA35, GPIO_A35, PIN_PULLUP, 1);
        HAL_PIN_Set(PAD_PA36, GPIO_A36, PIN_PULLUP, 1);

        HAL_PMU_ConfigPeriLdo(PMU_PERI_LDO2_3V3, true, true); // turn on NOR flash power
        BSP_Flash_hw2_init(); // reinitialize NOR flash after power cycling
#elif defined(BSP_USING_NOR_FLASH1)
        FLASH_HandleTypeDef *flash_handle;
        flash_handle = (FLASH_HandleTypeDef *)rt_flash_get_handle_by_addr(MPI1_MEM_BASE);
        HAL_FLASH_RELEASE_DPD(flash_handle); // exit deep sleep
        HAL_Delay_us(20); // delay per datasheet tRES1
#endif
    }
    else if (PM_STANDBY_BOOT == SystemPowerOnModeGet())
    {
    }
#elif defined(SOC_BF0_LCPU)
    { ; }
#endif
}

Note: When running XIP from NOR flash, code controlling NOR sleep/wake must be placed in RAM (HAL_RAM_RET_CODE_SECT).

5.2 Code Implementation

Pin configuration code resides in the board’s pinmux.c and bsp_power.c. Implement BSP_PIN_Init, BSP_Power_Up, BSP_IO_Power_Down per IO definitions and hardware.

5.2.1 Pin Configuration in Active State

BSP_PIN_Init runs once on cold boot and after STANDBY wake‑up. Set functional modes and IO directions for active state here. For example, configure PB46 as USART3_RX with digital input pull‑up:

HAL_PIN_Set(PAD_PB46, USART3_RXD, PIN_PULLUP, 0);

For output IOs, configuring only PIN_NOPULL without setting a GPIO output leaves the pin in default input state, potentially causing floating leakage. Set an explicit output level, e.g.:

HAL_PIN_Set(PAD_PA35, GPIO_A35, PIN_NOPULL, 1);
// Then configure as explicit high/low output, or HAL_GPIO_DeInit to restore input when needed

5.2.2 Pin Configuration in Sleep State

In bsp_power.c, implement the following virtual functions to dynamically switch pin settings when entering/exiting sleep:

Table 5‑2: Pin Configuration APIs for Sleep State

Function

Description

BSP_IO_Power_Down

Executed before entering sleep

BSP_Power_Up

Executed after wake‑up (STANDBY after BSP_PIN_Init)

BSP_TP_PowerDown

Executed after screen off

BSP_TP_PowerUp

Executed before screen on

BSP_LCD_PowerDown

Executed after screen off

BSP_LCD_PowerUp

Executed before screen on

If board‑level power control should align with sleep, you can power off components and adjust pins in BSP_IO_Power_Down, then reverse in BSP_Power_Up. This is coarse‑grained: e.g., after screen‑off, HPSYS may enter sleep later, and keeping LCD powered until then wastes energy; or when HPSYS wakes to run tasks without needing the screen, powering the screen in BSP_Power_Up also wastes energy. To refine control, handle display/touch power‑down in BSP_TP_PowerDown and BSP_LCD_PowerDown right after screen‑off, and call them again in BSP_Power_Up to keep pins in power‑down state until screen‑on. If screen‑on conditions are met, the system calls BSP_TP_PowerUp/BSP_LCD_PowerUp before lighting the screen to restore power and active pin settings.

Call order and coreid notes:

  • void BSP_IO_Power_Down(int coreid, bool is_deep_sleep) is called twice before HCPU sleeps:

    • First with coreid=CORE_ID_LCPU: before revoking LCPU wake requests, to close pins of LCPU peripherals used by HCPU. After revocation LCPU may enter low‑power and HCPU cannot access LCPU domain registers.

    • Second with coreid=CORE_ID_HCPU: right before HCPU sleep, to close pins used by HCPU itself.

  • In the LCPU project, this function is called once before LCPU sleeps to close pins used by LCPU.

  • Low‑power pin configurations differ per peripheral. Generally disable pull‑ups/downs to avoid leakage loops; output levels depend on board design and external device power state.

5.3 Sleep Flow

It is recommended to use DEEPSLEEP (sleep mode). In this mode all RAM data and hardware configurations are retained, wake‑up time back to active is shorter, and IO levels remain as in active state. Peripherals stop in sleep; CPU can only be woken by limited sources: GPIO interrupt, RTC interrupt, LPTIM interrupt, and inter‑core communication interrupt.

• HCPU sleep/wake (simplified): Enter sifli_deep_handler(); without peripheral SUSPEND/RESUME and context restore, wake‑up is faster: sifli_sleep log [pm]S:3,11620140sifli_deep_handler BSP_IO_Power_Down WFI enter deep → timer/IO wake‑up → continue after returning from WFI → BSP_Power_Up logs [pm]W:11620520, [pm]WSR:0x80.

Note: LCPU code is not open for modification on 52 series.

5.4 Hibernate Power‑Off Leakage Analysis

5.4.1 Hibernate Power‑Off Flow

Enter Hibernate: call HAL_PMU_EnterHibernate(). Before sleep, configure PMU wake‑up pins and levels for Hibernate. For 52 series, three internal LDOs exist; consider turning off unused LDOs via HAL_PMU_ConfigPeriLdo per hardware.

Hibernate wake‑up: press the wake‑up pin to wake. Use PM_HIBERNATE_BOOT == SystemPowerOnModeGet() to determine hibernate boot, and combine with button duration to decide whether to power on.

5.4.2 Hibernate Power‑Off Configuration

Before entering Hibernate:

  • Call HAL_PMU_EnterHibernate().

  • Configure PMU wake‑up pins and levels to ensure wake‑up.

  • 58/56/52 series: PMU provides pull‑up/down in Hibernate (hwp_rtc->PAWK1R/PAWK2R); configure via HAL_PIN_Set.

  • hwp_pmuc->WKUP_CNT sets external signal duration thresholds (58/56/52 series only).

– 52 series: Three internal LDOs (PMU_PERI_LDO_1V8/PMU_PERI_LDO2_3V3/PMU_PERI_LDO3_3V3); consider turning them off per hardware.

Example:

rt_kprintf("SF32LB52X entry_hibernate\n");
HAL_PMU_SelectWakeupPin(0, HAL_HPAON_QueryWakeupPin(hwp_gpio1, BSP_KEY1_PIN)); // select PA34 → wake_pin0
HAL_PMU_EnablePinWakeup(0, AON_PIN_MODE_HIGH);                                 // enable wake_pin0 
hwp_pmuc->WKUP_CNT = 0x50005; // 31-16bit: PIN1 wake CNT, 15-0bit: PIN0 wake CNT
rt_kprintf("SF32LB52X CR:0x%x,WER:0x%x\n", hwp_pmuc->CR, hwp_pmuc->WER);

HAL_PIN_Set(PAD_PA24, GPIO_A24, PIN_PULLDOWN, 1); // #WKUP_PIN0
HAL_PIN_Set(PAD_PA25, GPIO_A25, PIN_PULLDOWN, 1); // #WKUP_PIN1
HAL_PIN_Set(PAD_PA26, GPIO_A26, PIN_PULLDOWN, 1); // #WKUP_PIN2
HAL_PIN_Set(PAD_PA27, GPIO_A27, PIN_PULLDOWN, 1); // #WKUP_PIN3

HAL_PIN_Set(PAD_PA34, GPIO_A34, PIN_PULLDOWN, 1); // #WKUP_PIN10
HAL_PIN_Set(PAD_PA35, GPIO_A35, PIN_PULLDOWN, 1); // #WKUP_PIN11
HAL_PIN_Set(PAD_PA36, GPIO_A36, PIN_PULLDOWN, 1); // #WKUP_PIN12
HAL_PIN_Set(PAD_PA37, GPIO_A37, PIN_PULLDOWN, 1); // #WKUP_PIN13
HAL_PIN_Set(PAD_PA38, GPIO_A38, PIN_PULLDOWN, 1); // #WKUP_PIN14
HAL_PIN_Set(PAD_PA39, GPIO_A39, PIN_PULLDOWN, 1); // #WKUP_PIN15
HAL_PIN_Set(PAD_PA40, GPIO_A40, PIN_PULLDOWN, 1); // #WKUP_PIN16
HAL_PIN_Set(PAD_PA41, GPIO_A41, PIN_PULLDOWN, 1); // #WKUP_PIN17
HAL_PIN_Set(PAD_PA42, GPIO_A42, PIN_PULLDOWN, 1); // #WKUP_PIN18
HAL_PIN_Set(PAD_PA43, GPIO_A43, PIN_PULLDOWN, 1); // #WKUP_PIN19
HAL_PIN_Set(PAD_PA44, GPIO_A44, PIN_PULLDOWN, 1); // #WKUP_PIN20

rt_hw_interrupt_disable();
HAL_PMU_ConfigPeriLdo(PMU_PERI_LDO2_3V3, false, false);
HAL_PMU_ConfigPeriLdo(PMU_PERI_LDO3_3V3, false, false);
HAL_PMU_ConfigPeriLdo(PMU_PERI_LDO_1V8,  false, false);
HAL_PMU_EnterHibernate();

Notes:

  • 58/56/52 series: Only two wake‑up sources (pin0/pin1) are allowed at the same time; map via HAL_PMU_SelectWakeupPin.

– 52 series: #WKUP_PIN4-9 (PA28-PA33) are multiplexed with ADC and no longer support wake‑up; disconnect external IO and use internal pull‑down (no handling needed in Hibernate, no leakage). Do not set pull‑ups via hwp_rtc->PAWK1R/PAWK2R to avoid leakage.

../_images/low_power24.png

Figure 5.2 Handling of #WKUP_PIN4‑9 in 52 series Hibernate

Hibernate wake‑up detection:

if (PM_HIBERNATE_BOOT == SystemPowerOnModeGet())
{
    // Decide whether to power on based on button duration, etc.
}