Low-Power Development Guide
1 Introduction
The SiFli MCU is a dual-core Cortex‑M33 STAR SoC. The big core HCPU runs at 0~240 MHz and belongs to the HPSYS subsystem, suitable for high-performance tasks such as graphics, audio, and neural networks. The small core LCPU runs at 0~48 MHz and belongs to the LPSYS subsystem, suitable for tasks such as Bluetooth, sensor data collection, and computation.
Refer to example\pm\classical for low-power development examples.
For the power measurement report, see: Power Measurement Report
2 Configuring Low-Power Modes
2.1 Enabling Low-Power Mode
Run sdk.py menuconfig in the project directory to open the software configuration menu:
Enable the low-power feature (
Enable Low power support):Path: Sifli middleware → Enable Low power support
Enable: Enable Low power support
Macro switch:
BSP_USING_PMEffect: Enables the low-power feature
Figure Enable Low Power configuration menu
Select the 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 switch:
PM_DEEP_ENABLEEffect: Selects Deep Sleep mode as the low-power mode
Figure Deep Sleep configuration menu
Enable the low-power debug switch. Once enabled, low-power related logs will be printed (optional; the low-power log printing takes time and affects power consumption) (
Enable PM Debug)Path: Sifli middleware → Enable Low power support -> Enable PM Debug
Select: Enable PM Debug
Macro switch:
BSP_PM_DEBUGEffect: Enables the low-power log debug switch
Figure Debug configuration menu
After configuration, confirm that the project configuration file
rtconfig.hcontains the following definitions:
#define RT_USING_PM 1 // Enable the PM module
#define PM_DEEP_ENABLE 1 // DEEP sleep mode
#define BSP_USING_PM 1 // Enable the PM module
#define BSP_PM_DEBUG 1 // Print PM[S], PM[W] logs (optional)
2.2 Disabling Low-Power Mode
Run sdk.py menuconfig in the project directory, then reverse the steps described in Enabling Low-Power Mode to disable the corresponding options.
3 Key Wakeup Configuration
Refer to the SDK\example\pm\classical example for the configuration method.
Standby wakeup configuration (for standby/deep wakeup)
The following is an example of the HAL-layer standby wakeup APIs. You can use HAL_LPAON_QueryWakeupPin() and HAL_HPAON_QueryWakeupPin() to get the PIN number corresponding to a wakeup pin. If IO wakeup needs to process events, a GPIO interrupt must also be configured:
For the 52 series, no additional wakeup pin configuration is required for sleep in deepsleep mode; all pins can wake up the system, and the wakeup source goes through WSR_GPIO1.
Power-off wakeup configuration (for hibernate wakeup)
For MCUs after the 55 series: Two wakeup sources, PIN0 and PIN1, can exist simultaneously, and each wakeup source can be mapped to any HCPU/LCPU wakeup pin; see the PMUC CR register configuration in the user manual for details.
// Configuration method for 58x/56x/52x/57x:
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, waking up is equivalent to a cold boot (but with the PM_HIBERNATE_BOOT flag set), unlike Standby wakeup, which resumes the original program from where it left off. The wakeup PIN and level mode are controlled by PMU registers; you can print the WER/CR registers to verify. If the same IO is used for both standby and power-off wakeup, both configurations must take effect.
4 Debugging Low-Power Modes
4.1 Low-Power Modes
PM_SLEEP_MODE_IDLE: The CPU enters idle mode. The CPU stops at WFI or WFE, and high-speed clocks (HRC/HXT/DBLR/DLL) are present in the system. All peripherals can be enabled and generate interrupts.
PM_SLEEP_MODE_LIGHT: The CPU enters light sleep. The CPU stops at the WFI instruction, high-speed clocks are switched off, and CPU-related peripherals stop working (but remain powered). The system switches to the 32K clock. The system can be woken up by the low-power timer (LPTIM), RTC, BLE MAC (LCPU only), Mailbox (from the other CPU), or a pin. Wakeup time is 30us-100us. After wakeup, execution continues from the instruction after WFI.
PM_SLEEP_MODE_DEEP: Same as PM_SLEEP_MODE_LIGHT, except that the system power supply switches to RET_LDO and the wakeup time increases to 100us-1ms. After wakeup, execution continues from the instruction after WFI.PM_SLEEP_MODE_STANDBY: The CPU enters standby mode. High-speed clocks are switched off, CPU-related peripherals are powered down, all RAM except the system-configured portion is unpowered, pin states are retained, and the system power supply switches to RET_LDO. The system can be woken up by the low-power timer, RTC, BLE MAC (LCPU only), Mailbox (from the other CPU), or a specific wakeup pin. Wakeup time is 1ms-2ms. After wakeup, the system reboots. Based on the low-power mode indication in the AON registers, the software determines that the system has booted from standby mode, distinguishing it from a cold boot.
See Table 4‑1 for the current consumption of each low-power mode. If PSRAM is present, the HCPU backs up the data that must be retained in the power-down RAM to PSRAM before entering sleep and restores it after wakeup; without PSRAM, the data is backed up to the 64KB Retention RAM. Unless otherwise specified below, “entering sleep” refers to entering a low-power mode other than IDLE, and “wakeup” refers to exiting a low-power mode other than IDLE.
Table 4‑1: Low-Power Modes
Low-Power Mode |
CPU state |
Peripheral state |
SRAM |
Wakeup sources |
Wakeup time |
|---|---|---|---|---|---|
PM_SLEEP_MODE_IDLE |
stop |
run |
Accessible |
Any interrupt |
<1µs |
PM_SLEEP_MODE_DEEP |
stop |
stop |
LPSYS: inaccessible, all retained |
RTC, wakeup PIN, IO(PA), |
~ 250us |
PM_SLEEP_MODE_STANDBY |
reset |
reset |
LPSYS: inaccessible, all retained |
RTC, wakeup PIN, |
~ 1ms |
4.2 Power-Off Modes
In addition to the four low-power modes provided by each subsystem, the chip also provides two system-level power-off modes:
Hibernate: All subsystems are powered down and the system switches to the 32K crystal. It can be woken up by PIN and RTC (RTC wakeup time is accurate). Interface:
HAL_PMU_EnterHibernate.Shutdown: All subsystems are powered down and the system switches to RC10K. It can be woken up by PIN and RTC (RTC wakeup time is inaccurate). Interface:
HAL_PMU_EnterShutdown.
Table 4‑2: Power-Off Modes
Low-Power Mode |
CPU state |
Peripheral state |
SRAM |
IO |
Wakeup sources |
Wakeup time |
|---|---|---|---|---|---|---|
Hibernate |
reset |
reset |
Data not retained |
High-impedance |
RTC and PIN |
>2ms |
Shutdown |
reset |
reset |
Data not retained |
High-impedance |
RTC and PIN |
>2ms |
Note: The current figures are for reference only; actual values vary with peripheral enablement and IO settings. “stop” means the module stops working and can resume without reconfiguration after exiting low power; “reset” means the module has been reset after exiting (the CPU starts from ROM and peripherals need to be reconfigured).
If sleep conditions are not met but power still needs to be reduced, refer to WFI Automatic Frequency Scaling and Scenario-Based Dynamic Frequency Scaling.
4.3 WFI Automatic Frequency Scaling
When the IDLE thread is entered but sleep conditions are not met, the current during WFI can be reduced by lowering the HCPU frequency. The precondition for frequency reduction: no high-speed peripheral is active. High-speed peripherals include:
EPIC
EZIP
LCDC
USB
SD
Note: The busy detection for EPIC/EZIP is integrated into the LVGL implementation bundled with the SDK; if that implementation is not used, call rt_pm_hw_device_start/rt_pm_hw_device_stop respectively when a peripheral starts/stops working to indicate busy/idle, avoiding WFI frequency reduction while busy. The busy detection for LCDC/USB/SD is integrated into the RT-Thread LCD Device driver.
The WFI frequency after reduction is configured by HAL_RCC_HCPU_SetDeepWFIDiv. When an audio peripheral is active, the frequency can only be reduced to 48MHz; otherwise it can be reduced to 4MHz. In addition, the HPSYS_RCC_DBGR_FORCE_HP bit of hwp_hpsys_rcc->DBGR must be set to 1.
4.4 Scenario-Based Dynamic Frequency Scaling
For scenarios that do not require high performance, the big core can reduce its frequency and voltage to lower active power consumption (for example, after the screen is off, running only the wrist-raise algorithm can drop to 48MHz). Although a lower frequency lengthens execution time, the total energy (current × time) may be lower. The optimal mode can be measured in different scenarios and chosen accordingly.
Use rt_pm_run_enter to configure the current run mode. The HCPU supports the following four modes (the application starts with PM_RUN_MODE_HIGH_SPEED by default); switching to a high-speed mode takes effect immediately, while switching to a low-speed mode is deferred until the IDLE thread runs:
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 |
The SDK also provides pm_scenario_start/pm_scenario_stop for convenient per-scenario switching. Currently UI and Audio are supported:
If either is active → HIGH_SPEED is used;
If neither
UInorAudiois active → MEDIUM_SPEED is used.
4.5 Low-Power Flow
In this solution, HPSYS can enter sleep mode only after the screen is off; with the screen on, HPSYS can only enter IDLE mode when the HCPU is idle. LPSYS can enter sleep only after HPSYS has entered sleep; if HPSYS is not asleep, LPSYS can only enter IDLE even when the LCPU is idle (with the exception of the 52 series, where the HCPU and LCPU can sleep independently). Once HPSYS is asleep, LPSYS can freely enter and exit sleep without waking HPSYS.
4.5.1 Screen Off
The screen lock time can be selected in the settings interface. When the screen has been idle for longer than the lock time, the screen turns off. Sleep conditions are checked in the IDLE thread; if they are met, HPSYS enters sleep, and LPSYS can then also enter sleep.
Figure 4.1 Screen-off flow
4.5.2 HPSYS Wakeup
HPSYS can be woken up by the low-power timer (LPTIM), RTC, BLE MAC (LCPU only), Mailbox (from the other CPU), or any pin in deepsleep mode. For example, when a key is pressed, HPSYS wakes up.
Taking key-wakeup screen-on as an example, the flow is shown in Figure 4.2; after the screen turns on, a new round of screen-off judgment begins. The wakeup flow triggered by the setting event from the phone APP is shown in Figure 4.3; after the Setting request is processed, the system returns to the IDLE thread and can enter sleep immediately.
Figure 4.2 Key screen-on/screen-off flow
Figure 4.3 Wakeup flow upon receiving a phone Setting event
Regarding context retention in standby:
HCPU: In DEEPSLEEP mode, all RAM is retained, and after wakeup the context is restored directly from RAM.
LCPU: All RAM remains powered, and the CPU register context is saved in RAM; after wakeup, the context is restored directly from RAM.
4.6 Log Interpretation
The HCPU and LCPU output logs through the console. After enabling the low-power debug switch as described in Section 2.1, you can search for the following keywords in the logs to analyze the flow.
Table 4‑3: Log Keyword Interpretation
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 32768Hz |
[pm]W: gtime |
Exit sleep; gtime unit is 32768Hz |
[pm]WSR:0xXXX |
Wakeup reason (interpreted by register bit meaning) |
gtime is synchronized between the HCPU and LCPU sides. For example, if the system enters sleep at 2136602 and wakes up at 2142330, the sleep duration is sleep_time=(2142330-2136602)/32768=175ms; if WSR=0x200, it means the wakeup was triggered by a mailbox interrupt from LPSYS. See the chip manual for the meaning of each WSR bit.
Figure 4.4 Low-power log example
HPSYS WSR meaning
Bit field |
Meaning |
|---|---|
[0] |
RTC wakeup |
[1] |
GPIO1 wakeup |
[2] |
LPTIM1 wakeup |
[3] |
PMUC wakeup |
[6] |
LPSYS wakes up HPSYS manually |
[7] |
LPSYS wakes up HPSYS via Mailbox |
[8] |
PIN0 wakeup |
[9] |
PIN1 wakeup |
[10] |
PIN2 wakeup |
[11] |
PIN3 wakeup |
[18] |
PIN10 wakeup |
[19] |
PIN11 wakeup |
[20] |
PIN12 wakeup |
[21] |
PIN13 wakeup |
[22] |
PIN14 wakeup |
[23] |
PIN15 wakeup |
[24] |
PIN16 wakeup |
[25] |
PIN17 wakeup |
[26] |
PIN18 wakeup |
[27] |
PIN19 wakeup |
[28] |
PIN20 wakeup |
Table 4‑4: 52 Series HPSYS Wakeup PIN Mapping Table (partial)
Wakeup |
PIN Meaning |
|---|---|
PIN0 |
PA24 |
PIN1 |
PA25 |
PIN10 |
PA34 |
PIN11 |
PA35 |
PIN19 |
PA43 |
4.7 Common Issues Analysis
Because SWD cannot connect after entering sleep mode, UART must be used as the console port to capture logs for analysis.
4.7.1 Has the System Entered Sleep Mode?
If any of the following conditions is met, HPSYS has most likely entered sleep:
SWD cannot connect
The HCPU console does not respond
The HCPU log contains “S: mode, gtime”
If any of the following conditions is met, LPSYS has most likely entered sleep:
The LCPU console does not respond
The LCPU log contains “S: mode, gtime”
Make sure the finsh shell option in the LCPU Command shell has been enabled.
The current low-power mode can also be determined by measuring the chip power pin voltages:
HPSYS in active/sleep/deepsleep:
LDO1_VOUT≈1.1V; HPSYS in standby:LDO1_VOUTgradually drops to 0V.LPSYS in active/sleep/deepsleep:
LDO2_VOUTorBUCK2_VOUT≈0.9V; LPSYS in standby: the corresponding voltage gradually drops to 0V.Entering hibernate:
LDO1_VOUT/LDO2_VOUT/BUCK2_VOUT/VDD_RETall drop to 0V.
4.7.2 Why Didn’t the System Enter Sleep Mode?
Sleep entry conditions
For applications, the switching between sleep and active modes is transparently controlled by the lowest-priority IDLE thread: when all higher-priority threads have no work, IDLE is scheduled and checks whether all of the following conditions are met before entering sleep:
Sleep mode is not blocked (no unreleased
rt_pm_request(PM_SLEEP_MODE_IDLE)exists)The time of the nearest OS timer that will expire is greater than the threshold (100ms by default)
No wakeup condition is active (for example, enabled wakeup sources are currently inactive)
Data sent to the small core has been consumed (the IPC queue has no unconsumed data)
Timer wakeup configuration before sleep
Before entering sleep, LPTIM is configured according to the timeout of the “nearest timer” so that it generates an interrupt to wake up the big core at that moment. For example, if the nearest timer expires in 200ms, LPTIM is configured to interrupt after 200ms, ensuring that the OS timer callback still fires on time during sleep.
Sleep block/release APIs
An application can explicitly block sleep in a critical section, and the driver framework also blocks it automatically while a peripheral is active, to avoid accidentally falling asleep during interrupt processing:
// Block sleep until released
rt_pm_request(PM_SLEEP_MODE_IDLE);
// ... critical section / peripheral operations ...
rt_pm_release(PM_SLEEP_MODE_IDLE);
Timer threshold and policy
Refer to the configuration in Power Management to enable low-power support for the project. Different low-power policies can enter different low-power modes. The current default policy of the system is:
Default policy example:
static const pm_policy_t default_pm_policy[] =
{
{15, PM_SLEEP_MODE_LIGHT}, // Enter light sleep when idle for more than 15ms
#ifdef PM_STANDBY_ENABLE
{10000, PM_SLEEP_MODE_STANDBY}, // Enter standby when idle for more than 10s
#endif /* PM_STANDBY_ENABLE */
};
Common cause checklist (similar for HCPU/LCPU):
Enable the PM module and confirm the macro definitions:
#define RT_USING_PM 1
#define BSP_USING_PM 1 // Enable low-power mode
#define PM_DEEP_ENABLE 1 // Enter Deep low power
#define BSP_PM_DEBUG 1 // Enable low-power debug logs
Confirm the CPU is idle and has entered the idle thread:
Use the finsh command
list_threadto check thread states; excepttshellandtidle, which are ready, all others should be suspend, otherwise the IDLE thread cannot run.
Figure 4.6 Information returned by the list_thread command
Confirm sleep is not blocked:
Run
pm_dumpin the console; if the “Idle Mode Counter” > 0, some module has calledrt_pm_request(PM_SLEEP_MODE_IDLE)to block sleep and must be released with the correspondingrt_pm_release(PM_SLEEP_MODE_IDLE).
Figure 4.7 Information returned by the pm_dump command
Confirm the OS timer timeout is greater than the sleep threshold: Send the command
list_timerin the console to display all created OS timers. Compare the timeout of activated timers with the sleep threshold; if a timeout is smaller than the threshold, that timer prevents the system from entering sleep. The unit of the OS timer timeout is ms.
Figure 4.8 Information returned by the list_timer command
See the configuration below: the HPSYS sleep threshold is 100ms by default, and the LPSYS sleep threshold is 10ms.
RT_WEAK const pm_policy_t pm_policy[] =
{
#ifdef PM_STANDBY_ENABLE
#ifdef SOC_BF0_HCPU
{100, PM_SLEEP_MODE_STANDBY}, //Hcpu: if no timer wakeup within 100ms, enter standby sleep
#else
{10, PM_SLEEP_MODE_STANDBY}, //Lcpu: if no timer wakeup within 10ms, enter standby sleep
#endif /* SOC_BF0_HCPU */
#elif defined(PM_DEEP_ENABLE)
#ifdef SOC_BF0_HCPU
{100, PM_SLEEP_MODE_DEEP}, //Hcpu: if no timer wakeup within 100ms, enter Deep sleep
#else
{10, PM_SLEEP_MODE_DEEP}, //Lcpu: if no timer wakeup within 10ms, enter Deep sleep
#endif /* SOC_BF0_HCPU */
#else
#ifdef SOC_BF0_HCPU
{100, PM_SLEEP_MODE_LIGHT},
#else
If the HCPU code contains a 90ms periodic delay, the system will never sleep:
while (1)
{
rt_thread_delay(90); // 90ms delay
}
Note the difference between the delay functions:
HAL layer (no thread switching during the delay):
HAL_Delay(10); // 10ms
HAL_Delay_us(10); // 10us
RT-Thread APIs (switch to other threads, may trigger idle → sleep):
rt_thread_delay(100); // 100ms
Confirm there is no pending wakeup source:
Read the registers with commands to verify
WER/WSR:
52 series WSR addresses:
regop unlock 0000
regop read 40040024 1 # LPSYS WSR
regop read 500c0024 1 # HPSYS WSR
You can also read registers via Jlink/SifliUsartServer or print them through 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: the wakeup pin level is misconfigured (for example, low-level wakeup but the level stays low).
Confirm the data sent to the other core has been consumed:
You can connect via Ozone and dump memory to inspect with trace32, or print the
read_idx_mirror/write_idx_mirrorof theipc_ctxqueues through logs; if they are not equal, there is uncollected data that blocks sleep.
Figure 4.9 Non-empty ring buffer case
Figure 4.10 Empty ring buffer case
Example printing the active queue indices:
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);
}
}
}
Figure 4.11 Example of a missing channel caused by the LCPU data service not being enabled
5 Power Optimization
5.1 Standby Leakage Analysis
When both HPSYS and LPSYS have entered sleep, the focus of whole-device power optimization is:
Remove detachable components such as the screen, sensors, and charging IC first and measure the minimum system current;
Software IO level misconfiguration causing voltage difference/floating leakage;
The on-chip PSRAM/Flash and external NAND/Flash/eMMC not entering sleep.
If the hardware can measure current per rail, you can locate which rail of VSYS/ VLDO2/ VLDO3/ VDD_SIP/ VDDIOA is leaking to narrow down the scope.
5.1.1 Peripheral Leakage
Common causes:
Board-level devices not powered off;
Board-level devices powered off, but improper chip pin settings cause current to back-feed from the chip pins into the board-level devices.
For cause 2), avoid: chip pins connected to power-off devices outputting high or enabling pull-ups. The recommended configurations of common peripherals in active/sleep states are as follows (no pin change is needed when the external circuit stays powered; switch to the low-power configuration when the external circuit is powered off).
Table 5‑1: Recommended Pin Settings
Peripheral |
Pin |
Direction |
Active state |
Sleep (external circuit powered) |
Sleep (external circuit power-off) |
|---|---|---|---|---|---|
PSRAM |
PSRAM_CLK |
O |
Digital output |
Digital output |
GPIO mode output low |
PSRAM |
PSRAM_CLKB |
O |
Digital output |
Digital output |
GPIO mode output low |
PSRAM |
PSRAM_CS |
O |
Digital output |
Digital output |
GPIO mode output low |
PSRAM |
PSRAM_DM0 |
O |
Digital output |
Digital output |
GPIO mode output low |
PSRAM |
PSRAM_DM1 |
O |
Digital output |
Digital output |
GPIO mode output low |
PSRAM |
PSRAM_DQS0 |
I/O |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
PSRAM |
PSRAM_DQS1 |
I/O |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
PSRAM |
PSRAM_DQx |
I/O |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
QSPI |
QSPIx_CLK |
O |
Digital output |
Digital output |
GPIO mode output low |
QSPI |
QSPIx_CS |
O |
Digital output |
Digital output |
GPIO mode output low |
QSPI |
QSPIx_DIO0 |
I/O |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
QSPI |
QSPIx_DIO1 |
I/O |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
QSPI |
QSPIx_DIO2 |
I/O |
Digital input pull-up |
Digital input pull-up |
Digital input pull-down |
QSPI |
QSPIx_DIO3 |
I/O |
Digital input pull-up |
Digital input pull-up |
Digital input pull-down |
QSPI |
QSPIx_DIO4 |
I/O |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
QSPI |
QSPIx_DIO5 |
I/O |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
QSPI |
QSPIx_DIO6 |
I/O |
Digital input pull-up |
Digital input pull-up |
Digital input pull-down |
QSPI |
QSPIx_DIO7 |
I/O |
Digital input pull-up |
Digital input pull-up |
Digital input pull-down |
USART |
USARTx_RXD |
I |
Digital input pull-up |
Digital input pull-up |
Digital input pull-down |
USART |
USARTx_TXD |
O |
Digital output |
Digital output |
Digital output |
USART |
USARTx_CTS |
I |
Digital input pull-up |
Digital input pull-up |
Digital input pull-down |
USART |
USARTx_RTS |
O |
Digital output |
Digital output |
Digital output |
I2C |
I2Cx_SCL |
I/O |
Digital input |
Digital input |
Digital input pull-down |
I2C |
I2Cx_SDA |
I/O |
Digital input |
Digital input |
Digital input pull-down |
SPI M |
SPIx_CLK |
O |
Digital output |
Digital output |
GPIO mode output low |
SPI M |
SPIx_CS |
O |
Digital output |
Digital output |
GPIO mode output low |
SPI M |
SPIx_DI |
I |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
SPI M |
SPIx_DO |
O |
Digital output |
Digital output |
GPIO mode output low |
SPI M |
SPIx_DIO |
I/O |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
LCDC SPI |
LCDCx_SPI_CS |
O |
Digital output |
Digital output |
GPIO mode input pull-down |
LCDC SPI |
LCDCx_SPI_CLK |
O |
Digital output |
Digital output |
GPIO mode input pull-down |
LCDC SPI |
LCDCx_SPI_DIO0 |
I/O |
Digital input pull-down |
Digital input pull-down |
GPIO mode input pull-down |
LCDC SPI |
LCDCx_SPI_DIO1 |
O |
Digital output |
Digital output |
GPIO mode input pull-down |
LCDC SPI |
LCDCx_SPI_DIO2 |
O |
Digital output |
Digital output |
GPIO mode input pull-down |
LCDC SPI |
LCDCx_SPI_DIO3 |
O |
Digital output |
Digital output |
GPIO mode input pull-down |
LCDC SPI |
LCDCx_SPI_RSTB |
O |
Digital output |
Digital output |
GPIO output low |
LCDC SPI |
LCDCx_SPI_TE |
I |
Digital input |
Digital input |
GPIO mode input pull-down |
SDIO |
SD_CLK |
O |
Digital output |
Digital output |
GPIO mode output low |
SDIO |
SD_CMD |
I/O |
Digital input pull-up |
Digital input pull-up |
Digital input pull-down |
SDIO |
SD_DIOx |
I/O |
Digital input pull-up |
Digital input pull-up |
Digital input pull-down |
I2S |
I2S1_BCK |
O |
Digital output |
Digital output |
GPIO mode output low |
I2S |
I2S1_LRCK |
O |
Digital output |
Digital output |
GPIO mode output low |
I2S |
I2S1_SDI |
I |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
I2S |
I2S2_BCK |
O |
Digital output |
Digital output |
GPIO mode output low |
I2S |
I2S2_LRCK |
O |
Digital output |
Digital output |
GPIO mode output low |
I2S |
I2S2_SDI |
I |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
I2S |
I2S2_SDO |
O |
Digital output |
Digital output |
GPIO mode output low |
PDM |
PDM_CLK |
O |
Digital output |
Digital output |
GPIO mode output low |
PDM |
PDM_DATA |
I |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
GPTIM out |
GPTIMx_CHx |
O |
Digital output |
Digital output |
GPIO mode output low |
GPTIM in |
GPTIMx_CHx |
I |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
GPTIM |
GPTIMx_ETR |
I |
Digital input pull-down |
Digital input pull-down |
Digital input pull-down |
GPIO In |
GPIO |
I |
Digital input |
Digital input |
GPIO output low or digital input pull-down |
GPIO Out |
GPIO |
O |
Digital output |
Digital output |
GPIO mode output low |
5.1.2 On-Chip IO Internal Leakage
Common models (see FAQ 8.7/8.8 for details):
Input pins floating (a powered-off device on the other end is equivalent to floating) causing an undetermined level;
The IO output level not matching the internal/external pull-up/pull-down.
The following figure shows the pin internal structure (indicative functions: DS/OE/O/IE/PE/PS, etc.).
Figure 5.1 Pin internal structure diagram
5.1.3 On-Chip and External Memory Chip Leakage
Example of PSRAM entering/exiting Half_sleep:
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
}
Example of Flash power-off and Deep Sleep:
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); // Power off nor flash supply
HAL_PIN_Set(PAD_PA16, GPIO_A16, PIN_PULLDOWN, 1); // Change IO to pull-down after power-off
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; no IO change needed
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 active state before powering 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); // Power on nor flash supply
BSP_Flash_hw2_init(); // nor flash must be reinitialized after power-off
#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); // See the chip manual for tRES1
#endif
}
else if (PM_STANDBY_BOOT == SystemPowerOnModeGet())
{
}
#elif defined(SOC_BF0_LCPU)
{ ; }
#endif
}
Note: When XIP runs from nor flash, the code that puts nor flash to sleep/wakes it up must be placed in RAM (HAL_RAM_RET_CODE_SECT).
5.2 Code Implementation
The pin configuration code is located in the board’s pinmux.c and bsp_power.c. Implement the BSP_PIN_Init, BSP_Power_Up, BSP_IO_Power_Down and other interfaces based on the IO definitions and hardware.
5.2.1 Pin Configuration in Active State
BSP_PIN_Init runs once on both cold boot and STANDBY wakeup, and can set the function/input-output mode of pins in the active state there. For example, configure PB46 as USART3_RX, digital input with pull-up:
HAL_PIN_Set(PAD_PB46, USART3_RXD, PIN_PULLUP, 0);
For output IOs, configuring only PIN_NOPULL without setting the GPIO output leaves the pin in the “default input state” and causes floating leakage. Set the output level, for example:
HAL_PIN_Set(PAD_PA35, GPIO_A35, PIN_NOPULL, 1);
// Then configure it as an explicit high/low level output, or call HAL_GPIO_DeInit to restore the input state when needed
5.2.2 Pin Configuration in Sleep State
The following weak functions can be implemented in bsp_power.c to dynamically switch pin settings when entering/exiting sleep:
Table 5‑2: Pin Configuration APIs for the Sleep State
Function name |
Description |
|---|---|
BSP_IO_Power_Down |
Executed before entering sleep |
BSP_Power_Up |
Executed after wakeup (STANDBY wakeup runs after |
BSP_TP_PowerDown |
Executed after the screen turns off |
BSP_TP_PowerUp |
Executed before the screen turns on |
BSP_LCD_PowerDown |
Executed after the screen turns off |
BSP_LCD_PowerUp |
Executed before the screen turns on |
If the power-off and power-on control of board-level devices both accompany sleep, you can power off the board-level devices and change the corresponding pin settings in BSP_IO_Power_Down, and do the reverse in BSP_Power_Up. However, this approach lacks fine-grained control. For example, after the screen turns off, HPSYS may still take some time before entering sleep; if the LCD remains powered during that period, power consumption increases. Or when HPSYS is woken up to run tasks for a while without needing the screen on, turning on the screen power in BSP_Power_Up also increases power consumption. For this reason, you can implement more complex control logic in BSP_IO_Power_Down and BSP_Power_Up. Taking the screen and touch as an example, put the power-down handling of the screen and touch into BSP_TP_PowerDown and BSP_LCD_PowerDown, so that the screen and touch chips are powered off immediately once the screen turns off; in BSP_Power_Up, call BSP_TP_PowerDown and BSP_LCD_PowerDown again, so that even when HPSYS is woken up, the pin settings are restored to the power-off state. When the screen-on condition is met, the system calls BSP_TP_PowerUp and BSP_LCD_PowerUp before turning on the screen to restore the power supply of the screen and touch and the active-state pin settings.
Call order and coreid description:
void BSP_IO_Power_Down(int coreid, bool is_deep_sleep)is called twice before the HCPU enters sleep:First call with
coreid=CORE_ID_LCPU: executed before revoking the LCPU wakeup request, used to turn off “the pins related to LCPU peripherals used by the HCPU”. After revocation, the LCPU may enter low power, and the HCPU can no longer access the LCPU power domain registers.Second call with
coreid=CORE_ID_HCPU: executed right before the HCPU actually falls asleep, used to turn off the other pins used by the HCPU itself.
In the LCPU project, this function is called once before the LCPU falls asleep, used to turn off the pins used by the LCPU.
Different peripherals have different low-power pin configurations. Generally, pull-ups/pull-downs should be turned off to avoid loop leakage; whether a pin outputs high or low depends on the board design (whether it matches the peripheral power state).
5.3 Sleep Flow
It is recommended to use the deepsleep low-power mode (sleep mode). In this mode all RAM data and hardware configurations are retained, and the recovery time from sleep mode back to the active state is also shorter. During sleep, IO levels can remain in the active state. However, peripherals stop working in sleep mode, and the CPU can only be woken up by a limited set of wakeup sources, including GPIO interrupts, RTC interrupts, LPTIM interrupts, and inter-core communication interrupts.
• HCPU sleep/wakeup (simplified flow):
Entering sifli_deep_handler() with no peripheral SUSPEND/RESUME or context restore makes wakeup faster:
sifli_sleep → log [pm]S:3,11620140 → sifli_deep_handler → BSP_IO_Power_Down → WFI enter deep → timer/IO wakeup → continue after WFI → BSP_Power_Up → log [pm]W:11620520, [pm]WSR:0x80.
Note: The LCPU code of the 52 series is not open for modification.
5.4 Hibernate Power-Off Leakage Analysis
5.4.1 Hibernate Power-Off Flow
Entering Hibernate: call HAL_PMU_EnterHibernate(). Before hibernation, configure the PMU wakeup PIN and level for Hibernate. The 52/57 series have 3 built-in LDOs; disable the unused LDOs via HAL_PMU_ConfigPeriLdo based on the hardware.
Hibernate wakeup: The system wakes up after the wakeup PIN is pressed. You can use PM_HIBERNATE_BOOT == SystemPowerOnModeGet() to determine whether it is a hibernate boot, and combine it with the key press duration to decide whether to power on.
5.4.2 Hibernate Power-Off Configuration
Before entering Hibernate:
Call
HAL_PMU_EnterHibernate();Configure the PMU wakeup PIN and level to ensure the system can be woken up;
58/56/52 series: In Hibernate, the PMU side provides pull-ups/pull-downs (
hwp_rtc->PAWK1R/PAWK2R); it is recommended to configure them withHAL_PIN_Set;hwp_pmuc->WKUP_CNTcan configure the external signal duration threshold (58/56/52 series only).
52 series: There are 3 built-in LDOs (
PMU_PERI_LDO_1V8/PMU_PERI_LDO2_3V3/PMU_PERI_LDO3_3V3); decide whether to turn them off based on the 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 2 wakeup sources
pin0/pin1are allowed simultaneously; useHAL_PMU_SelectWakeupPinto specify the mapping;
On the 52 series,
#WKUP_PIN4-9 (PA28-PA33)are multiplexed with ADC and their wakeup function has been removed; handle them by disconnecting the external IO and using internal pull-down (no handling is needed in Hibernate and no leakage occurs). Do not directly set the pull-ups ofhwp_rtc->PAWK1R/PAWK2Rto avoid leakage.
Figure 5.2 Handling of #WKUP_PIN4-9 in Hibernate on the 52 series
Hibernate wakeup determination:
if (PM_HIBERNATE_BOOT == SystemPowerOnModeGet())
{
// Decide whether to power on based on the key press duration etc.
}