mirror of
https://github.com/raspberrypi/pico-sdk.git
synced 2026-03-14 21:19:43 +01:00
Make it compile and work with RP2040 and RP2350
Includes AMY-1782 workaround
This commit is contained in:
parent
991fc65582
commit
eb89a06c64
4 changed files with 83 additions and 48 deletions
|
|
@ -41,6 +41,16 @@ extern "C" {
|
|||
#include "hardware/clocks.h"
|
||||
|
||||
|
||||
typedef enum {
|
||||
DORMANT_CLOCK_SOURCE_XOSC,
|
||||
DORMANT_CLOCK_SOURCE_ROSC,
|
||||
#if !PICO_RP2040
|
||||
DORMANT_CLOCK_SOURCE_LPOSC,
|
||||
#endif
|
||||
NUM_DORMANT_CLOCK_SOURCES
|
||||
} dormant_clock_source_t;
|
||||
|
||||
|
||||
// NOTE: Need to deinit usb before doing into any of these sleep states
|
||||
// could keep usb clk_sys and clk_usb to usbctrl running during low_power_sleep. Although you'll get woken
|
||||
// up pretty quickly
|
||||
|
|
@ -49,7 +59,7 @@ extern "C" {
|
|||
// sleep is really just calling a __wfi() until an irq, with some optional clock gating in the sleep_en register. Activated once processor goes to sleep
|
||||
// So can just do it for arbitrary interrupts. processors implement their own internal clock gating, just leaving the wakeup interrupt controller running during
|
||||
// __wfi()
|
||||
int low_power_sleep_until_irq(const clock_dest_set_t *keep_enabled)
|
||||
int low_power_sleep_until_irq(const clock_dest_set_t *keep_enabled);
|
||||
|
||||
// sleep until the given timer reaches the specified value; if the time passes then no sleep occurs
|
||||
// keep_enabled defaults to none if NULL
|
||||
|
|
@ -58,18 +68,21 @@ int low_power_sleep_until_timer(timer_hw_t *timer, absolute_time_t until, const
|
|||
// Note bool above saying shall we only listen for timer irq or other irqs
|
||||
// Need to defer handling of irqs to do clock setup etc
|
||||
|
||||
static inline int low_power_sleep_until_default_timer(absolute_time_t until, const clock_dest_set_t *keep_enabled) {
|
||||
static inline int low_power_sleep_until_default_timer(absolute_time_t until, const clock_dest_set_t *keep_enabled, bool exclusive) {
|
||||
// Need to assert (or add) ticks block and timer clocks to the keep_enabled list
|
||||
return low_power_sleep_until_timer(PICO_DEFAULT_TIMER_INSTANCE(), until, keep_enabled);
|
||||
return low_power_sleep_until_timer(PICO_DEFAULT_TIMER_INSTANCE(), until, keep_enabled, exclusive);
|
||||
}
|
||||
|
||||
// ** LIAM BLESSED this for RP2040; why not RP2350 **
|
||||
// This should work on both via io bank interrupts
|
||||
void low_power_sleep_until_pin_state(uint gpio_pin, bool edge, bool high);
|
||||
|
||||
#if 0
|
||||
// ** LIAM SAYS THIS IS NO MORE USEFUL THAN SLEEP_UNTIL TIMER... **
|
||||
// There isn't much advantage to using the aon timer here as system timers are more accurate and on anyway
|
||||
// ** WILL AGREES WITH LIAM ON THIS **
|
||||
int low_power_sleep_until_aon_timer(absolute_time_t until, const clock_dest_set_t *keep_enabled);
|
||||
#endif
|
||||
|
||||
// ** LIAM BLESSED but we need to impl it correctly (note not blessed for RP2040, but we should do it anyway for orthogonality **
|
||||
// Only works for RP2350 as every clock will be stopped on RP2040 (unless you provide a clock for the RTC)
|
||||
|
|
@ -78,18 +91,17 @@ int low_power_sleep_until_aon_timer(absolute_time_t until, const clock_dest_set_
|
|||
// NOTE: Asserting that we will alway use rosc for dormant and simplifies the API
|
||||
// Means if the user has sped up the rosc they should slow it down before going into dormant
|
||||
// Need to re initialize clocks after this
|
||||
int low_power_dormant_until_aon_timer(absolute_time_t until, uint src_hz, uint gpio_pin);
|
||||
int low_power_dormant_until_aon_timer(absolute_time_t until, dormant_clock_source_t dormant_clock_source, uint src_hz, uint gpio_pin, const clock_dest_set_t *keep_enabled);
|
||||
|
||||
// ** LIAM BLESSED but we need to impl it correctly (note not blessed for RP2040, but we should do it anyway for orthogonality **
|
||||
// This works on both
|
||||
// Need to re initialize clocks after this
|
||||
void low_power_dormant_until_pin_state(uint gpio_pin, bool edge, bool high);
|
||||
void low_power_dormant_until_pin_state(uint gpio_pin, bool edge, bool high, dormant_clock_source_t dormant_clock_source, const clock_dest_set_t *keep_enabled);
|
||||
|
||||
#if !PICO_RP2040
|
||||
#if !PICO_RP2040 && 0 // todo - implement these
|
||||
// pstate functions should return to the pstate you were in
|
||||
void low_power_pstate_until_aon_timer(pstate_bitset_t *pstate);
|
||||
void low_power_pstate_until_pin_state(pstate_bitset_t *pstate);
|
||||
#endif
|
||||
|
||||
// Or a function saying how did I boot?
|
||||
// Would like to make it easy to get back to main after going to sleep
|
||||
|
|
@ -103,8 +115,9 @@ void low_power_pstate_until_pin_state(pstate_bitset_t *pstate);
|
|||
// Doesn't support powering down switched core domain
|
||||
void low_power_pstate_set(pstate_bitset_t *pstate);
|
||||
pstate_bitset_t low_power_pstate_get(void);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#if 0 // todo - I think these are not being done?
|
||||
void sleep_run_from_dormant_source(dormant_clock_source_t dormant_source);
|
||||
/*! \brief Send system to sleep until a leading high edge is detected on GPIO
|
||||
* \ingroup pico_sleep
|
||||
|
|
@ -128,8 +141,6 @@ static inline void sleep_goto_dormant_until_level_high(uint gpio_pin) {
|
|||
sleep_goto_dormant_until_pin(gpio_pin, false, true);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*! \brief Reconfigure clocks to wake up properly from sleep/dormant mode
|
||||
* \ingroup pico_sleep
|
||||
*
|
||||
|
|
@ -138,6 +149,8 @@ static inline void sleep_goto_dormant_until_level_high(uint gpio_pin) {
|
|||
*/
|
||||
void sleep_power_up(void);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -59,7 +59,9 @@ static void prepare_for_clock_gating(void) {
|
|||
}
|
||||
|
||||
static void post_clock_gating(void) {
|
||||
|
||||
// restore all clocks in sleep mode, to prevent other __wfi from causing issues
|
||||
clock_dest_set_t all = clock_dest_set_all();
|
||||
clock_gate_sleep_en(&all);
|
||||
}
|
||||
|
||||
static void prepare_for_clock_switch(void) {
|
||||
|
|
@ -113,7 +115,7 @@ static void replace_null_enable_values(const clock_dest_set_t *keep_enabled,
|
|||
|
||||
// only the deep_sleep variant of this, as DORMANT cannot wake from TIMER
|
||||
int low_power_sleep_until_timer(timer_hw_t *timer, absolute_time_t until,
|
||||
const clock_dest_set_t *keep_enabled) {
|
||||
const clock_dest_set_t *keep_enabled, __unused bool exclusive) {
|
||||
int alarm_num = timer_hardware_alarm_claim_unused(timer, false);
|
||||
if (alarm_num < 0) return PICO_ERROR_INSUFFICIENT_RESOURCES;
|
||||
|
||||
|
|
@ -131,7 +133,7 @@ int low_power_sleep_until_timer(timer_hw_t *timer, absolute_time_t until,
|
|||
// we know that people in the wild (MicroPython) have wanted to do some mapping to also
|
||||
// figure out what PLLs are still on via these bits
|
||||
#if PICO_RP2040
|
||||
clock_dests_add(&local_keep_enabled, CLK_DEST_SYS_TIMER);
|
||||
clock_dest_set_add(&local_keep_enabled, CLK_DEST_SYS_TIMER);
|
||||
#elif PICO_RP2350
|
||||
clock_dest_set_add(&local_keep_enabled, timer_get_index(timer) ? CLK_DEST_SYS_TIMER1 : CLK_DEST_SYS_TIMER0);
|
||||
clock_dest_set_add(&local_keep_enabled, CLK_DEST_REF_TICKS);
|
||||
|
|
@ -155,6 +157,7 @@ int low_power_sleep_until_timer(timer_hw_t *timer, absolute_time_t until,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// todo note this has (not surprisingly a lot of commonality with the timer one)
|
||||
int low_power_sleep_until_aon_timer(absolute_time_t until,
|
||||
const clock_dest_set_t *keep_enabled) {
|
||||
|
|
@ -172,7 +175,7 @@ int low_power_sleep_until_aon_timer(absolute_time_t until,
|
|||
// fun, we probably want to have a "sparse" bitset macro encoded as 4 bytes or 8 bytes (for 4 or 7 indices
|
||||
// between 0 and 254) - i say 7, to leave encoding space for maybe indices > 256 in the 8 byte variant
|
||||
#if PICO_RP2040
|
||||
clock_dests_add(&local_keep_enabled, CLOCK_DEST_RTC_RTC);
|
||||
clock_dest_set_add(&local_keep_enabled, CLOCK_DEST_RTC_RTC);
|
||||
#elif PICO_RP2350
|
||||
clock_dest_set_add(&local_keep_enabled, CLK_DEST_REF_POWMAN);
|
||||
#else
|
||||
|
|
@ -198,7 +201,7 @@ int low_power_sleep_until_aon_timer(absolute_time_t until,
|
|||
post_clock_gating();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// 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
|
||||
|
|
@ -225,6 +228,7 @@ void low_power_setup_clocks_for_dormant(dormant_clock_source_t dormant_source) {
|
|||
#endif
|
||||
default:
|
||||
hard_assert(false);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
clock_configure_undivided(clk_ref,
|
||||
|
|
@ -314,7 +318,7 @@ int low_power_dormant_until_aon_timer(absolute_time_t until,
|
|||
#if PICO_RP2040
|
||||
// The RTC must be run from an external source, since the dormant source will be inactive
|
||||
rtc_run_from_external_source(src_hz, gpio_pin);
|
||||
clock_dests_add(&local_keep_enabled, CLOCK_DEST_RTC_RTC);
|
||||
clock_dest_set_add(&local_keep_enabled, CLK_DEST_RTC_RTC);
|
||||
#elif PICO_RP2350
|
||||
// todo
|
||||
((void)gpio_pin);
|
||||
|
|
@ -393,3 +397,24 @@ void low_power_dormant_until_pin_state(uint gpio_pin, bool edge, bool high,
|
|||
low_power_wake_from_dormant();
|
||||
}
|
||||
|
||||
#if !PICO_RUNTIME_NO_INIT_RP2350_SLEEP_FIX
|
||||
#include "hardware/sync.h"
|
||||
void __weak runtime_init_rp2350_sleep_fix(void) {
|
||||
if (watchdog_hw->reason && WATCHDOG_REASON_TIMER_BITS) { // detect rom_reboot() usage
|
||||
int alarm_num = timer_hardware_alarm_claim_unused(timer_hw, false);
|
||||
if (alarm_num < 0) return;
|
||||
|
||||
timer_hardware_alarm_set_callback(timer_hw, alarm_num, ((hardware_alarm_callback_t )low_power_wakeup));
|
||||
timer_hardware_alarm_set_target(timer_hw, alarm_num, make_timeout_time_us(100));
|
||||
|
||||
__wfi();
|
||||
|
||||
timer_hardware_alarm_set_callback(timer_hw, alarm_num, NULL);
|
||||
timer_hardware_alarm_unclaim(timer_hw, alarm_num);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !PICO_RUNTIME_SKIP_INIT_RP2350_SLEEP_FIX
|
||||
PICO_RUNTIME_INIT_FUNC_RUNTIME(runtime_init_rp2350_sleep_fix, PICO_RUNTIME_INIT_RP2350_SLEEP_FIX);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -416,6 +416,22 @@ void runtime_init_bootrom_locking_enable(void);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// RP2350 sleep fix
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#ifndef PICO_RUNTIME_INIT_RP2350_SLEEP_FIX
|
||||
#define PICO_RUNTIME_INIT_RP2350_SLEEP_FIX "11010"
|
||||
#endif
|
||||
|
||||
#ifndef PICO_RUNTIME_SKIP_INIT_RP2350_SLEEP_FIX
|
||||
#define PICO_RUNTIME_SKIP_INIT_RP2350_SLEEP_FIX !PICO_RP2350
|
||||
#endif
|
||||
|
||||
#ifndef PICO_RUNTIME_NO_INIT_RP2350_SLEEP_FIX
|
||||
#define PICO_RUNTIME_NO_INIT_RP2350_SLEEP_FIX !PICO_RP2350
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// stack guard; these are a special case as they take a parameter; however the normal defines apply
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -26,12 +26,19 @@ int main() {
|
|||
printf("Waiting 1 sec\n"); // so we can see some repeat printfs
|
||||
busy_wait_ms(1100);
|
||||
#endif
|
||||
|
||||
absolute_time_t start_time;
|
||||
absolute_time_t wakeup_time;
|
||||
int64_t diff;
|
||||
uint64_t current_aon;
|
||||
struct timespec ts;
|
||||
|
||||
printf("Going to sleep for 5 seconds via TIMER\n");
|
||||
|
||||
absolute_time_t start_time = get_absolute_time();
|
||||
absolute_time_t wakeup_time = delayed_by_ms(start_time, 5000);
|
||||
low_power_sleep_until_timer(timer_hw, wakeup_time, NULL);
|
||||
int64_t diff = absolute_time_diff_us(wakeup_time, get_absolute_time());
|
||||
start_time = get_absolute_time();
|
||||
wakeup_time = delayed_by_ms(start_time, 5000);
|
||||
low_power_sleep_until_timer(timer_hw, wakeup_time, NULL, true);
|
||||
diff = absolute_time_diff_us(wakeup_time, get_absolute_time());
|
||||
printf("Woken up now @%dus since target\n", (int)diff);
|
||||
if (diff < 0) {
|
||||
printf("ERROR: Woke up too soon\n");
|
||||
|
|
@ -39,33 +46,7 @@ int main() {
|
|||
}
|
||||
busy_wait_ms(3000);
|
||||
|
||||
printf("Going to sleep for 5 seconds via AON TIMER\n");
|
||||
|
||||
// todo, ah; we should start the aon timer; still have to decide what to do about keeping them in sync
|
||||
start_time = get_absolute_time();
|
||||
struct timespec ts;
|
||||
us_to_timespec(start_time, &ts);
|
||||
aon_timer_start(&ts);
|
||||
|
||||
wakeup_time = delayed_by_ms(start_time, 5000);
|
||||
low_power_sleep_until_aon_timer(wakeup_time, NULL);
|
||||
diff = absolute_time_diff_us(get_absolute_time(), wakeup_time);
|
||||
// need to use the AON timer for checking time, since the other timer is unclocked
|
||||
diff = absolute_time_diff_us(wakeup_time, get_absolute_time());
|
||||
if (diff > -4000000) {
|
||||
printf("ERROR: doesn't seem like timer was stopped\n");
|
||||
return - 1;
|
||||
}
|
||||
aon_timer_get_time(&ts);
|
||||
uint64_t current_aon = timespec_to_us(&ts);
|
||||
diff = absolute_time_diff_us(wakeup_time, from_us_since_boot(current_aon));
|
||||
printf("Woken up now @%dus since target\n", (int)diff);
|
||||
if (diff < 0) {
|
||||
printf("WARNING: Woke up too soon - is this within the resolution of the aon timer?\n");
|
||||
}
|
||||
printf("5 second pause to prove timer still running\n");
|
||||
busy_wait_ms(5000);
|
||||
|
||||
#if !PICO_RP2040
|
||||
printf("Going DORMANT for 5 seconds via AON TIMER\n");
|
||||
|
||||
// todo, ah; we should start the aon timer; still have to decide what to do about keeping them in sync
|
||||
|
|
@ -77,7 +58,6 @@ int main() {
|
|||
low_power_dormant_until_aon_timer(wakeup_time, DORMANT_CLOCK_SOURCE_LPOSC, XOSC_KHZ * 1000,
|
||||
0, // gpio pin (unused with powman)
|
||||
NULL);
|
||||
low_power_sleep_until_aon_timer(wakeup_time, NULL);
|
||||
diff = absolute_time_diff_us(get_absolute_time(), wakeup_time);
|
||||
// need to use the AON timer for checking time, since the other timer is unclocked
|
||||
diff = absolute_time_diff_us(wakeup_time, get_absolute_time());
|
||||
|
|
@ -94,6 +74,7 @@ int main() {
|
|||
}
|
||||
printf("Final 5 second pause to prove timer still running\n");
|
||||
busy_wait_ms(5000);
|
||||
#endif
|
||||
|
||||
printf("SUCCESS\n");
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue