// The BOOTSEL button doubles as the touch button. #include "button.h" #include "hardware/gpio.h" #include "hardware/structs/ioqspi.h" #include "hardware/structs/sio.h" #include "hardware/sync.h" #include "platform.h" #define SAMPLE_INTERVAL_MS 10 #define DEBOUNCE_MS 30 #define QSPI_SS_INDEX 1 static bool pressed; static uint32_t last_sample_ms; static uint32_t last_change_ms; static bool press_pending; static uint32_t press_ms; // BOOTSEL pulls the flash chip-select line low. Reading it means briefly // releasing that line, so flash is unusable: run from RAM with interrupts off. static bool __no_inline_not_in_flash_func(bootsel_pressed)(void) { uint32_t irq = save_and_disable_interrupts(); hw_write_masked(&ioqspi_hw->io[QSPI_SS_INDEX].ctrl, GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB, IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS); for (volatile int i = 0; i < 1000; i++) { // let the line settle; sleep functions live in flash } #if PICO_RP2040 bool down = !(sio_hw->gpio_hi_in & (1u << QSPI_SS_INDEX)); #else bool down = !(sio_hw->gpio_hi_in & SIO_GPIO_HI_IN_QSPI_CSN_BITS); #endif hw_write_masked(&ioqspi_hw->io[QSPI_SS_INDEX].ctrl, GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB, IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS); restore_interrupts(irq); return down; } void button_poll(void) { uint32_t now = platform_millis(); if (now - last_sample_ms < SAMPLE_INTERVAL_MS) return; last_sample_ms = now; bool down = bootsel_pressed(); if (down == pressed || now - last_change_ms < DEBOUNCE_MS) return; pressed = down; last_change_ms = now; if (pressed) { press_pending = true; press_ms = now; } } bool platform_button_take_press(uint32_t max_age_ms) { button_poll(); if (!press_pending) return false; press_pending = false; return platform_millis() - press_ms <= max_age_ms; }