Skip to content

Instantly share code, notes, and snippets.

@Siapran
Created December 25, 2020 14:42
Show Gist options
  • Save Siapran/e1753b3a89f0e73a07b465cdfbf77680 to your computer and use it in GitHub Desktop.
Save Siapran/e1753b3a89f0e73a07b465cdfbf77680 to your computer and use it in GitHub Desktop.
#pragma once
#include "oled_gui.h"
template <typename T>
T clamped_sub(T lhs, T rhs)
{
return lhs > rhs ? lhs - rhs : 0;
}
template <const unsigned long VALID_MS,
const unsigned long HOLD_MS,
const unsigned long REPEAT_MS>
struct DebouncedKey
{
// comparing DURATIONS with < is allowed
// comparing TIMESTAMPS is not. use - and overflow math
// assume `now` is always measured at or after `last_change`
// returns true on debounced change
bool add_sample(bool sample, unsigned long now)
{
if (sample != last_sample)
{
last_sample = sample;
last_change = now;
}
else if (debounced_state != sample && now - last_change >= VALID_MS)
{
debounced_state = sample;
return true;
}
return false;
}
bool has_changed(unsigned long now) { return add_sample(last_sample, now); }
bool is_set(unsigned long now)
{
add_sample(last_sample, now);
return debounced_state;
}
bool is_held(unsigned long now)
{
return is_set(now) &&
clamped_sub(now - last_change, VALID_MS) >= HOLD_MS;
}
unsigned long count_repeats(unsigned long now)
{
unsigned long counts =
clamped_sub(now - last_change, VALID_MS + HOLD_MS) / REPEAT_MS;
return is_set(now) ? counts : 0;
}
unsigned long consume_repeats(unsigned long now)
{
unsigned long counts = count_repeats(now);
unsigned long consumed = clamped_sub(counts, repeats);
repeats = counts;
return consumed;
}
unsigned long last_change = 0;
unsigned long repeats = 0;
bool last_sample = false;
bool debounced_state = false;
};
struct Key
{
const size_t pin;
char const* const name;
void (*action)(void);
DebouncedKey<DEBOUNCE_KEY_VALID_MS,
DEBOUNCE_KEY_HOLD_MS,
DEBOUNCE_KEY_REPEAT_MS>
debounced{};
};
Key keys[] = {{PIN_KEY_UP, "KEY_UP", upAction},
{PIN_KEY_DOWN, "KEY_DOWN", downAction},
{PIN_KEY_ENTER, "KEY_ENTER", enterAction},
{PIN_KEY_BACK, "KEY_BACK", backAction}};
void init_keys()
{
for (auto& key : keys)
{
attachInterrupt(
key.pin, InterruptDelegate([&key] {
unsigned long now = millis();
if (key.debounced.add_sample(digitalRead(key.pin) == LOW, now))
{
if (key.debounced.is_set(now))
{
key.action();
}
}
}),
CHANGE);
pinMode(key.pin, INPUT_PULLUP);
}
}
void update_input()
{
for (auto& key : keys)
{
unsigned long now = millis();
if (key.debounced.has_changed(now))
{
if (key.debounced.is_set(now))
{
key.action();
}
}
auto consumed = key.debounced.consume_repeats(now);
for (int i = 0; i < consumed; ++i)
{
key.action();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment