kopia lustrzana https://github.com/raspberrypi/pico-extras
Merge branch 'master' into develop
commit
b054c993ab
|
@ -83,6 +83,8 @@ inline static void rosc_write(io_rw_32 *addr, uint32_t value) {
|
|||
assert(rosc_write_okay());
|
||||
};
|
||||
|
||||
void rosc_enable(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -58,4 +58,12 @@ void rosc_set_dormant(void) {
|
|||
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));
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
pico_simple_hardware_target(sleep)
|
||||
target_link_libraries(hardware_sleep INTERFACE
|
||||
pico_mirrored_target_link_libraries(hardware_sleep INTERFACE
|
||||
hardware_clocks
|
||||
hardware_rosc
|
||||
hardware_rtc)
|
||||
hardware_irq
|
||||
pico_aon_timer
|
||||
)
|
||||
target_include_directories(hardware_sleep INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/include
|
||||
)
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
#define _PICO_SLEEP_H_
|
||||
|
||||
#include "pico.h"
|
||||
#include "hardware/rtc.h"
|
||||
#include "hardware/rosc.h"
|
||||
|
||||
#include "pico/aon_timer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -33,7 +35,8 @@ extern "C" {
|
|||
typedef enum {
|
||||
DORMANT_SOURCE_NONE,
|
||||
DORMANT_SOURCE_XOSC,
|
||||
DORMANT_SOURCE_ROSC
|
||||
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.
|
||||
|
@ -50,6 +53,12 @@ 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
|
||||
*/
|
||||
|
@ -62,10 +71,32 @@ static inline void sleep_run_from_rosc(void) {
|
|||
*
|
||||
* One of the sleep_run_* functions must be called prior to this call
|
||||
*
|
||||
* \param t The time to wake up
|
||||
* \param ts The time to wake up
|
||||
* \param callback Function to call on wakeup.
|
||||
*/
|
||||
void sleep_goto_sleep_until(datetime_t *t, rtc_callback_t callback);
|
||||
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
|
||||
|
@ -76,6 +107,7 @@ void sleep_goto_sleep_until(datetime_t *t, rtc_callback_t callback);
|
|||
* \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
|
||||
|
@ -100,8 +132,16 @@ 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
|
||||
#endif
|
||||
|
|
|
@ -4,21 +4,35 @@
|
|||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "pico.h"
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/sleep.h"
|
||||
|
||||
#include "hardware/rtc.h"
|
||||
#include "hardware/pll.h"
|
||||
#include "hardware/regs/clocks.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/watchdog.h"
|
||||
#include "hardware/xosc.h"
|
||||
#include "hardware/rosc.h"
|
||||
#include "hardware/regs/io_bank0.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.
|
||||
|
@ -26,13 +40,22 @@
|
|||
// 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.
|
||||
|
||||
|
||||
// TODO: Optionally, memories can also be powered down.
|
||||
|
||||
static dormant_source_t _dormant_source;
|
||||
|
||||
bool dormant_source_valid(dormant_source_t dormant_source) {
|
||||
return (dormant_source == DORMANT_SOURCE_XOSC) || (dormant_source == DORMANT_SOURCE_ROSC);
|
||||
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:
|
||||
|
@ -42,11 +65,26 @@ void sleep_run_from_dormant_source(dormant_source_t dormant_source) {
|
|||
assert(dormant_source_valid(dormant_source));
|
||||
_dormant_source = dormant_source;
|
||||
|
||||
// FIXME: Just defining average rosc freq here.
|
||||
uint src_hz = (dormant_source == DORMANT_SOURCE_XOSC) ? XOSC_HZ : 6.5 * MHZ;
|
||||
uint clk_ref_src = (dormant_source == DORMANT_SOURCE_XOSC) ?
|
||||
CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC :
|
||||
CLOCKS_CLK_REF_CTRL_SRC_VALUE_ROSC_CLKSRC_PH;
|
||||
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,
|
||||
|
@ -62,15 +100,17 @@ void sleep_run_from_dormant_source(dormant_source_t dormant_source) {
|
|||
src_hz,
|
||||
src_hz);
|
||||
|
||||
// CLK USB = 0MHz
|
||||
clock_stop(clk_usb);
|
||||
|
||||
// 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 :
|
||||
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,
|
||||
|
@ -78,6 +118,7 @@ void sleep_run_from_dormant_source(dormant_source_t dormant_source) {
|
|||
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,
|
||||
|
@ -102,25 +143,80 @@ void sleep_run_from_dormant_source(dormant_source_t dormant_source) {
|
|||
setup_default_uart();
|
||||
}
|
||||
|
||||
// Go to sleep until woken up by the RTC
|
||||
void sleep_goto_sleep_until(datetime_t *t, rtc_callback_t callback) {
|
||||
// We should have already called the sleep_run_from_dormant_source function
|
||||
assert(dormant_source_valid(_dormant_source));
|
||||
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
|
||||
}
|
||||
|
||||
// Turn off all clocks when in sleep mode except for RTC
|
||||
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
|
||||
|
||||
rtc_set_alarm(t, callback);
|
||||
aon_timer_enable_alarm(ts, callback, false);
|
||||
|
||||
stdio_flush();
|
||||
|
||||
uint save = scb_hw->scr;
|
||||
// Enable deep sleep at the proc
|
||||
scb_hw->scr = save | M0PLUS_SCR_SLEEPDEEP_BITS;
|
||||
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));
|
||||
|
||||
|
@ -131,6 +227,34 @@ static void _go_dormant(void) {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -145,6 +269,8 @@ void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high) {
|
|||
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();
|
||||
|
@ -152,4 +278,29 @@ void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high) {
|
|||
|
||||
// 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();
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue