Merge branch 'master' into master

This commit is contained in:
Thomas Göttgens 2024-10-08 12:33:34 +02:00 committed by GitHub
commit 5db4918413
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 479 additions and 9 deletions

View File

@ -1,14 +1,16 @@
; Common settings for rp2040 Processor based targets
[rp2040_base]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#60d6ae81fcc73c34b1493ca9e261695e471bc0c2
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#v1.2.0-gcc12
extends = arduino_base
platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.7.2
platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#4.0.3
board_build.core = earlephilhower
board_build.filesystem_size = 0.5m
build_flags =
${arduino_base.build_flags} -Wno-unused-variable -Wcast-align
-Isrc/platform/rp2xx0
-Isrc/platform/rp2xx0/hardware_rosc/include
-Isrc/platform/rp2xx0/pico_sleep/include
-D__PLAT_RP2040__
# -D _POSIX_THREADS
build_src_filter =

View File

@ -588,7 +588,7 @@ void Power::shutdown()
{
LOG_INFO("Shutting down\n");
#if defined(ARCH_NRF52) || defined(ARCH_ESP32)
#if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040)
#ifdef PIN_LED1
ledOff(PIN_LED1);
#endif

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _HARDWARE_ROSC_H_
#define _HARDWARE_ROSC_H_
#include "pico.h"
#include "hardware/structs/rosc.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, youll 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());
};
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico.h"
// For MHZ definitions etc
#include "hardware/clocks.h"
#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));
}

View File

@ -4,20 +4,67 @@
#include <hardware/pll.h>
#include <pico/stdlib.h>
#include <pico/unique_id.h>
#include <stdio.h>
#include <pico/sleep.h>
void setBluetoothEnable(bool enable)
{
// not needed
}
static bool awake;
static void sleep_callback(void) {
awake = true;
}
void epoch_to_datetime(time_t epoch, datetime_t *dt) {
struct tm *tm_info;
tm_info = gmtime(&epoch);
dt->year = tm_info->tm_year;
dt->month = tm_info->tm_mon + 1;
dt->day = tm_info->tm_mday;
dt->dotw = tm_info->tm_wday;
dt->hour = tm_info->tm_hour;
dt->min = tm_info->tm_min;
dt->sec = tm_info->tm_sec;
}
void debug_date(datetime_t t)
{
LOG_DEBUG("%d %d %d %d %d %d %d\n", t.year, t.month, t.day, t.hour, t.min, t.sec, t.dotw);
uart_default_tx_wait_blocking();
}
void cpuDeepSleep(uint32_t msecs)
{
/* Disable both PLL to avoid power dissipation */
pll_deinit(pll_sys);
pll_deinit(pll_usb);
time_t seconds = (time_t)(msecs/1000);
datetime_t t_init, t_alarm;
awake = false;
// Start the RTC
rtc_init();
epoch_to_datetime(0, &t_init);
rtc_set_datetime(&t_init);
epoch_to_datetime(seconds, &t_alarm);
// debug_date(t_init);
// debug_date(t_alarm);
uart_default_tx_wait_blocking();
sleep_run_from_dormant_source(DORMANT_SOURCE_ROSC);
sleep_goto_sleep_until(&t_alarm, &sleep_callback);
// Make sure we don't wake
while (!awake) {
delay(1);
}
/* For now, I don't know how to revert this state
We just reboot in order to get back operational */
rp2040.reboot();
/* Set RP2040 in dormant mode. Will not wake up. */
xosc_dormant();
// xosc_dormant();
}
void updateBatteryLevel(uint8_t level)

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_SLEEP_H_
#define _PICO_SLEEP_H_
#include "pico.h"
#include "hardware/rtc.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_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);
}
/*! \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 t The time to wake up
* \param callback Function to call on wakeup.
*/
void sleep_goto_sleep_until(datetime_t *t, rtc_callback_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);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,159 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico.h"
#include "pico/stdlib.h"
#include "pico/sleep.h"
#include "hardware/rtc.h"
#include "hardware/pll.h"
#include "hardware/clocks.h"
#include "hardware/xosc.h"
#include "hardware/rosc.h"
#include "hardware/regs/io_bank0.h"
// For __wfi
#include "hardware/sync.h"
// For scb_hw so we can enable deep sleep
#include "hardware/structs/scb.h"
// when using old SDK this macro is not defined
#ifndef XOSC_HZ
#define XOSC_HZ 12000000u
#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.
// 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);
}
// 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;
// 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;
// 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 USB = 0MHz
clock_stop(clk_usb);
// CLK ADC = 0MHz
clock_stop(clk_adc);
// 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);
// 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
/* This dones not work with our current core */
//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));
// Turn off all clocks when in sleep mode except for RTC
clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS;
clocks_hw->sleep_en1 = 0x0;
rtc_set_alarm(t, callback);
uint save = scb_hw->scr;
// Enable deep sleep at the proc
scb_hw->scr = save | M0PLUS_SCR_SLEEPDEEP_BITS;
// Go to sleep
__wfi();
}
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_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_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);
}

View File

@ -44,7 +44,7 @@ void powerCommandsCheck()
if (shutdownAtMsec && millis() > shutdownAtMsec) {
LOG_INFO("Shutting down from admin command\n");
#if defined(ARCH_NRF52) || defined(ARCH_ESP32)
#if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040)
playShutdownMelody();
power->shutdown();
#elif defined(ARCH_PORTDUINO)

View File

@ -2,6 +2,8 @@
extends = rp2040_base
board = wiscore_rak11300
upload_protocol = picotool
# keep an old SDK to use less memory.
platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.7.2
# add our variants files to the include and src paths
build_flags = ${rp2040_base.build_flags}

View File

@ -6,6 +6,7 @@
#define LED_CONN PIN_LED2
#define LED_PIN LED_BUILTIN
#define ledOff(pin) pinMode(pin, INPUT)
#define BUTTON_PIN 9
#define BUTTON_NEED_PULLUP

View File

@ -23,6 +23,7 @@
// ratio of voltage divider = 3.0 (R17=200k, R18=100k)
// #define ADC_MULTIPLIER 3.1 // 3.0 + a bit for being optimistic
#define HAS_CPU_SHUTDOWN 1
#define USE_SX1262
#undef LORA_SCK