diff --git a/docs/library/rp2.rst b/docs/library/rp2.rst index ec7666d7ce..7a473387b4 100644 --- a/docs/library/rp2.rst +++ b/docs/library/rp2.rst @@ -66,6 +66,17 @@ For running PIO programs, see :class:`rp2.StateMachine`. >>> rp2.asm_pio_encode("set(0, 1)", 0) 57345 +.. function:: bootsel_button() + + Temporarily turns the QSPI_SS pin into an input and reads its value, + returning 1 for low and 0 for high. + On a typical RP2040 board with a BOOTSEL button, a return value of 1 + indicates that the button is pressed. + + Since this function temporarily disables access to the external flash + memory, it also temporarily disables interrupts and the other core to + prevent them from trying to execute code from flash. + .. class:: PIOASMError This exception is raised from `asm_pio()` or `asm_pio_encode()` if there is diff --git a/ports/rp2/modrp2.c b/ports/rp2/modrp2.c index cb5f515642..94d49489c0 100644 --- a/ports/rp2/modrp2.c +++ b/ports/rp2/modrp2.c @@ -41,49 +41,43 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mod_network_country_obj); #endif -// Picoboard has a button attached to the flash CS pin, which the bootrom -// checks, and jumps straight to the USB bootcode if the button is pressed -// (pulling flash CS low). We can check this pin in by jumping to some code in -// SRAM (so that the XIP interface is not required), floating the flash CS -// pin, and observing whether it is pulled low. -// -// This doesn't work if others are trying to access flash at the same time, -// e.g. XIP streamer, or the other core. +// Improved version of // https://github.com/raspberrypi/pico-examples/blob/master/picoboard/button/button.c - -bool __no_inline_not_in_flash_func(get_bootsel_button)() { +STATIC bool __no_inline_not_in_flash_func(bootsel_button)(void) { const uint CS_PIN_INDEX = 1; - // Must disable interrupts, as interrupt handlers may be in flash, and we - // are about to temporarily disable flash access! - uint32_t flags = save_and_disable_interrupts(); + // Disable interrupts and the other core since they might be + // executing code from flash and we are about to temporarily + // disable flash access. + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); - // Set chip select to Hi-Z + // Set the CS pin to high impedance. hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl, - GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB, - IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS); + (GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB), + IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS); - // Note we can't call into any sleep functions in flash right now - for (volatile int i = 0; i < 1000; ++i) {; + // Delay without calling any functions in flash. + uint32_t start = timer_hw->timerawl; + while ((uint32_t)(timer_hw->timerawl - start) <= MICROPY_HW_BOOTSEL_DELAY_US) { + ; } // The HI GPIO registers in SIO can observe and control the 6 QSPI pins. - // Note the button pulls the pin *low* when pressed. - bool button_state = !(sio_hw->gpio_hi_in & (1u << CS_PIN_INDEX)); + // The button pulls the QSPI_SS pin *low* when pressed. + bool button_state = !(sio_hw->gpio_hi_in & (1 << CS_PIN_INDEX)); - // Need to restore the state of chip select, else we are going to have a - // bad time when we return to code in flash! + // Restore the QSPI_SS pin so we can use flash again. hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl, - GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB, - IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS); + (GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB), + IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS); - restore_interrupts(flags); + MICROPY_END_ATOMIC_SECTION(atomic_state); return button_state; } STATIC mp_obj_t rp2_bootsel_button(void) { - return MP_OBJ_NEW_SMALL_INT(get_bootsel_button()); + return MP_OBJ_NEW_SMALL_INT(bootsel_button()); } MP_DEFINE_CONST_FUN_OBJ_0(rp2_bootsel_button_obj, rp2_bootsel_button); diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index c987a0dc24..fc86e33e7e 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -217,6 +217,10 @@ extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k; #define MICROPY_HW_USB_PID (0x0005) // RP2 MicroPython #endif +#ifndef MICROPY_HW_BOOTSEL_DELAY_US +#define MICROPY_HW_BOOTSEL_DELAY_US 8 +#endif + // Entering a critical section. extern uint32_t mp_thread_begin_atomic_section(void); extern void mp_thread_end_atomic_section(uint32_t);