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+
4759public:
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+
96124class PicoButton {
97125protected:
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
116144public:
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