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:
Phil Howard 2025-06-06 15:19:25 +01:00
parent 9ec3001410
commit da4cbd989f
12 changed files with 614 additions and 0 deletions

View File

@ -1,3 +1,10 @@
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../") 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) include(usermod-common)

View File

@ -1,3 +1,10 @@
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../") 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) include(usermod-common)

150
modules/c/powman/bindings.c Normal file
View 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);

View 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
View 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
View 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
View 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
View 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, 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());
};
void rosc_enable(void);
#ifdef __cplusplus
}
#endif
#endif

View 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
View 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);

View 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;
}
}

View 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();