101 lines
3.2 KiB
C++
101 lines
3.2 KiB
C++
#include "bootsel_pairing_button.h"
|
|
|
|
#include "hardware/gpio.h"
|
|
#include "hardware/structs/ioqspi.h"
|
|
#include "hardware/structs/sio.h"
|
|
#include "pico/flash.h"
|
|
#include "pico/time.h"
|
|
#if PICO_RP2350
|
|
#include "hardware/regs/sio.h"
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
constexpr uint32_t kPollIntervalMs = 100;
|
|
constexpr uint32_t kFlashSafeTimeoutMs = 100;
|
|
constexpr uint32_t kQspiCsPinIndex = 1;
|
|
|
|
BootselPairingButtonHoldFsm g_hold_fsm;
|
|
uint32_t g_last_sample_ms = 0;
|
|
|
|
// QSPI CSn sampling adapted from awalol/DS5Dongle's button_functions.cpp:
|
|
// https://github.com/awalol/DS5Dongle/blob/master/src/button_functions.cpp
|
|
// Copyright (c) 2026 awalol; used under the MIT License.
|
|
//
|
|
// This callback and everything it executes while CSn is floated must remain in
|
|
// SRAM or be an inlined hardware-register operation. In particular, do not add
|
|
// logging or ordinary flash-backed data access here.
|
|
void __no_inline_not_in_flash_func(read_bootsel_callback)(void* parameter) {
|
|
auto* pressed = static_cast<bool*>(parameter);
|
|
|
|
hw_write_masked(
|
|
&ioqspi_hw->io[kQspiCsPinIndex].ctrl,
|
|
GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
|
|
IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
|
|
|
|
for (volatile uint32_t delay = 0; delay < 1000; ++delay) {
|
|
}
|
|
|
|
#if PICO_RP2350
|
|
*pressed =
|
|
(sio_hw->gpio_hi_in & SIO_GPIO_HI_IN_QSPI_CSN_BITS) == 0;
|
|
#else
|
|
*pressed = (sio_hw->gpio_hi_in & (1u << kQspiCsPinIndex)) == 0;
|
|
#endif
|
|
|
|
hw_write_masked(
|
|
&ioqspi_hw->io[kQspiCsPinIndex].ctrl,
|
|
GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
|
|
IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
|
|
}
|
|
|
|
BootselPairingButtonSample sample_bootsel() {
|
|
bool pressed = false;
|
|
const int result = flash_safe_execute(read_bootsel_callback, &pressed,
|
|
kFlashSafeTimeoutMs);
|
|
if (result != PICO_OK) {
|
|
return BootselPairingButtonSample::kUnread;
|
|
}
|
|
return pressed ? BootselPairingButtonSample::kPressed
|
|
: BootselPairingButtonSample::kReleased;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
BootselPairingButtonEvent BootselPairingButtonHoldFsm::update(
|
|
BootselPairingButtonSample sample) {
|
|
if (sample == BootselPairingButtonSample::kUnread) {
|
|
return BootselPairingButtonEvent::kNone;
|
|
}
|
|
|
|
if (sample == BootselPairingButtonSample::kReleased) {
|
|
pressed_samples_ = 0;
|
|
pairing_reported_ = false;
|
|
clear_reported_ = false;
|
|
return BootselPairingButtonEvent::kNone;
|
|
}
|
|
|
|
if (pressed_samples_ < kClearHoldSamples) {
|
|
++pressed_samples_;
|
|
}
|
|
if (pressed_samples_ >= kClearHoldSamples && !clear_reported_) {
|
|
clear_reported_ = true;
|
|
return BootselPairingButtonEvent::kClearPairings;
|
|
}
|
|
if (pressed_samples_ >= kPairingHoldSamples && !pairing_reported_) {
|
|
pairing_reported_ = true;
|
|
return BootselPairingButtonEvent::kOpenPairing;
|
|
}
|
|
return BootselPairingButtonEvent::kNone;
|
|
}
|
|
|
|
BootselPairingButtonEvent bootsel_pairing_button_task() {
|
|
const uint32_t now_ms =
|
|
static_cast<uint32_t>(to_ms_since_boot(get_absolute_time()));
|
|
if (now_ms - g_last_sample_ms < kPollIntervalMs) {
|
|
return BootselPairingButtonEvent::kNone;
|
|
}
|
|
g_last_sample_ms = now_ms;
|
|
|
|
return g_hold_fsm.update(sample_bootsel());
|
|
}
|