Skip to content

XOSC vs LPOSC Sleep Differences #98

@mcanyucel

Description

@mcanyucel

When I test the two different sleep modes with the below code:

#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include "pico/sleep.h"
#include "hardware/gpio.h"

#define WAKE_PIN 22  // GP22 for button wake-up

void led_blink(int count, int on_ms, int off_ms) {
    for (int i = 0; i < count; i++) {
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
        sleep_ms(on_ms);
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
        sleep_ms(off_ms);
    }
}

void lposc_dormant_mode() {
    // Initialize WiFi
    if (cyw43_arch_init()) {
        while(1) tight_loop_contents();
    }
    
    // PHASE 1: Baseline measurement (33mA expected)
    led_blink(3, 100, 100);  // 3 blinks = starting
    sleep_ms(3000);  // Measure baseline current
    
    // PHASE 2: Configure for dormant mode PROPERLY
    led_blink(5, 200, 200);  // 5 slow blinks = preparing for dormant
    
    // Configure wake button
    gpio_init(WAKE_PIN);
    gpio_set_dir(WAKE_PIN, GPIO_IN);
    gpio_pull_up(WAKE_PIN);
    
    // CRITICAL: Configure clock source for dormant mode
    // This was missing in previous test!
    sleep_run_from_lposc();  // Required for dormant mode
    
    // Turn off LED and WiFi
    cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
    cyw43_arch_deinit();
    
    // NOW go dormant - should reach ~1mA
    sleep_goto_dormant_until_pin(WAKE_PIN, true, false);  // Edge trigger, falling edge
    
    // === WAKE UP CODE ===
    // Restore clocks after dormant wake
    sleep_power_up();
    
    // Re-initialize WiFi
    if (cyw43_arch_init()) {
        while(1) tight_loop_contents();
    }
    
    // SUCCESS: 10 very fast blinks
    led_blink(10, 50, 50);
    
    // Continue with heartbeat pattern
    while(1) {
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
        sleep_ms(100);
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
        sleep_ms(2900);
    }
}

void test_fixed_dormant_mode() {
    // Initialize WiFi
    if (cyw43_arch_init()) {
        while(1) tight_loop_contents();
    }
    
    // PHASE 1: Baseline measurement (33mA expected)
    led_blink(3, 100, 100);  // 3 blinks = starting
    sleep_ms(3000);  // Measure baseline current
    
    // PHASE 2: Configure for dormant mode PROPERLY
    led_blink(5, 200, 200);  // 5 slow blinks = preparing for dormant
    
    // Configure wake button
    gpio_init(WAKE_PIN);
    gpio_set_dir(WAKE_PIN, GPIO_IN);
    gpio_pull_up(WAKE_PIN);
    
    // CRITICAL: Configure clock source for dormant mode
    // This was missing in previous test!
    sleep_run_from_xosc();  // Required for dormant mode
    
    // Turn off LED and WiFi
    cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
    cyw43_arch_deinit();
    
    // NOW go dormant - should reach ~1mA
    sleep_goto_dormant_until_pin(WAKE_PIN, true, false);  // Edge trigger, falling edge
    
    // === WAKE UP CODE ===
    // Restore clocks after dormant wake
    sleep_power_up();
    
    // Re-initialize WiFi
    if (cyw43_arch_init()) {
        while(1) tight_loop_contents();
    }
    
    // SUCCESS: 10 very fast blinks
    led_blink(10, 50, 50);
    
    // Continue with heartbeat pattern
    while(1) {
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
        sleep_ms(100);
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
        sleep_ms(2900);
    }
}

int main() {
    lposc_dormant_mode();
    return 0;
}

I have found out that the xosc sleep mode drops the current draw immediately to 3.8 mA, and it can wake up with the interrupt immediately. However, the lposc sleep seems to have two phases; first it drops to 16 mA for around 40 seconds during which the interrupt does not wake the system up. After that stage, the current draw drops to 1.5 mA, and the system can be awaken with the gp pin interrupt:

Clock Source Initial Sleep Current True Dormant Current Wake Response Time to True Dormant Best Use Case
XOSC 3.8mA 3.8mA Immediate ~0 seconds Short sleeps (<2 min)
LPOSC 16mA → 1.5mA 1.5mA Delayed ~45 seconds Long sleeps (>2 min)

Detailed Behavior

XOSC (Crystal Oscillator)

  • Power: Consistent 3.8mA throughout sleep
  • Wake: GPIO button responsive immediately
  • Stability: Immediate, no transition period
  • Power Savings: 88% reduction (33mA → 3.8mA)

LPOSC (Low Power Oscillator)

  • Power: Two-stage transition
    • Stage 1 (0-45s): 16mA, GPIO wake disabled
    • Stage 2 (45s+): 1.5mA, GPIO wake enabled
  • Wake: Only responsive after 45-second stabilization
  • Stability: Requires ~45 seconds to reach true dormant
  • Power Savings: 95% reduction (33mA → 1.5mA)

This is not a bug report or anything, just an excerpt of knowledge for people who are working on sleep stuff for two weeks with no avail :P

The setup is:

  • RPi Pico 2 W
  • Multimeter connected between an 18650 and rpi pico (vsys)
  • Multimeter parallel to gp22 and ground (to see interrupts)
  • Push button between gp22 and ground

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions