initial commit

This commit is contained in:
2026-09-25 10:48:51 +10:00
commit b90beea8ae
1020 changed files with 131665 additions and 0 deletions
@@ -0,0 +1,188 @@
#if defined(ARDUINO_ARCH_RP2350) || defined(PICO_RP2350) || \
defined(TARGET_RP2350)
#include "WatchdogRP2350.h"
/**********************************************************************************************/
/*!
@brief Initializes the RP2350's hardware watchdog timer.
@param maxPeriodMS
Timeout period of WDT in milliseconds
@return The actual period (in milliseconds) before a watchdog timer
reset is returned, 0 otherwise.
*/
/**********************************************************************************************/
int WatchdogRP2350::enable(int maxPeriodMS) {
if (maxPeriodMS < 0)
return 0;
// Enables the RP2350's hardware WDT with maxPeriodMS delay
// (wdt should be updated every maxPeriodMS ms) and
// enables pausing the WDT on debugging when stepping thru
watchdog_enable(maxPeriodMS, 1);
_wdto = maxPeriodMS;
return maxPeriodMS;
}
/**************************************************************************/
/*!
@brief Reload the watchdog counter with the amount of time set in
enable().
*/
/**************************************************************************/
void WatchdogRP2350::reset() { watchdog_update(); }
/**************************************************************************/
/*!
@brief Once enabled, the RP2350's Watchdog Timer can NOT be disabled.
*/
/**************************************************************************/
void WatchdogRP2350::disable() {}
/**************************************************************************/
/*!
@brief Configures the RP2350 to enter a lower power (WFE) sleep
for a period of time.
@param maxPeriodMS
Time to sleep the RP2350, in millis.
@return The actual period (in milliseconds) that the hardware was
asleep will be returned. Otherwise, 0 will be returned if the
hardware could not enter the low-power mode.
*/
/**************************************************************************/
int WatchdogRP2350::sleep(int maxPeriodMS) {
if (maxPeriodMS < 0)
return 0;
// perform a lower power (WFE) sleep (pico-core calls sleep_ms(sleepTime))
sleep_ms(maxPeriodMS);
return maxPeriodMS;
}
/**************************************************************************/
/*!
@brief Re-enables clock sources and generators after waking from sleep,
must be called by user code.
*/
/**************************************************************************/
void WatchdogRP2350::resumeFromSleep() {
// Re-enable clock sources and generators
sleep_power_up();
// Try to reattach USB connection on "native USB" boards (connection is
// lost on sleep). Host will also need to reattach to the Serial monitor.
// Seems not entirely reliable, hence the LED indicator fallback.
#if defined(USBCON) && !defined(USE_TINYUSB)
USBDevice.attach();
#endif
}
/**************************************************************************/
/*!
@brief Sets a callback function to be called upon wake from sleep. Only one
callback can be set at a time.
@param cb
Pointer to the user-defined callback function.
*/
/**************************************************************************/
void WatchdogRP2350::setWakeCb(WakeCb cb) { _cb_wake = cb; }
/**************************************************************************/
/*!
@brief Gets the duration of the previous sleep cycle, in milliseconds.
@return The sleep duration in milliseconds.
*/
/**************************************************************************/
long WatchdogRP2350::getSleepDuration() {
// Obtain the current time from the AON timer
struct timespec ts_sleep_end;
aon_timer_get_time(&ts_sleep_end);
// Compare timespecs to get sleep duration
long sleep_duration_ms =
(ts_sleep_end.tv_sec - _ts_sleep_start.tv_sec) * 1000 +
(ts_sleep_end.tv_nsec - _ts_sleep_start.tv_nsec) / 1000000;
return sleep_duration_ms;
}
/**************************************************************************/
/*!
@brief Puts the RP2350 into Sleep State (6.5.2, 6.5.3 in RP2350 Datasheet)
for a specified period of time, uses the AON timer to wake the device.
@param max_period_ms
Desired sleep period, in milliseconds.
@param is_dormant
If true, enters Dormant State (6.5.3) instead of Sleep State.
*/
/**************************************************************************/
void WatchdogRP2350::goToSleepUntil(int max_period_ms, bool is_dormant) {
if (max_period_ms < 0)
return;
// Configure the AON timer
startAonTimer();
// Get and store the sleep start time
aon_timer_get_time(&_ts_sleep_start);
// Configure the sleep clock source
if (!is_dormant) {
sleep_run_from_xosc();
} else {
sleep_run_from_lposc();
}
// Configure aon_timer alarm
struct timespec ts;
aon_timer_get_time(&ts);
ts.tv_sec += max_period_ms / 1000;
// Enter sleep/dormant mode until the AON timer alarm fires
if (!is_dormant) {
sleep_goto_sleep_until(&ts, _cb_wake);
} else {
sleep_goto_dormant_until(&ts, _cb_wake);
}
}
/**************************************************************************/
/*!
@brief Puts the RP2350 into Dormant State (6.5.3 in RP2350 Datasheet) until
the specified GPIO pin changes state.
@param gpio_pin
GPIO pin to monitor for wakeup.
@param edge
True for rising edge, False for falling edge
@param high
True for active high, False for active low.
*/
/**************************************************************************/
void WatchdogRP2350::goToSleepUntilPin(uint gpio_pin, bool edge, bool high) {
// Set the crystal oscillator as the dormant clock source, UART will be
// reconfigured from here This is necessary before sending the pico into
// dormancy
// NOTE: Because we are using the crystal oscillator as the clock source, the
// AON timer will not run, so we can not use the AON timer to measure sleep
// duration like we do in goToSleepUntil().
sleep_run_from_xosc();
// Enter dormant state until the specified GPIO pin changes state
sleep_goto_dormant_until_pin(gpio_pin, edge, high);
}
/**************************************************************************/
/*!
@brief Helper function to initialize and check AON timer.
@return True if the AON timer has been started, False otherwise.
*/
bool WatchdogRP2350::startAonTimer() {
if (!_aon_timer_started) {
struct timespec ts_init = {.tv_sec = 1723124088, .tv_nsec = 0};
aon_timer_start(&ts_init);
_aon_timer_started = true;
}
return _aon_timer_started;
}
#endif // ARDUINO_ARCH_RP2350 || PICO_RP2350 || TARGET_RP2350
@@ -0,0 +1,60 @@
/*!
* @file WatchdogRP2350.h
*
* Support for RP2350 Hardware Watchdog Timer API
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
* Written by Brent Rubell for Adafruit Industries.
*
* MIT License, all text here must be included in any redistribution.
*
*/
#ifndef WATCHDOGRP2350_H_
#define WATCHDOGRP2350_H_
#include "pico/aon_timer.h"
#include "pico_sleep/include/pico/sleep.h"
#include <hardware/watchdog.h>
#include <pico/time.h>
// Callback type for wake-from-sleep functions
typedef void (*WakeCb)(void);
/**************************************************************************/
/*!
@brief Class that contains functions for interacting with the
RP2350's hardware watchdog timer and low-power sleep functionality.
*/
/**************************************************************************/
class WatchdogRP2350 {
public:
WatchdogRP2350() : _wdto(-1), _aon_timer_started(false){};
// Watchdog API
int enable(int maxPeriodMS = 0);
void disable()
__attribute__((error("RP2350 WDT cannot be disabled once enabled")));
void reset();
// Low-power ("sleep") mode API
int sleep(
int maxPeriodMS = 0); // Basic sleep function, handled by software timer
// Advanced "Sleep" State API
// Sleep State (6.5.2, 6.5.3 in RP2350 Datasheet)
void goToSleepUntil(int max_period_ms = 0, bool is_dormant = false);
void goToSleepUntilPin(uint gpio_pin, bool edge = false, bool high = false);
// Advanced sleep state helper functions
void setWakeCb(WakeCb cb);
void resumeFromSleep();
long getSleepDuration();
private:
int _wdto;
WakeCb _cb_wake = nullptr;
struct timespec _ts_sleep_start;
bool _aon_timer_started;
bool startAonTimer();
};
#endif // WATCHDOGRP2350_H_
@@ -0,0 +1 @@
pico_simple_hardware_target(rosc)
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#if defined(ARDUINO_ARCH_RP2350) || defined(PICO_RP2350) || \
defined(TARGET_RP2350)
#ifndef _HARDWARE_ROSC_H_
#define _HARDWARE_ROSC_H_
#include "hardware/structs/rosc.h"
#include "pico.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file rosc.h
* \defgroup hardware_rosc hardware_rosc
*
* Ring Oscillator (ROSC) API
*
* A Ring Oscillator is an on-chip oscillator that requires no external crystal.
* Instead, the output is generated from a series of inverters that are chained
* together to create a feedback loop. RP2040 boots from the ring oscillator
* initially, meaning the first stages of the bootrom, including booting from
* SPI flash, will be clocked by the ring oscillator. If your design has a
* crystal oscillator, you'll likely want to switch to this as your reference
* clock as soon as possible, because the frequency is more accurate than the
* ring oscillator.
*/
/*! \brief Set frequency of the Ring Oscillator
* \ingroup hardware_rosc
*
* \param code The drive strengths. See the RP2040 datasheet for information on
* this value.
*/
void rosc_set_freq(uint32_t code);
/*! \brief Set range of the Ring Oscillator
* \ingroup hardware_rosc
*
* Frequency range. Frequencies will vary with Process, Voltage & Temperature
* (PVT). Clock output will not glitch when changing the range up one step at a
* time.
*
* \param range 0x01 Low, 0x02 Medium, 0x03 High, 0x04 Too High.
*/
void rosc_set_range(uint range);
/*! \brief Disable the Ring Oscillator
* \ingroup hardware_rosc
*
*/
void rosc_disable(void);
/*! \brief Put Ring Oscillator in to dormant mode.
* \ingroup hardware_rosc
*
* The ROSC supports a dormant mode,which stops oscillation until woken up up by
* an asynchronous interrupt. This can either come from the RTC, being clocked
* by an external clock, or a GPIO pin going high or low. If no IRQ is
* configured before going into dormant mode the ROSC will never restart.
*
* PLLs should be stopped before selecting dormant mode.
*/
void rosc_set_dormant(void);
// FIXME: Add doxygen
uint32_t next_rosc_code(uint32_t code);
uint rosc_find_freq(uint32_t low_mhz, uint32_t high_mhz);
void rosc_set_div(uint32_t div);
inline static void rosc_clear_bad_write(void) {
hw_clear_bits(&rosc_hw->status, ROSC_STATUS_BADWRITE_BITS);
}
inline static bool rosc_write_okay(void) {
return !(rosc_hw->status & ROSC_STATUS_BADWRITE_BITS);
}
inline static void rosc_write(io_rw_32 *addr, uint32_t value) {
rosc_clear_bad_write();
assert(rosc_write_okay());
*addr = value;
assert(rosc_write_okay());
};
void rosc_enable(void);
#ifdef __cplusplus
}
#endif
#endif // _HARDWARE_ROSC_H_
#endif // ARDUINO_ARCH_RP2350
@@ -0,0 +1,81 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#if defined(ARDUINO_ARCH_RP2350)
#include "pico.h"
// For MHZ definitions etc
#include "hardware/clocks.h"
#include "include/hardware/rosc.h"
// Given a ROSC delay stage code, return the next-numerically-higher code.
// Top result bit is set when called on maximum ROSC code.
uint32_t next_rosc_code(uint32_t code) {
return ((code | 0x08888888u) + 1u) & 0xf7777777u;
}
uint rosc_find_freq(uint32_t low_mhz, uint32_t high_mhz) {
// TODO: This could be a lot better
rosc_set_div(1);
for (uint32_t code = 0; code <= 0x77777777u; code = next_rosc_code(code)) {
rosc_set_freq(code);
uint rosc_mhz =
frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC) / 1000;
if ((rosc_mhz >= low_mhz) && (rosc_mhz <= high_mhz)) {
return rosc_mhz;
}
}
return 0;
}
void rosc_set_div(uint32_t div) {
assert(div <= 31 && div >= 1);
rosc_write(&rosc_hw->div, ROSC_DIV_VALUE_PASS + div);
}
void rosc_set_freq(uint32_t code) {
rosc_write(&rosc_hw->freqa,
(ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) |
(code & 0xffffu));
rosc_write(&rosc_hw->freqb,
(ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) |
(code >> 16u));
}
void rosc_set_range(uint range) {
// Range should use enumvals from the headers and thus have the password
// correct
rosc_write(&rosc_hw->ctrl,
(ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB) | range);
}
void rosc_disable(void) {
uint32_t tmp = rosc_hw->ctrl;
tmp &= (~ROSC_CTRL_ENABLE_BITS);
tmp |= (ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB);
rosc_write(&rosc_hw->ctrl, tmp);
// Wait for stable to go away
while (rosc_hw->status & ROSC_STATUS_STABLE_BITS)
;
}
void rosc_set_dormant(void) {
// WARNING: This stops the rosc until woken up by an irq
rosc_write(&rosc_hw->dormant, ROSC_DORMANT_VALUE_DORMANT);
// Wait for it to become stable once woken up
while (!(rosc_hw->status & ROSC_STATUS_STABLE_BITS))
;
}
void rosc_enable(void) {
// Re-enable the rosc
rosc_write(&rosc_hw->ctrl, ROSC_CTRL_ENABLE_BITS);
// Wait for it to become stable once restarted
while (!(rosc_hw->status & ROSC_STATUS_STABLE_BITS))
;
}
#endif // ARDUINO_ARCH_RP2350
@@ -0,0 +1,5 @@
pico_simple_hardware_target(sleep) pico_mirrored_target_link_libraries(
hardware_sleep INTERFACE hardware_clocks hardware_rosc hardware_irq
pico_aon_timer) target_include_directories(hardware_sleep INTERFACE ${
CMAKE_CURRENT_LIST_DIR} /
include)
@@ -0,0 +1,159 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#if defined(ARDUINO_ARCH_RP2350) || defined(PICO_RP2350) || \
defined(TARGET_RP2350)
#ifndef _PICO_SLEEP_H_
#define _PICO_SLEEP_H_
#include "../../../hardware_rosc/include/hardware/rosc.h"
#include "hardware/timer.h"
#include "pico.h"
#include "pico/aon_timer.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file sleep.h
* \defgroup hardware_sleep hardware_sleep
*
* Lower Power Sleep API
*
* The difference between sleep and dormant is that ALL clocks are stopped in
dormant mode,
* until the source (either xosc or rosc) is started again by an external event.
* In sleep mode some clocks can be left running controlled by the SLEEP_EN
registers in the clocks
* block. For example you could keep clk_rtc running. Some destinations (proc0
and proc1 wakeup logic)
* can't be stopped in sleep mode otherwise there wouldn't be enough logic to
wake up again.
*
* \subsection sleep_example Example
* \addtogroup hardware_sleep
* \include hello_sleep.c
*/
typedef enum {
DORMANT_SOURCE_NONE,
DORMANT_SOURCE_XOSC,
DORMANT_SOURCE_ROSC,
DORMANT_SOURCE_LPOSC, // rp2350 only
} dormant_source_t;
/*! \brief Set all clock sources to the the dormant clock source to prepare for
* sleep. \ingroup hardware_sleep
*
* \param dormant_source The dormant clock source to use
*/
void sleep_run_from_dormant_source(dormant_source_t dormant_source);
/*! \brief Set the dormant clock source to be the crystal oscillator
* \ingroup hardware_sleep
*/
static inline void sleep_run_from_xosc(void) {
sleep_run_from_dormant_source(DORMANT_SOURCE_XOSC);
}
#if !PICO_RP2040
static inline void sleep_run_from_lposc(void) {
sleep_run_from_dormant_source(DORMANT_SOURCE_LPOSC);
}
#endif
/*! \brief Set the dormant clock source to be the ring oscillator
* \ingroup hardware_sleep
*/
static inline void sleep_run_from_rosc(void) {
sleep_run_from_dormant_source(DORMANT_SOURCE_ROSC);
}
/*! \brief Send system to sleep until the specified time
* \ingroup hardware_sleep
*
* One of the sleep_run_* functions must be called prior to this call
*
* \param ts The time to wake up
* \param callback Function to call on wakeup.
*/
void sleep_goto_sleep_until(struct timespec *ts,
aon_timer_alarm_handler_t callback);
/*! \brief Send system to sleep for a specified duration in milliseconds. This
provides an alternative to sleep_goto_sleep_until to allow for shorter duration
sleeps.
* \ingroup hardware_sleep
*
* One of the sleep_run_* functions must be called prior to this call
*
* \param delay_ms The duration to sleep for in milliseconds.
* \param callback Function to call on wakeup.
* \return Returns true if the device went to sleep
*/
bool sleep_goto_sleep_for(uint32_t delay_ms,
hardware_alarm_callback_t callback);
/*! \brief Send system to dormant until the specified time, note for RP2040 the
* RTC must be driven by an external clock \ingroup hardware_sleep
*
* One of the sleep_run_* functions must be called prior to this call
*
* \param ts The time to wake up
* \param callback Function to call on wakeup.
*/
void sleep_goto_dormant_until(struct timespec *ts,
aon_timer_alarm_handler_t callback);
/*! \brief Send system to sleep until the specified GPIO changes
* \ingroup hardware_sleep
*
* One of the sleep_run_* functions must be called prior to this call
*
* \param gpio_pin The pin to provide the wake up
* \param edge true for leading edge, false for trailing edge
* \param high true for active high, false for active low
*/
void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high);
/*! \brief Send system to sleep until a leading high edge is detected on GPIO
* \ingroup hardware_sleep
*
* One of the sleep_run_* functions must be called prior to this call
*
* \param gpio_pin The pin to provide the wake up
*/
static inline void sleep_goto_dormant_until_edge_high(uint gpio_pin) {
sleep_goto_dormant_until_pin(gpio_pin, true, true);
}
/*! \brief Send system to sleep until a high level is detected on GPIO
* \ingroup hardware_sleep
*
* One of the sleep_run_* functions must be called prior to this call
*
* \param gpio_pin The pin to provide the wake up
*/
static inline void sleep_goto_dormant_until_level_high(uint gpio_pin) {
sleep_goto_dormant_until_pin(gpio_pin, false, true);
}
/*! \brief Reconfigure clocks to wake up properly from sleep/dormant mode
* \ingroup hardware_sleep
*
* This must be called immediately after continuing execution when waking up
* from sleep/dormant mode
*
*/
void sleep_power_up(void);
#ifdef __cplusplus
}
#endif
#endif // _PICO_SLEEP_H_
#endif // ARDUINO_ARCH_RP2350
@@ -0,0 +1,310 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#if defined(ARDUINO_ARCH_RP2350)
#include <inttypes.h>
#include <stdio.h>
#include "pico.h"
#include "include/pico/sleep.h"
#include "pico/stdlib.h"
#include "hardware/clocks.h"
#include "hardware/pll.h"
#include "hardware/regs/clocks.h"
#include "hardware/regs/io_bank0.h"
#include "hardware/watchdog.h"
#include "hardware/xosc.h"
// For __wfi
#include "hardware/sync.h"
#include "pico/runtime_init.h"
#ifdef __riscv
#include "hardware/riscv.h"
#else
// For scb_hw so we can enable deep sleep
#include "hardware/structs/scb.h"
#endif
#if !PICO_RP2040
#include "hardware/powman.h"
#endif
// The difference between sleep and dormant is that ALL clocks are stopped in
// dormant mode, until the source (either xosc or rosc) is started again by an
// external event. In sleep mode some clocks can be left running controlled by
// the SLEEP_EN registers in the clocks block. For example you could keep
// clk_rtc running. Some destinations (proc0 and proc1 wakeup logic) can't be
// stopped in sleep mode otherwise there wouldn't be enough logic to wake up
// again.
static dormant_source_t _dormant_source;
bool dormant_source_valid(dormant_source_t dormant_source) {
switch (dormant_source) {
case DORMANT_SOURCE_XOSC:
return true;
case DORMANT_SOURCE_ROSC:
return true;
#if !PICO_RP2040
case DORMANT_SOURCE_LPOSC:
return true;
#endif
default:
return false;
}
}
// In order to go into dormant mode we need to be running from a stoppable clock
// source: either the xosc or rosc with no PLLs running. This means we disable
// the USB and ADC clocks and all PLLs
void sleep_run_from_dormant_source(dormant_source_t dormant_source) {
assert(dormant_source_valid(dormant_source));
_dormant_source = dormant_source;
uint src_hz;
uint clk_ref_src;
switch (dormant_source) {
case DORMANT_SOURCE_XOSC:
src_hz = XOSC_HZ;
clk_ref_src = CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC;
break;
case DORMANT_SOURCE_ROSC:
src_hz = 6500 * KHZ; // todo
clk_ref_src = CLOCKS_CLK_REF_CTRL_SRC_VALUE_ROSC_CLKSRC_PH;
break;
#if !PICO_RP2040
case DORMANT_SOURCE_LPOSC:
src_hz = 32 * KHZ;
clk_ref_src = CLOCKS_CLK_REF_CTRL_SRC_VALUE_LPOSC_CLKSRC;
break;
#endif
default:
hard_assert(false);
}
// CLK_REF = XOSC or ROSC
clock_configure(clk_ref, clk_ref_src,
0, // No aux mux
src_hz, src_hz);
// CLK SYS = CLK_REF
clock_configure(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF,
0, // Using glitchless mux
src_hz, src_hz);
// CLK ADC = 0MHz
clock_stop(clk_adc);
clock_stop(clk_usb);
#if PICO_RP2350
clock_stop(clk_hstx);
#endif
#if PICO_RP2040
// CLK RTC = ideally XOSC (12MHz) / 256 = 46875Hz but could be rosc
uint clk_rtc_src = (dormant_source == DORMANT_SOURCE_XOSC)
? CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC
: CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_ROSC_CLKSRC_PH;
clock_configure(clk_rtc,
0, // No GLMUX
clk_rtc_src, src_hz, 46875);
#endif
// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so
// just select and enable
clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
src_hz, src_hz);
pll_deinit(pll_sys);
pll_deinit(pll_usb);
// Assuming both xosc and rosc are running at the moment
if (dormant_source == DORMANT_SOURCE_XOSC) {
// Can disable rosc
rosc_disable();
} else {
// Can disable xosc
xosc_disable();
}
// Reconfigure uart with new clocks
// setup_default_uart();
}
static void processor_deep_sleep(void) {
// Enable deep sleep at the proc
#ifdef __riscv
uint32_t bits = RVCSR_MSLEEP_POWERDOWN_BITS;
if (!get_core_num()) {
bits |= RVCSR_MSLEEP_DEEPSLEEP_BITS;
}
riscv_set_csr(RVCSR_MSLEEP_OFFSET, bits);
#else
scb_hw->scr |= ARM_CPU_PREFIXED(SCR_SLEEPDEEP_BITS);
#endif
}
void sleep_goto_sleep_until(struct timespec *ts,
aon_timer_alarm_handler_t callback) {
// We should have already called the sleep_run_from_dormant_source function
// This is only needed for dormancy although it saves power running from xosc
// while sleeping
// assert(dormant_source_valid(_dormant_source));
#if PICO_RP2040
clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS;
clocks_hw->sleep_en1 = 0x0;
#else
clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_REF_POWMAN_BITS;
clocks_hw->sleep_en1 = 0x0;
#endif
aon_timer_enable_alarm(ts, callback, false);
// stdio_flush();
// Enable deep sleep at the proc
processor_deep_sleep();
// Go to sleep
__wfi();
}
bool sleep_goto_sleep_for(uint32_t delay_ms,
hardware_alarm_callback_t callback) {
// We should have already called the sleep_run_from_dormant_source function
// This is only needed for dormancy although it saves power running from xosc
// while sleeping
// assert(dormant_source_valid(_dormant_source));
// Turn off all clocks except for the timer
clocks_hw->sleep_en0 = 0x0;
#if PICO_RP2040
clocks_hw->sleep_en1 = CLOCKS_SLEEP_EN1_CLK_SYS_TIMER_BITS;
#elif PICO_RP2350
clocks_hw->sleep_en1 = CLOCKS_SLEEP_EN1_CLK_REF_TICKS_BITS |
CLOCKS_SLEEP_EN1_CLK_SYS_TIMER0_BITS;
#else
#error Unknown processor
#endif
int alarm_num = hardware_alarm_claim_unused(true);
hardware_alarm_set_callback(alarm_num, callback);
absolute_time_t t = make_timeout_time_ms(delay_ms);
if (hardware_alarm_set_target(alarm_num, t)) {
hardware_alarm_set_callback(alarm_num, NULL);
hardware_alarm_unclaim(alarm_num);
return false;
}
// stdio_flush();
// Enable deep sleep at the proc
processor_deep_sleep();
// Go to sleep
__wfi();
return true;
}
static void _go_dormant(void) {
assert(dormant_source_valid(_dormant_source));
if (_dormant_source == DORMANT_SOURCE_XOSC) {
xosc_dormant();
} else {
rosc_set_dormant();
}
}
void sleep_goto_dormant_until(struct timespec *ts,
aon_timer_alarm_handler_t callback) {
// We should have already called the sleep_run_from_dormant_source function
#if PICO_RP2040
clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS;
clocks_hw->sleep_en1 = 0x0;
#else
assert(_dormant_source == DORMANT_SOURCE_LPOSC);
uint64_t restore_ms = powman_timer_get_ms();
powman_timer_set_1khz_tick_source_lposc();
powman_timer_set_ms(restore_ms);
clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_REF_POWMAN_BITS;
clocks_hw->sleep_en1 = 0x0;
#endif
// Set the AON timer to wake up the proc from dormant mode
aon_timer_enable_alarm(ts, callback, true);
// stdio_flush();
// Enable deep sleep at the proc
processor_deep_sleep();
// Go dormant
_go_dormant();
}
void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high) {
bool low = !high;
bool level = !edge;
// Configure the appropriate IRQ at IO bank 0
assert(gpio_pin < NUM_BANK0_GPIOS);
uint32_t event = 0;
if (level && low)
event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_LOW_BITS;
if (level && high)
event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_HIGH_BITS;
if (edge && high)
event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_HIGH_BITS;
if (edge && low)
event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_LOW_BITS;
gpio_init(gpio_pin);
gpio_set_input_enabled(gpio_pin, true);
gpio_set_dormant_irq_enabled(gpio_pin, event, true);
_go_dormant();
// Execution stops here until woken up
// Clear the irq so we can go back to dormant mode again if we want
gpio_acknowledge_irq(gpio_pin, event);
gpio_set_input_enabled(gpio_pin, false);
}
// To be called after waking up from sleep/dormant mode to restore system clocks
// properly
void sleep_power_up(void) {
// Re-enable the ring oscillator, which will essentially kickstart the proc
rosc_enable();
// Reset the sleep enable register so peripherals and other hardware can be
// used
clocks_hw->sleep_en0 |= ~(0u);
clocks_hw->sleep_en1 |= ~(0u);
// Restore all clocks
clocks_init();
#if PICO_RP2350
// make powerman use xosc again
uint64_t restore_ms = powman_timer_get_ms();
powman_timer_set_1khz_tick_source_xosc();
powman_timer_set_ms(restore_ms);
#endif
// UART needs to be reinitialised with the new clock frequencies for stable
// output setup_default_uart();
}
#endif // ARDUINO_ARCH_RP2350