151 lines
4.5 KiB
Plaintext
Raw Normal View History

2018-05-10 22:23:31 +02:00
<!-- { "title": "Yet Another Debouncing Method" } -->
2018-05-11 14:39:55 +02:00
<h1>#title#</h1>
<p>
2018-05-10 22:23:31 +02:00
You can find several approaches for debouncing mechanical switches on the Internet, some work better, some not so good.
2018-05-11 14:39:55 +02:00
</p>
2018-05-10 22:23:31 +02:00
2018-05-11 14:39:55 +02:00
<p>
One common approach is to ignore events in an ISR when they come too fast:<br/>
2018-05-10 22:23:31 +02:00
2018-05-11 14:39:55 +02:00
<pre><code class="C">
2018-05-10 22:23:31 +02:00
void count() {
static uint32_t lastEvent = 0;
uint32_t currentEvent = micros();
if (currentEvent &gt; (lastEvent + configBlock.debounce)) {
lastEvent = currentEvent;
cnt++;
}
}
void setup() {
pinMode(REED_PIN, INPUT_PULLUP);
attachInterrupt(REED_PIN, count, FALLING);
}
2018-05-11 14:39:55 +02:00
</code>
</pre>
2018-05-10 22:23:31 +02:00
This works very good when only the tipping of a switch is relevant.
2018-05-11 14:39:55 +02:00
</p>
2018-05-10 22:23:31 +02:00
2018-05-11 14:39:55 +02:00
<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/>
2018-05-10 22:23:31 +02:00
2018-05-11 14:39:55 +02:00
Since I couldn't remember the approaches I read about earlier I've sketched this state machine:<br/>
2018-05-10 22:23:31 +02:00
2018-05-11 14:39:55 +02:00
<img src="files/20180430110848869_0001.jpg" width="500"/>
2018-05-10 22:23:31 +02:00
2018-05-11 14:39:55 +02:00
<br/>
(The double-lined states are action-states which send out the related information.)<br>
2018-05-10 22:23:31 +02:00
2018-05-11 14:39:55 +02:00
At least for me, this approach is working very reliable so far, I'm quite happy with it.<br>
2018-05-10 22:23:31 +02:00
2018-05-11 14:39:55 +02:00
<pre><code class="C">
2018-05-10 22:23:31 +02:00
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-&gt;buttonPin);
#ifdef DEBUG
if (button-&gt;oldPressedState != button-&gt;pressedState) {
Serial.print("Index ");
Serial.print(button-&gt;index);
Serial.print(", state changed from ");
Serial.print(button-&gt;oldPressedState);
Serial.print(" to ");
Serial.print(button-&gt;pressedState);
Serial.println();
button-&gt;oldPressedState = button-&gt;pressedState;
}
#endif
switch (button-&gt;pressedState) {
case psHIGH:
if (buttonState == LOW) {
button-&gt;pressedState = psLOW;
button-&gt;lastStateChange = currentMicros;
}
break;
case psLOW:
if (buttonState == HIGH) {
button-&gt;pressedState = psHIGH;
button-&gt;lastStateChange = currentMicros;
} else {
if (currentMicros &gt; (button-&gt;lastStateChange + configBlock.debounce)) {
button-&gt;pressedState = psACCEPTED_LOW;
button-&gt;lastStateChange = currentMicros;
}
}
break;
case psACCEPTED_LOW:
if (buttonState == HIGH) {
button-&gt;pressedState = psSHORT;
button-&gt;lastStateChange = currentMicros;
}
if (currentMicros &gt; (button-&gt;lastStateChange + (configBlock.longPress * 1000))) {
button-&gt;pressedState = psLONG_START;
button-&gt;lastStateChange = currentMicros;
}
break;
case psSHORT:
sendMsg(button-&gt;index, "PRESS_SHORT");
button-&gt;pressedState = psHIGH;
button-&gt;lastStateChange = currentMicros;
break;
case psLONG_START:
sendMsg(button-&gt;index, "PRESS_LONG_START");
button-&gt;pressedState = psLONG_CONT;
button-&gt;lastStateChange = currentMicros;
break;
case psLONG_CONT:
if (buttonState == HIGH) {
button-&gt;pressedState = psLONG_END;
button-&gt;lastStateChange = currentMicros;
}
if (currentMicros &gt; (button-&gt;lastStateChange + (configBlock.longPressRepeat * 1000))) {
button-&gt;pressedState = psLONG_CONT_SEND;
button-&gt;lastStateChange = currentMicros;
}
break;
case psLONG_CONT_SEND:
sendMsg(button-&gt;index, "PRESS_LONG_CONT");
button-&gt;pressedState = psLONG_CONT;
button-&gt;lastStateChange = currentMicros;
break;
case psLONG_END:
sendMsg(button-&gt;index, "PRESS_LONG_END");
button-&gt;pressedState = psHIGH;
button-&gt;lastStateChange = currentMicros;
break;
default:
button-&gt;pressedState = psHIGH;
button-&gt;lastStateChange = currentMicros;
}
}
2018-05-11 14:39:55 +02:00
</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>