Skip to content

Commit 2d18bc4

Browse files
authored
Update PicoButtonAsync.h
The following changes are added: - The DebounceManager objects are initialised inside the PicoButton constructor. No need to call it explicitly from void setup() or main(). - Added the option using gpio interrupts to start the timer on a rising/falling edge.
1 parent d3cbd28 commit 2d18bc4

1 file changed

Lines changed: 46 additions & 8 deletions

File tree

src/PicoButtonAsync.h

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#include "pico/stdlib.h"
55
#include "pico/async_context_threadsafe_background.h"
6+
#include "pico/sync.h"
7+
#include "hardware/gpio.h"
68
#include <string.h>
79

810
#ifndef DEBOUNCE_SAMPLES
@@ -18,22 +20,31 @@ class DebounceManager {
1820
uint32_t _mask = 0;
1921
uint32_t _interval_ms;
2022

23+
static DebounceManager *s_active_instance;
24+
2125
static void _worker_callback(async_context_t *context, async_at_time_worker_t *worker) {
2226
DebounceManager *instance = (DebounceManager *)worker->user_data;
2327
instance->tick();
2428
async_context_add_at_time_worker_in_ms(context, worker, instance->_interval_ms);
2529
}
2630

31+
static void _gpio_irq_bridge(uint gpio, uint32_t events) {
32+
if (s_active_instance) {
33+
// Nudge the async context to run the worker immediately.
34+
async_context_add_at_time_worker_in_ms(s_active_instance->_context,
35+
&s_active_instance->_worker,
36+
0);
37+
}
38+
}
39+
2740
static async_context_t *_get_default_context() {
2841
static async_context_threadsafe_background_t shared_bg_context;
2942
static bool initialized = false;
30-
31-
// Disable Interrupts while initializing context.
3243
uint32_t status = save_and_disable_interrupts();
3344
if (!initialized) {
3445
#if LIB_PICO_CYW43_ARCH
3546
async_context_t *wifi_ctx = cyw43_arch_async_context();
36-
if (wifi_ctx){
47+
if (wifi_ctx) {
3748
restore_interrupts(status);
3849
return wifi_ctx;
3950
}
@@ -44,6 +55,7 @@ class DebounceManager {
4455
restore_interrupts(status);
4556
return &shared_bg_context.core;
4657
}
58+
4759
public:
4860
volatile uint32_t currentState = 0xFFFFFFFF;
4961

@@ -53,32 +65,45 @@ class DebounceManager {
5365
memset(_history, 0xFF, sizeof(_history));
5466
_worker.do_work = _worker_callback;
5567
_worker.user_data = this;
68+
s_active_instance = this;
5669
}
5770

5871
void begin() {
5972
async_context_add_at_time_worker_in_ms(_context, &_worker, _interval_ms);
6073
}
6174

62-
void addPin(int pin) {
75+
/**
76+
* @param pin The GPIO pin
77+
* @param useInterrupt If true, uses GPIO IRQs to trigger an immediate debounce check.
78+
*/
79+
void addPin(int pin, bool useInterrupt = false) {
6380
gpio_init(pin);
6481
gpio_set_dir(pin, GPIO_IN);
6582
gpio_pull_up(pin);
6683

6784
async_context_acquire_lock_blocking(_context);
6885
_mask |= (1UL << pin);
86+
87+
if (useInterrupt) {
88+
// Attach the global bridge to this pin
89+
gpio_set_irq_enabled_with_callback(pin, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &_gpio_irq_bridge);
90+
}
6991
async_context_release_lock(_context);
7092
}
7193

7294
void tick() {
7395
_history[_index] = gpio_get_all() & _mask;
7496
_index = (_index + 1) % DEBOUNCE_SAMPLES;
75-
uint32_t stableHigh = 0xFFFFFFFF, stableLow = 0;
7697

98+
uint32_t stableHigh = 0xFFFFFFFF, stableLow = 0;
7799
for (uint8_t i = 0; i < DEBOUNCE_SAMPLES; i++) {
78100
stableHigh &= _history[i];
79101
stableLow |= _history[i];
80102
}
103+
104+
async_context_acquire_lock_blocking(_context);
81105
currentState = (currentState | (stableHigh & _mask)) & (stableLow | ~_mask);
106+
async_context_release_lock(_context);
82107
}
83108

84109
uint32_t getSafeState() {
@@ -93,6 +118,9 @@ class DebounceManager {
93118
}
94119
};
95120

121+
// Initialize the static pointer
122+
DebounceManager *DebounceManager::s_active_instance = nullptr;
123+
96124
class PicoButton {
97125
protected:
98126
DebounceManager &_mgr;
@@ -102,7 +130,7 @@ class PicoButton {
102130

103131
/*
104132
Separate trackers so that the press and release
105-
functions don't "steal" the edge from each other.
133+
functions don't "steal" the edge from each other
106134
*/
107135

108136
bool _lastPressCheck = false;
@@ -114,10 +142,20 @@ class PicoButton {
114142
}
115143

116144
public:
117-
PicoButton(DebounceManager &mgr, int pin)
145+
/**
146+
* @param mgr Reference to the DebounceManager
147+
* @param pin GPIO number
148+
* @param useInterrupt Whether to use IRQs for zero-latency wakeup
149+
*/
150+
PicoButton(DebounceManager &mgr, int pin, bool useInterrupt = false)
118151
: _mgr(mgr), _pinMask(1UL << pin) {
119-
_mgr.addPin(pin);
152+
153+
_mgr.addPin(pin, useInterrupt);
154+
155+
// Start the manager
156+
_mgr.begin();
120157
}
158+
121159
virtual bool isPressed() {
122160
return !(_mgr.getSafeState() & _pinMask);
123161
}

0 commit comments

Comments
 (0)