151 lines
4.5 KiB
Plaintext
151 lines
4.5 KiB
Plaintext
<!-- { "title": "Yet Another Debouncing Method" } -->
|
|
|
|
<h1>#title#</h1>
|
|
<p>
|
|
You can find several approaches for debouncing mechanical switches on the Internet, some work better, some not so good.
|
|
</p>
|
|
|
|
<p>
|
|
One common approach is to ignore events in an ISR when they come too fast:<br/>
|
|
|
|
<pre><code class="C">
|
|
void count() {
|
|
static uint32_t lastEvent = 0;
|
|
uint32_t currentEvent = micros();
|
|
if (currentEvent > (lastEvent + configBlock.debounce)) {
|
|
lastEvent = currentEvent;
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
void setup() {
|
|
pinMode(REED_PIN, INPUT_PULLUP);
|
|
attachInterrupt(REED_PIN, count, FALLING);
|
|
}
|
|
</code>
|
|
</pre>
|
|
|
|
This works very good when only the tipping of a switch is relevant.
|
|
</p>
|
|
|
|
<p>
|
|
When also the time the button was pressed is relevant and when it is especially necessary to distinguish between a short and a long press this approach doesn't work anymore.<br/>
|
|
|
|
Since I couldn't remember the approaches I read about earlier I've sketched this state machine:<br/>
|
|
|
|
<img src="files/20180430110848869_0001.jpg" width="500"/>
|
|
|
|
<br/>
|
|
(The double-lined states are action-states which send out the related information.)<br>
|
|
|
|
At least for me, this approach is working very reliable so far, I'm quite happy with it.<br>
|
|
|
|
<pre><code class="C">
|
|
enum tPressedState { psHIGH, psLOW, psACCEPTED_LOW, psLONG_START, psLONG_CONT, psLONG_CONT_SEND, psLONG_END, psSHORT, psINVALID };
|
|
|
|
typedef struct {
|
|
uint8_t index;
|
|
uint8_t buttonPin;
|
|
tPressedState pressedState;
|
|
tPressedState oldPressedState;
|
|
uint32_t lastStateChange;
|
|
} tButton;
|
|
|
|
tButton buttons[] = {
|
|
{ 1, SWITCH_1, psHIGH, psINVALID, 0 },
|
|
{ 2, SWITCH_2, psHIGH, psINVALID, 0 },
|
|
{ 3, SWITCH_3, psHIGH, psINVALID, 0 },
|
|
{ 0, 0, psINVALID, psINVALID, 0 } // END MARKER
|
|
};
|
|
|
|
static void buttonHandler(tButton *button) {
|
|
uint32_t currentMicros = micros();
|
|
uint8_t buttonState = digitalRead(button->buttonPin);
|
|
|
|
#ifdef DEBUG
|
|
if (button->oldPressedState != button->pressedState) {
|
|
Serial.print("Index ");
|
|
Serial.print(button->index);
|
|
Serial.print(", state changed from ");
|
|
Serial.print(button->oldPressedState);
|
|
Serial.print(" to ");
|
|
Serial.print(button->pressedState);
|
|
Serial.println();
|
|
button->oldPressedState = button->pressedState;
|
|
}
|
|
#endif
|
|
|
|
switch (button->pressedState) {
|
|
case psHIGH:
|
|
if (buttonState == LOW) {
|
|
button->pressedState = psLOW;
|
|
button->lastStateChange = currentMicros;
|
|
}
|
|
break;
|
|
case psLOW:
|
|
if (buttonState == HIGH) {
|
|
button->pressedState = psHIGH;
|
|
button->lastStateChange = currentMicros;
|
|
} else {
|
|
if (currentMicros > (button->lastStateChange + configBlock.debounce)) {
|
|
button->pressedState = psACCEPTED_LOW;
|
|
button->lastStateChange = currentMicros;
|
|
}
|
|
}
|
|
break;
|
|
case psACCEPTED_LOW:
|
|
if (buttonState == HIGH) {
|
|
button->pressedState = psSHORT;
|
|
button->lastStateChange = currentMicros;
|
|
}
|
|
if (currentMicros > (button->lastStateChange + (configBlock.longPress * 1000))) {
|
|
button->pressedState = psLONG_START;
|
|
button->lastStateChange = currentMicros;
|
|
}
|
|
break;
|
|
case psSHORT:
|
|
sendMsg(button->index, "PRESS_SHORT");
|
|
button->pressedState = psHIGH;
|
|
button->lastStateChange = currentMicros;
|
|
break;
|
|
case psLONG_START:
|
|
sendMsg(button->index, "PRESS_LONG_START");
|
|
button->pressedState = psLONG_CONT;
|
|
button->lastStateChange = currentMicros;
|
|
break;
|
|
case psLONG_CONT:
|
|
if (buttonState == HIGH) {
|
|
button->pressedState = psLONG_END;
|
|
button->lastStateChange = currentMicros;
|
|
}
|
|
if (currentMicros > (button->lastStateChange + (configBlock.longPressRepeat * 1000))) {
|
|
button->pressedState = psLONG_CONT_SEND;
|
|
button->lastStateChange = currentMicros;
|
|
}
|
|
break;
|
|
case psLONG_CONT_SEND:
|
|
sendMsg(button->index, "PRESS_LONG_CONT");
|
|
button->pressedState = psLONG_CONT;
|
|
button->lastStateChange = currentMicros;
|
|
break;
|
|
case psLONG_END:
|
|
sendMsg(button->index, "PRESS_LONG_END");
|
|
button->pressedState = psHIGH;
|
|
button->lastStateChange = currentMicros;
|
|
break;
|
|
default:
|
|
button->pressedState = psHIGH;
|
|
button->lastStateChange = currentMicros;
|
|
}
|
|
}
|
|
</code>
|
|
</pre>
|
|
</p>
|
|
|
|
<p>
|
|
Find it embedded in the code of a small ESP8266-based switch thing I'm using in
|
|
my home automation setup (home grown control code (<a href="https://gitlab.com/wolutator/dispatcher_ng">on gitlab</a>),
|
|
<a href="https://homegear.eu/">homegear</a> for device integration and <a href="https://www.openhab.org/">openhab</a>
|
|
as user interface) <a href="https://gitlab.com/wolutator/MySwitch">here on gitlab</a>.
|
|
</p>
|