Created
December 25, 2020 14:42
-
-
Save Siapran/e1753b3a89f0e73a07b465cdfbf77680 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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