LiPo: Add powman and wakeup modules.
These are useful for dealing with low power situations, though powman's time keeping leaves a little to be desired.
This commit is contained in:
parent
9ec3001410
commit
da4cbd989f
@ -1,3 +1,10 @@
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../")
|
||||
|
||||
# Wakeup module for early GPIO latch
|
||||
include(modules/c/wakeup/micropython)
|
||||
|
||||
# Powman example for low power sleep
|
||||
include(modules/c/powman/micropython)
|
||||
|
||||
include(usermod-common)
|
||||
@ -1,3 +1,10 @@
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../")
|
||||
|
||||
# Wakeup module for early GPIO latch
|
||||
include(modules/c/wakeup/micropython)
|
||||
|
||||
# Powman example for low power sleep
|
||||
include(modules/c/powman/micropython)
|
||||
|
||||
include(usermod-common)
|
||||
150
modules/c/powman/bindings.c
Normal file
150
modules/c/powman/bindings.c
Normal file
@ -0,0 +1,150 @@
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/util/datetime.h"
|
||||
#include "powman.h"
|
||||
|
||||
#include "mphalport.h"
|
||||
#include "py/runtime.h"
|
||||
#include "shared/timeutils/timeutils.h"
|
||||
|
||||
#define GPIO_I2C_POWER 2
|
||||
#define GPIO_WAKE 3
|
||||
#define GPIO_EXT_CLK 12
|
||||
#define GPIO_LED_A 10
|
||||
|
||||
enum {
|
||||
WAKE_BUTTON_A = 0x00,
|
||||
WAKE_BUTTON_B,
|
||||
WAKE_BUTTON_C,
|
||||
WAKE_TIMER = 0xf0,
|
||||
WAKE_UNKNOWN = 0xff,
|
||||
};
|
||||
|
||||
mp_obj_t _sleep_get_wake_reason(void) {
|
||||
uint8_t wake_reason = powman_get_wake_reason();
|
||||
if(wake_reason & POWMAN_WAKE_ALARM) {
|
||||
return MP_ROM_INT(WAKE_TIMER);
|
||||
}
|
||||
if(wake_reason & POWMAN_WAKE_PWRUP0) return MP_ROM_INT(WAKE_BUTTON_A);
|
||||
if(wake_reason & POWMAN_WAKE_PWRUP1) return MP_ROM_INT(WAKE_BUTTON_B);
|
||||
if(wake_reason & POWMAN_WAKE_PWRUP2) return MP_ROM_INT(WAKE_BUTTON_C);
|
||||
return MP_ROM_INT(WAKE_UNKNOWN);
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(_sleep_get_wake_reason_obj, _sleep_get_wake_reason);
|
||||
|
||||
/*! \brief Send system to sleep until the specified GPIO changes
|
||||
*
|
||||
* \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
|
||||
* \param timeout wakeup after timeout milliseconds if no edge occurs
|
||||
*/
|
||||
mp_obj_t _sleep_goto_dormant_until_pin(size_t n_args, const mp_obj_t *args) {
|
||||
enum { ARG_pin, ARG_edge, ARG_high, ARG_timeout };
|
||||
|
||||
uint pin = UINT16_MAX;
|
||||
if(args[ARG_pin] != mp_const_none) {
|
||||
pin = mp_hal_get_pin_obj(args[ARG_pin]);
|
||||
}
|
||||
bool edge = mp_obj_is_true(args[ARG_edge]);
|
||||
bool high = mp_obj_is_true(args[ARG_high]);
|
||||
uint64_t timeout_ms = 0;
|
||||
|
||||
if (n_args == 4) {
|
||||
timeout_ms = (uint64_t)mp_obj_get_float(args[ARG_timeout]) * 1000;
|
||||
}
|
||||
|
||||
powman_init();
|
||||
|
||||
if (pin != UINT16_MAX) {
|
||||
powman_setup_gpio_wakeup(POWMAN_WAKE_PWRUP0_CH, pin, edge, high, 1000);
|
||||
} else {
|
||||
int err = 0;
|
||||
err = powman_setup_gpio_wakeup(POWMAN_WAKE_PWRUP0_CH, 12, edge, high, 1000); // Tufty Button A
|
||||
if (err == -1) {mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Timeout waiting for Button A"));}
|
||||
err = powman_setup_gpio_wakeup(POWMAN_WAKE_PWRUP1_CH, 13, edge, high, 1000); // Tufty Button B
|
||||
if (err == -1) {mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Timeout waiting for Button B"));}
|
||||
err = powman_setup_gpio_wakeup(POWMAN_WAKE_PWRUP2_CH, 14, edge, high, 1000); // Tufty Button C
|
||||
if (err == -1) {mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Timeout waiting for Button C"));}
|
||||
}
|
||||
|
||||
// power off
|
||||
int rc = 0;
|
||||
if (timeout_ms > 0) {
|
||||
absolute_time_t timeout = make_timeout_time_ms(timeout_ms);
|
||||
rc = powman_off_until_time(timeout);
|
||||
} else {
|
||||
rc = powman_off();
|
||||
}
|
||||
hard_assert(rc == PICO_OK);
|
||||
hard_assert(false); // should never get here!
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(_sleep_goto_dormant_until_pin_obj, 3, 4, _sleep_goto_dormant_until_pin);
|
||||
|
||||
/*! \brief Send system to dormant until the specified time, note for RP2040 the RTC must be driven by an external clock
|
||||
*
|
||||
* \param ts The time to wake up
|
||||
* \param callback Function to call on wakeup.
|
||||
*/
|
||||
mp_obj_t _sleep_goto_dormant_until(mp_obj_t absolute_time_in) {
|
||||
// Borrowed from https://github.com/micropython/micropython/blob/master/ports/rp2/machine_rtc.c#L83C1-L96
|
||||
mp_obj_t *items;
|
||||
mp_obj_get_array_fixed_n(absolute_time_in, 8, &items);
|
||||
timeutils_struct_time_t tm = {
|
||||
.tm_year = mp_obj_get_int(items[0]),
|
||||
.tm_mon = mp_obj_get_int(items[1]),
|
||||
.tm_mday = mp_obj_get_int(items[2]),
|
||||
.tm_hour = mp_obj_get_int(items[4]),
|
||||
.tm_min = mp_obj_get_int(items[5]),
|
||||
.tm_sec = mp_obj_get_int(items[6]),
|
||||
};
|
||||
struct timespec ts = { 0, 0 };
|
||||
ts.tv_sec = timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||
|
||||
int rc = powman_off_until_time(timespec_to_ms(&ts));
|
||||
hard_assert(rc == PICO_OK);
|
||||
hard_assert(false); // should never get here!
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(_sleep_goto_dormant_until_obj, _sleep_goto_dormant_until);
|
||||
|
||||
/*! \brief Send system to dormant until the specified time, note for RP2040 the RTC must be driven by an external clock
|
||||
*
|
||||
* \param ts The time to wake up
|
||||
* \param callback Function to call on wakeup.
|
||||
*/
|
||||
mp_obj_t _sleep_goto_dormant_for(mp_obj_t time_seconds_in) {
|
||||
uint64_t ms = (uint64_t)(mp_obj_get_float(time_seconds_in) * 1000);
|
||||
int rc = powman_off_for_ms(ms);
|
||||
hard_assert(rc == PICO_OK);
|
||||
hard_assert(false); // should never get here!
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(_sleep_goto_dormant_for_obj, _sleep_goto_dormant_for);
|
||||
|
||||
static const mp_map_elem_t sleep_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_powman) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_goto_dormant_until_pin), MP_ROM_PTR(&_sleep_goto_dormant_until_pin_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_goto_dormant_until), MP_ROM_PTR(&_sleep_goto_dormant_until_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_goto_dormant_for), MP_ROM_PTR(&_sleep_goto_dormant_for_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_get_wake_reason), MP_ROM_PTR(&_sleep_get_wake_reason_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_WAKE_BUTTON_A), MP_ROM_INT(WAKE_BUTTON_A) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_WAKE_BUTTON_B), MP_ROM_INT(WAKE_BUTTON_B) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_WAKE_BUTTON_C), MP_ROM_INT(WAKE_BUTTON_C) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_WAKE_TIMER), MP_ROM_INT(WAKE_TIMER) }, // TODO: Rename to ALARM?
|
||||
{ MP_ROM_QSTR(MP_QSTR_WAKE_UNKNOWN), MP_ROM_INT(WAKE_UNKNOWN) },
|
||||
};
|
||||
static MP_DEFINE_CONST_DICT(mp_module_sleep_globals, sleep_globals_table);
|
||||
|
||||
const mp_obj_module_t sleep_user_cmodule = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_sleep_globals,
|
||||
};
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_powman, sleep_user_cmodule);
|
||||
27
modules/c/powman/micropython.cmake
Normal file
27
modules/c/powman/micropython.cmake
Normal file
@ -0,0 +1,27 @@
|
||||
add_library(usermod_sleep INTERFACE)
|
||||
|
||||
target_sources(usermod_sleep INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/bindings.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/powman.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/rosc.c
|
||||
)
|
||||
|
||||
target_include_directories(usermod_sleep INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(usermod_sleep INTERFACE hardware_powman hardware_gpio)
|
||||
|
||||
target_link_libraries(usermod INTERFACE usermod_sleep)
|
||||
|
||||
#set_source_files_properties(
|
||||
# ${CMAKE_CURRENT_LIST_DIR}/sleep.c
|
||||
# PROPERTIES COMPILE_FLAGS
|
||||
# "-Wno-maybe-uninitialized"
|
||||
#)
|
||||
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_LIST_DIR}/bindings.c
|
||||
PROPERTIES COMPILE_FLAGS
|
||||
"-Wno-discarded-qualifiers"
|
||||
)
|
||||
150
modules/c/powman/powman.c
Normal file
150
modules/c/powman/powman.c
Normal file
@ -0,0 +1,150 @@
|
||||
|
||||
#include "powman.h"
|
||||
|
||||
static powman_power_state off_state;
|
||||
static powman_power_state on_state;
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
uint8_t powman_get_wake_reason(void) {
|
||||
// 0 = chip reset, for the source of the last reset see POWMAN_CHIP_RESET
|
||||
// 1 = pwrup0 (GPIO interrupt 0)
|
||||
// 2 = pwrup1 (GPIO interrupt 1)
|
||||
// 3 = pwrup2 (GPIO interrupt 2)
|
||||
// 4 = pwrup3 (GPIO interrupt 3)
|
||||
// 5 = coresight_pwrup
|
||||
// 6 = alarm_pwrup (timeout or alarm wakeup)
|
||||
return powman_hw->last_swcore_pwrup & 0x7f;
|
||||
}
|
||||
|
||||
void powman_init() {
|
||||
uint64_t abs_time_ms = 1746057600000; // 2025/05/01 - Milliseconds since epoch
|
||||
|
||||
// Run everything from pll_usb pll and stop pll_sys
|
||||
set_sys_clock_48mhz();
|
||||
|
||||
// Use the 32768 Hz clockout from the RTC to keep time accurately
|
||||
//clock_configure_gpin(clk_ref, 12, 32.768f * KHZ, 32.768f * KHZ);
|
||||
//clock_configure_gpin(clk_sys, 12, 32.768f * KHZ, 32.768f * KHZ);
|
||||
//clock_configure_undivided(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, 32.768f * KHZ);
|
||||
//powman_timer_set_1khz_tick_source_lposc_with_hz(32768);
|
||||
|
||||
powman_timer_set_1khz_tick_source_lposc();
|
||||
pll_deinit(pll_sys);
|
||||
|
||||
// Set all pins to input (as far as SIO is concerned)
|
||||
gpio_set_dir_all_bits(0);
|
||||
for (int i = 0; i < NUM_BANK0_GPIOS; ++i) {
|
||||
gpio_set_function(i, GPIO_FUNC_SIO);
|
||||
if (i > NUM_BANK0_GPIOS - NUM_ADC_CHANNELS) {
|
||||
gpio_disable_pulls(i);
|
||||
gpio_set_input_enabled(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock the VREG control interface
|
||||
hw_set_bits(&powman_hw->vreg_ctrl, POWMAN_PASSWORD_BITS | POWMAN_VREG_CTRL_UNLOCK_BITS);
|
||||
|
||||
// Turn off USB PHY and apply pull downs on DP & DM
|
||||
usb_hw->phy_direct = USB_USBPHY_DIRECT_TX_PD_BITS | USB_USBPHY_DIRECT_RX_PD_BITS | USB_USBPHY_DIRECT_DM_PULLDN_EN_BITS | USB_USBPHY_DIRECT_DP_PULLDN_EN_BITS;
|
||||
|
||||
usb_hw->phy_direct_override = USB_USBPHY_DIRECT_RX_DM_BITS | USB_USBPHY_DIRECT_RX_DP_BITS | USB_USBPHY_DIRECT_RX_DD_BITS |
|
||||
USB_USBPHY_DIRECT_OVERRIDE_TX_DIFFMODE_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_DM_PULLUP_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_TX_FSSLEW_OVERRIDE_EN_BITS |
|
||||
USB_USBPHY_DIRECT_OVERRIDE_TX_PD_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_RX_PD_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_TX_DM_OVERRIDE_EN_BITS |
|
||||
USB_USBPHY_DIRECT_OVERRIDE_TX_DP_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_TX_DM_OE_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_TX_DP_OE_OVERRIDE_EN_BITS |
|
||||
USB_USBPHY_DIRECT_OVERRIDE_DM_PULLDN_EN_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_DP_PULLDN_EN_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_DP_PULLUP_EN_OVERRIDE_EN_BITS |
|
||||
USB_USBPHY_DIRECT_OVERRIDE_DM_PULLUP_HISEL_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_DP_PULLUP_HISEL_OVERRIDE_EN_BITS;
|
||||
|
||||
// start powman and set the time
|
||||
powman_timer_start();
|
||||
powman_timer_set_ms(abs_time_ms);
|
||||
|
||||
// Allow power down when debugger connected
|
||||
powman_set_debug_power_request_ignored(true);
|
||||
|
||||
// Power states
|
||||
powman_power_state P1_7 = POWMAN_POWER_STATE_NONE;
|
||||
|
||||
powman_power_state P0_3 = POWMAN_POWER_STATE_NONE;
|
||||
P0_3 = powman_power_state_with_domain_on(P0_3, POWMAN_POWER_DOMAIN_SWITCHED_CORE);
|
||||
P0_3 = powman_power_state_with_domain_on(P0_3, POWMAN_POWER_DOMAIN_XIP_CACHE);
|
||||
|
||||
off_state = P1_7;
|
||||
on_state = P0_3;
|
||||
}
|
||||
|
||||
// Initiate power off
|
||||
int __no_inline_not_in_flash_func(powman_off)(void) {
|
||||
// Set power states
|
||||
bool valid_state = powman_configure_wakeup_state(off_state, on_state);
|
||||
if (!valid_state) {
|
||||
return PICO_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// reboot to main
|
||||
powman_hw->boot[0] = 0;
|
||||
powman_hw->boot[1] = 0;
|
||||
powman_hw->boot[2] = 0;
|
||||
powman_hw->boot[3] = 0;
|
||||
|
||||
// Switch to required power state
|
||||
int rc = powman_set_power_state(off_state);
|
||||
if (rc != PICO_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Power down
|
||||
while (true) __wfi();
|
||||
}
|
||||
|
||||
int powman_setup_gpio_wakeup(int hw_wakeup, int gpio, bool edge, bool high, uint64_t timeout_ms) {
|
||||
gpio_init(gpio);
|
||||
gpio_set_dir(gpio, false);
|
||||
gpio_set_input_enabled(gpio, true);
|
||||
|
||||
// Must set pulls here, or our pin may never go into its idle state
|
||||
gpio_set_pulls(gpio, !high, high);
|
||||
|
||||
// If the pin is currently in a triggered state, wait for idle
|
||||
absolute_time_t timeout = make_timeout_time_ms(timeout_ms);
|
||||
if (gpio_get(gpio) == high) {
|
||||
while(gpio_get(gpio) == high) {
|
||||
sleep_ms(10);
|
||||
if(time_reached(timeout)) return -1;
|
||||
}
|
||||
}
|
||||
powman_enable_gpio_wakeup(hw_wakeup, gpio, edge, high);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Power off until a gpio goes high
|
||||
int powman_off_until_gpio_high(int gpio, bool edge, bool high, uint64_t timeout_ms) {
|
||||
powman_init();
|
||||
|
||||
powman_setup_gpio_wakeup(POWMAN_WAKE_PWRUP0_CH, gpio, edge, high, 1000);
|
||||
|
||||
if (timeout_ms > 0) {
|
||||
uint64_t ms = powman_timer_get_ms();
|
||||
return powman_off_until_time(ms + timeout_ms);
|
||||
} else {
|
||||
return powman_off();
|
||||
}
|
||||
}
|
||||
|
||||
// Power off until an absolute time
|
||||
int powman_off_until_time(uint64_t absolute_time_ms) {
|
||||
powman_init();
|
||||
|
||||
// Start powman timer and turn off
|
||||
powman_enable_alarm_wakeup_at_ms(absolute_time_ms);
|
||||
return powman_off();
|
||||
}
|
||||
|
||||
// Power off for a number of milliseconds
|
||||
int powman_off_for_ms(uint64_t duration_ms) {
|
||||
powman_init();
|
||||
|
||||
uint64_t ms = powman_timer_get_ms();
|
||||
return powman_off_until_time(ms + duration_ms);
|
||||
}
|
||||
37
modules/c/powman/powman.h
Normal file
37
modules/c/powman/powman.h
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "pico/stdio.h"
|
||||
#include "pico/sync.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/powman.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/pll.h"
|
||||
#include "hardware/adc.h"
|
||||
#include "hardware/structs/usb.h"
|
||||
#include "hardware/structs/xosc.h"
|
||||
#include "hardware/vreg.h"
|
||||
#include "hardware/flash.h"
|
||||
#include "hardware/structs/qmi.h"
|
||||
|
||||
#define POWMAN_WAKE_PWRUP0_CH 0
|
||||
#define POWMAN_WAKE_PWRUP1_CH 1
|
||||
#define POWMAN_WAKE_PWRUP2_CH 2
|
||||
|
||||
#define POWMAN_WAKE_RESET 0b00000001
|
||||
#define POWMAN_WAKE_PWRUP0 0b00000010
|
||||
#define POWMAN_WAKE_PWRUP1 0b00000100
|
||||
#define POWMAN_WAKE_PWRUP2 0b00001000
|
||||
#define POWMAN_WAKE_PWRUP3 0b00010000
|
||||
#define POWMAN_WAKE_CORESI 0b00100000
|
||||
#define POWMAN_WAKE_ALARM 0b01000000
|
||||
|
||||
int powman_off_until_gpio_high(int gpio, bool edge, bool high, uint64_t timeout_ms);
|
||||
int powman_off_until_time(uint64_t absolute_time_ms);
|
||||
int powman_off_for_ms(uint64_t duration_ms);
|
||||
uint8_t powman_get_wake_reason(void);
|
||||
|
||||
void powman_init();
|
||||
int powman_setup_gpio_wakeup(int hw_wakeup, int gpio, bool edge, bool high, uint64_t timeout_ms);
|
||||
int powman_off(void);
|
||||
71
modules/c/powman/rosc.c
Normal file
71
modules/c/powman/rosc.c
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// Vendored from: https://github.com/raspberrypi/pico-extras/blob/4ccfef6fe068bbae8106d0d1f072c7f997472040/src/rp2_common/hardware_rosc/rosc.c
|
||||
|
||||
#include "pico.h"
|
||||
|
||||
// For MHZ definitions etc
|
||||
#include "hardware/clocks.h"
|
||||
#include "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));
|
||||
}
|
||||
94
modules/c/powman/rosc.h
Normal file
94
modules/c/powman/rosc.h
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// Vendored from: https://github.com/raspberrypi/pico-extras/blob/4ccfef6fe068bbae8106d0d1f072c7f997472040/src/rp2_common/hardware_rosc/include/hardware/rosc.h
|
||||
|
||||
#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, 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
|
||||
18
modules/c/wakeup/micropython.cmake
Normal file
18
modules/c/wakeup/micropython.cmake
Normal file
@ -0,0 +1,18 @@
|
||||
add_library(usermod_wakeup INTERFACE)
|
||||
|
||||
target_sources(usermod_wakeup INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/wakeup.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/wakeup.cpp
|
||||
)
|
||||
|
||||
target_include_directories(usermod_wakeup INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(usermod INTERFACE usermod_wakeup)
|
||||
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_LIST_DIR}/wakeup.c
|
||||
PROPERTIES COMPILE_FLAGS
|
||||
"-Wno-discarded-qualifiers"
|
||||
)
|
||||
21
modules/c/wakeup/wakeup.c
Normal file
21
modules/c/wakeup/wakeup.c
Normal file
@ -0,0 +1,21 @@
|
||||
#include "wakeup.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "pico/runtime_init.h"
|
||||
|
||||
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(Wakeup_get_gpio_state_obj, Wakeup_get_gpio_state);
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(Wakeup_reset_gpio_state_obj, Wakeup_reset_gpio_state);
|
||||
|
||||
static const mp_map_elem_t wakeup_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_wakeup) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_get_gpio_state), MP_ROM_PTR(&Wakeup_get_gpio_state_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_reset_gpio_state), MP_ROM_PTR(&Wakeup_reset_gpio_state_obj) },
|
||||
};
|
||||
static MP_DEFINE_CONST_DICT(mp_module_wakeup_globals, wakeup_globals_table);
|
||||
|
||||
const mp_obj_module_t wakeup_user_cmodule = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_wakeup_globals,
|
||||
};
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_wakeup, wakeup_user_cmodule);
|
||||
27
modules/c/wakeup/wakeup.cpp
Normal file
27
modules/c/wakeup/wakeup.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
|
||||
uint64_t gpio_state = 0;
|
||||
|
||||
static void __attribute__((constructor(101))) latch_inputs() {
|
||||
gpio_set_function_masked64(0xffffffffffffffff, GPIO_FUNC_SIO);
|
||||
gpio_state = gpio_get_all64();
|
||||
sleep_ms(5);
|
||||
gpio_state |= gpio_get_all64();
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#include "wakeup.h"
|
||||
|
||||
mp_obj_t Wakeup_get_gpio_state(mp_obj_t button_in) {
|
||||
int button = mp_obj_get_int(button_in);
|
||||
return (gpio_state & (1 << button)) ? mp_const_true : mp_const_false;
|
||||
}
|
||||
|
||||
mp_obj_t Wakeup_reset_gpio_state() {
|
||||
gpio_state = 0;
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
}
|
||||
5
modules/c/wakeup/wakeup.h
Normal file
5
modules/c/wakeup/wakeup.h
Normal file
@ -0,0 +1,5 @@
|
||||
#include "py/runtime.h"
|
||||
#include "py/objstr.h"
|
||||
|
||||
extern mp_obj_t Wakeup_get_gpio_state(mp_obj_t button_in);
|
||||
extern mp_obj_t Wakeup_reset_gpio_state();
|
||||
Loading…
Reference in New Issue
Block a user