diff --git a/CMakeLists.txt b/CMakeLists.txt index f1ae86f32..573b14506 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,7 @@ add_subdirectory(gpio) add_subdirectory(hstx) add_subdirectory(i2c) add_subdirectory(interp) +add_subdirectory(low_power) add_subdirectory(multicore) add_subdirectory(otp) add_subdirectory(picoboard) diff --git a/README.md b/README.md index f129093f5..da0d73d3a 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,19 @@ App|Description ---|--- [hello_interp](interp/hello_interp) | A bundle of small examples, showing how to access the core-local interpolator hardware, and use most of its features. +### Low Power + +These examples demonstrate how to use the pico_low_power library. + +App|Description +---|--- +[low_power_domant_timer](low_power/low_power_dormant) | Go dormant (disable clocks) and wakeup on a timer. Requires an external clock for when testing with RP2040. See [low_power_clksrc](low_power/low_power_dormant). +[low_power_dormant_gpio](low_power/low_power_dormant) | Go dormant (disable clocks) and wakeup on a GPIO. +[low_power_pstate_timer](low_power/low_power_pstate) | Go to a lower power state (RP2350 only) and restart on an AON timer. +[low_power_pstate_gpio](low_power/low_power_pstate) | Go to a lower power state (RP2350 only) and restart on a GPIO. +[low_power_sleep_timer](low_power/low_power_sleep) | Go to sleep and wakeup on a timer. +[low_power_sleep_gpio](low_power/low_power_sleep) | Go to sleep and wakeup on a GPIO. + ### Multicore App|Description diff --git a/low_power/CMakeLists.txt b/low_power/CMakeLists.txt new file mode 100644 index 000000000..e53891a6c --- /dev/null +++ b/low_power/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(low_power_sleep) +add_subdirectory(low_power_dormant) +if (NOT PICO_RP2040) + add_subdirectory(low_power_pstate) +endif() diff --git a/low_power/low_power_dormant/CMakeLists.txt b/low_power/low_power_dormant/CMakeLists.txt new file mode 100644 index 000000000..8932b5ac2 --- /dev/null +++ b/low_power/low_power_dormant/CMakeLists.txt @@ -0,0 +1,38 @@ +add_executable(low_power_dormant_timer + low_power_dormant_timer.c +) +target_link_libraries(low_power_dormant_timer PRIVATE + pico_stdlib + pico_low_power + pico_status_led +) +pico_add_extra_outputs(low_power_dormant_timer) +example_auto_set_url(low_power_dormant_timer) +pico_enable_stdio_usb(low_power_dormant_timer 0) +pico_enable_stdio_uart(low_power_dormant_timer 1) + +add_executable(low_power_dormant_gpio + low_power_dormant_gpio.c +) +target_link_libraries(low_power_dormant_gpio PRIVATE + pico_stdlib + pico_low_power + pico_status_led +) +pico_add_extra_outputs(low_power_dormant_gpio) +example_auto_set_url(low_power_dormant_gpio) +pico_enable_stdio_usb(low_power_dormant_gpio 0) +pico_enable_stdio_uart(low_power_dormant_gpio 1) + +# firmware for generating a clock on gp21 for running the dormant test on rp2040 +# connect the clock out from gp21 to gp20 (or gp22) on the rp2040 running the dormant test +add_executable(low_power_clksrc + low_power_clksrc.c +) +target_link_libraries(low_power_clksrc PRIVATE + pico_stdlib + pico_status_led +) +pico_add_extra_outputs(low_power_clksrc) +pico_enable_stdio_usb(low_power_clksrc 0) +pico_enable_stdio_uart(low_power_clksrc 1) diff --git a/low_power/low_power_dormant/low_power_clksrc.c b/low_power/low_power_dormant/low_power_clksrc.c new file mode 100644 index 000000000..c76c248d8 --- /dev/null +++ b/low_power/low_power_dormant/low_power_clksrc.c @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "pico/stdlib.h" +#include "pico/status_led.h" +#include "pico/sync.h" +#include "hardware/clocks.h" + +#ifndef RTC_CLOCK_SRC_GPIO_OUT +#define RTC_CLOCK_SRC_GPIO_OUT 21 +#endif + +bool repeater(repeating_timer_t *timer) { + printf(" Repeating timer at %dms\n", to_ms_since_boot(get_absolute_time())); + status_led_set_state(!status_led_get_state()); + return true; +} + +int main() { + stdio_init_all(); + status_led_init(); + + clock_gpio_init(RTC_CLOCK_SRC_GPIO_OUT, CLOCKS_CLK_GPOUT3_CTRL_AUXSRC_VALUE_CLK_USB, 1024); // 48kHz + + repeating_timer_t repeat; + add_repeating_timer_ms(500, repeater, NULL, &repeat); + + while (true) __wfi(); + + return 0; +} \ No newline at end of file diff --git a/low_power/low_power_dormant/low_power_dormant_gpio.c b/low_power/low_power_dormant/low_power_dormant_gpio.c new file mode 100644 index 000000000..4d6c726e7 --- /dev/null +++ b/low_power/low_power_dormant/low_power_dormant_gpio.c @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "pico/stdlib.h" +#include "pico/low_power.h" +#include "pico/status_led.h" + +// GPIO to wait to go high, connect the other end to 3V3 OUT +#ifndef LOW_POWER_WAKE_GPIO +#define LOW_POWER_WAKE_GPIO 15 +#endif + +#ifndef CLOCK_SOURCE +#if PICO_RP2040 +#define CLOCK_SOURCE DORMANT_CLOCK_SOURCE_XOSC +#else +#define CLOCK_SOURCE DORMANT_CLOCK_SOURCE_LPOSC +#endif +#endif + +// Got to sleep and wakeup when gpio goes high +int main() { + + stdio_init_all(); + hard_assert(status_led_init()); + gpio_init(LOW_POWER_WAKE_GPIO); + printf("State of gpio %u is %u\n", LOW_POWER_WAKE_GPIO, gpio_get(LOW_POWER_WAKE_GPIO)); + + uint32_t count = 1; + while(true) { + status_led_set_state(true); + + printf("Wake up, test run: %u\n", count++); + + // Wait for gpio to go low + if (gpio_get(LOW_POWER_WAKE_GPIO)) { + printf("Awake until gpio %u goes low\n", LOW_POWER_WAKE_GPIO); + while(gpio_get(LOW_POWER_WAKE_GPIO)) { + tight_loop_contents(); + } + } + + // go dormant + printf("Dormant until gpio %u goes high\n", LOW_POWER_WAKE_GPIO); + status_led_set_state(false); + + int rc = low_power_dormant_until_gpio_pin_state(LOW_POWER_WAKE_GPIO, true, true, CLOCK_SOURCE, NULL); + status_led_set_state(true); + if (rc != PICO_OK) { + printf("low_power_dormant_until_aon_timer returned error %d\n", rc); + hard_assert(false); + } + } + return 0; +} diff --git a/low_power/low_power_dormant/low_power_dormant_timer.c b/low_power/low_power_dormant/low_power_dormant_timer.c new file mode 100644 index 000000000..346bfe058 --- /dev/null +++ b/low_power/low_power_dormant/low_power_dormant_timer.c @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "pico/stdlib.h" +#include "pico/low_power.h" +#include "pico/aon_timer.h" +#include "pico/status_led.h" + +#ifdef PICO_RP2350 +#include "hardware/powman.h" +#endif + +// How long to wait +#ifndef AWAKE_TIME_MS +#define AWAKE_TIME_MS 10000 +#endif +#ifndef SLEEP_TIME_MS +#define SLEEP_TIME_MS 10000 +#endif + +#if PICO_RP2040 +#ifndef RTC_CLOCK_SRC_GPIO_IN +#define RTC_CLOCK_SRC_GPIO_IN 20 +#endif +#endif + +#ifndef CLOCK_SOURCE +#if PICO_RP2040 +#define CLOCK_SOURCE DORMANT_CLOCK_SOURCE_XOSC +#else +#define CLOCK_SOURCE DORMANT_CLOCK_SOURCE_LPOSC +#endif +#endif + +// Got to sleep and wakeup after 5 seconds +// The example will repeatedly wait 10 seconds then switch off for 10 seconds +// The debugger will appear to be unresponsive while the device is off +int main() { + stdio_init_all(); + // Must start aon timer + low_power_start_aon_timer_at_time_ms(1776858754000); +#if AWAKE_TIME_MS < 10000 + // pause for at least 10s to allow the debugger to attach on power up to allow the device to be re-programmed + printf("Waiting a bit to allow debugger to attach\n"); + sleep_ms(10000 - AWAKE_TIME_MS); +#endif + hard_assert(status_led_init()); + uint32_t count = 1; + while(true) { + status_led_set_state(true); + + printf("Wake up, test run: %u\n", count++); + + // Stay awake for a few seconds + printf("Awake for %dms\n", AWAKE_TIME_MS); + sleep_ms(AWAKE_TIME_MS); + + // go dormant + printf("Dormant for %dms\n", SLEEP_TIME_MS); + status_led_set_state(false); + +#if PICO_RP2040 + low_power_set_external_clock_source(RTC_CLOCK_FREQ_HZ, RTC_CLOCK_SRC_GPIO_IN); +#endif + int rc = low_power_dormant_for_ms(SLEEP_TIME_MS, CLOCK_SOURCE, NULL); + status_led_set_state(true); + if (rc != PICO_OK) { + printf("low_power_dormant_for_ms returned error %d\n", rc); + hard_assert(false); + } + } + return 0; +} diff --git a/low_power/low_power_pstate/CMakeLists.txt b/low_power/low_power_pstate/CMakeLists.txt new file mode 100644 index 000000000..5889d7b6a --- /dev/null +++ b/low_power/low_power_pstate/CMakeLists.txt @@ -0,0 +1,27 @@ +add_executable(low_power_pstate_timer + low_power_pstate_timer.c + ) +target_link_libraries(low_power_pstate_timer PRIVATE + pico_stdlib + pico_low_power + pico_status_led +) +pico_set_persistent_data_loc(low_power_pstate_timer 0x20040000) # SRAM1 power domain +pico_add_extra_outputs(low_power_pstate_timer) +example_auto_set_url(low_power_pstate_timer) +pico_enable_stdio_usb(low_power_pstate_timer 0) +pico_enable_stdio_uart(low_power_pstate_timer 1) + +add_executable(low_power_pstate_gpio + low_power_pstate_gpio.c + ) +target_link_libraries(low_power_pstate_gpio PRIVATE + pico_stdlib + pico_low_power + pico_status_led +) +pico_set_persistent_data_loc(low_power_pstate_gpio 0x20040000) # SRAM1 power domain +pico_add_extra_outputs(low_power_pstate_gpio) +example_auto_set_url(low_power_pstate_gpio) +pico_enable_stdio_usb(low_power_pstate_gpio 0) +pico_enable_stdio_uart(low_power_pstate_gpio 1) diff --git a/low_power/low_power_pstate/low_power_pstate_gpio.c b/low_power/low_power_pstate/low_power_pstate_gpio.c new file mode 100644 index 000000000..bea8f5984 --- /dev/null +++ b/low_power/low_power_pstate/low_power_pstate_gpio.c @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "pico/stdlib.h" +#include "pico/low_power.h" +#include "pico/aon_timer.h" +#include "pico/status_led.h" + +// GPIO to wait to go high, connect the other end to 3V3 OUT +#ifndef LOW_POWER_WAKE_GPIO +#define LOW_POWER_WAKE_GPIO 15 +#endif + +static uint32_t __persistent_data(run_count); + +// The example will repeatedly wait 10 seconds then switch off for 10 seconds +// The debugger will appear to be unresponsive while the device is off +int main() { + stdio_init_all(); + + hard_assert(status_led_init()); + status_led_set_state(true); + + // Scratch register survives power down + printf("Wake up, test run: %u\n", run_count++); + + gpio_init(LOW_POWER_WAKE_GPIO); + printf("State of gpio %u is %u\n", LOW_POWER_WAKE_GPIO, gpio_get(LOW_POWER_WAKE_GPIO)); + + // Wait for gpio to go low + if (gpio_get(LOW_POWER_WAKE_GPIO)) { + printf("Awake until gpio %u goes low\n", LOW_POWER_WAKE_GPIO); + while(gpio_get(LOW_POWER_WAKE_GPIO)) { + tight_loop_contents(); + } + } + + // power off + printf("Low power until gpio %u goes high\n", LOW_POWER_WAKE_GPIO); + status_led_set_state(false); + int rc = low_power_pstate_until_gpio_pin_state(LOW_POWER_WAKE_GPIO, true, true, NULL, NULL); + status_led_set_state(true); + printf("low_power_pstate_until_aon_timer returned error %d\n", rc); + hard_assert(false); // should never get here! + return 0; +} diff --git a/low_power/low_power_pstate/low_power_pstate_timer.c b/low_power/low_power_pstate/low_power_pstate_timer.c new file mode 100644 index 000000000..48bcc9630 --- /dev/null +++ b/low_power/low_power_pstate/low_power_pstate_timer.c @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "pico/stdlib.h" +#include "pico/low_power.h" +#include "pico/aon_timer.h" +#include "pico/status_led.h" + +// How long to wait +#ifndef AWAKE_TIME_MS +#define AWAKE_TIME_MS 10000 +#endif +#ifndef SLEEP_TIME_MS +#define SLEEP_TIME_MS 10000 +#endif + +static uint32_t __persistent_data(run_count); +static bool __persistent_data(aon_timer_started); + +// The example will repeatedly wait 10 seconds then switch off for 10 seconds +// The debugger will appear to be unresponsive while the device is off +int main() { + stdio_init_all(); + // Must start the aon timer if needed + printf("Current time: %llu\n", aon_timer_get_absolute_time()); + if (!aon_timer_started) { + low_power_start_aon_timer_at_time_ms(1776858754000); + aon_timer_started = true; + } +#if AWAKE_TIME_MS < 10000 + // pause for at least 10s to allow the debugger to attach on power up to allow the device to be re-programmed + printf("Waiting a bit to allow debugger to attach\n"); + sleep_ms(10000 - AWAKE_TIME_MS); +#endif + hard_assert(status_led_init()); + status_led_set_state(true); + + // Scratch register survives power down + printf("Wake up, test run: %u\n", run_count++); + + // Stay awake for a few seconds + printf("Awake for %dms\n", AWAKE_TIME_MS); + sleep_ms(AWAKE_TIME_MS); + + // power off + printf("Low power for %dms\n", SLEEP_TIME_MS); + status_led_set_state(false); + int rc = low_power_pstate_for_ms(SLEEP_TIME_MS, NULL, NULL); + status_led_set_state(true); + printf("low_power_pstate_for_ms returned error %d\n", rc); + hard_assert(false); // should never get here! + return 0; +} diff --git a/low_power/low_power_sleep/CMakeLists.txt b/low_power/low_power_sleep/CMakeLists.txt new file mode 100644 index 000000000..b143d5364 --- /dev/null +++ b/low_power/low_power_sleep/CMakeLists.txt @@ -0,0 +1,25 @@ +add_executable(low_power_sleep_timer + low_power_sleep_timer.c + ) +target_link_libraries(low_power_sleep_timer PRIVATE + pico_stdlib + pico_low_power + pico_status_led +) +pico_add_extra_outputs(low_power_sleep_timer) +example_auto_set_url(low_power_sleep_timer) +pico_enable_stdio_usb(low_power_sleep_timer 0) +pico_enable_stdio_uart(low_power_sleep_timer 1) + +add_executable(low_power_sleep_gpio + low_power_sleep_gpio.c + ) +target_link_libraries(low_power_sleep_gpio PRIVATE + pico_stdlib + pico_low_power + pico_status_led +) +pico_add_extra_outputs(low_power_sleep_gpio) +example_auto_set_url(low_power_sleep_gpio) +pico_enable_stdio_usb(low_power_sleep_gpio 0) +pico_enable_stdio_uart(low_power_sleep_gpio 1) diff --git a/low_power/low_power_sleep/low_power_sleep_gpio.c b/low_power/low_power_sleep/low_power_sleep_gpio.c new file mode 100644 index 000000000..0f6400648 --- /dev/null +++ b/low_power/low_power_sleep/low_power_sleep_gpio.c @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "pico/stdlib.h" +#include "pico/low_power.h" +#include "pico/status_led.h" + +// GPIO to wait to go high, connect the other end to 3V3 OUT +#ifndef LOW_POWER_WAKE_GPIO +#define LOW_POWER_WAKE_GPIO 15 +#endif + +// Got to sleep and wakeup after 10 seconds +int main() { + + stdio_init_all(); + hard_assert(status_led_init()); + gpio_init(LOW_POWER_WAKE_GPIO); + printf("State of gpio %u is %u\n", LOW_POWER_WAKE_GPIO, gpio_get(LOW_POWER_WAKE_GPIO)); + + uint32_t count = 1; + while(true) { + status_led_set_state(true); + + printf("Wake up, test run: %u\n", count++); + + // Wait for gpio to go low + if (gpio_get(LOW_POWER_WAKE_GPIO)) { + printf("Awake until gpio %u goes low\n", LOW_POWER_WAKE_GPIO); + while(gpio_get(LOW_POWER_WAKE_GPIO)) { + tight_loop_contents(); + } + } + + // go to sleep + printf("Sleeping until gpio %u goes high\n", LOW_POWER_WAKE_GPIO); + status_led_set_state(false); + + int rc = low_power_sleep_until_gpio_pin_state(LOW_POWER_WAKE_GPIO, true, true, NULL, true); + status_led_set_state(true); + if (rc != PICO_OK) { + printf("low_power_sleep_until_gpio_pin_state returned error %d\n", rc); + hard_assert(false); + } + } + return 0; +} diff --git a/low_power/low_power_sleep/low_power_sleep_timer.c b/low_power/low_power_sleep/low_power_sleep_timer.c new file mode 100644 index 000000000..94ac03ee7 --- /dev/null +++ b/low_power/low_power_sleep/low_power_sleep_timer.c @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "pico/stdlib.h" +#include "pico/low_power.h" +#include "pico/status_led.h" + +// How long to wait +#ifndef AWAKE_TIME_MS +#define AWAKE_TIME_MS 10000 +#endif +#ifndef SLEEP_TIME_MS +#define SLEEP_TIME_MS 10000 +#endif + +// Got to sleep and wakeup after 10 seconds +int main() { + + stdio_init_all(); + hard_assert(status_led_init()); + + uint32_t count = 1; + while(true) { + status_led_set_state(true); + + printf("Wake up, test run: %u\n", count++); + + // Stay awake for a few seconds + printf("Awake for %dms\n", AWAKE_TIME_MS); + sleep_ms(AWAKE_TIME_MS); + + // go to sleep + printf("Sleeping for %dms\n", SLEEP_TIME_MS); + status_led_set_state(false); + int rc = low_power_sleep_for_ms(SLEEP_TIME_MS, NULL, true); + status_led_set_state(true); + if (rc != PICO_OK) { + printf("low_power_sleep_for_ms returned error %d\n", rc); + hard_assert(false); + } + } + return 0; +}