This commit is contained in:
137
content/snippets/0020-debouncing.md
Normal file
137
content/snippets/0020-debouncing.md
Normal file
@ -0,0 +1,137 @@
|
||||
<!--
|
||||
title: Yet Another Debouncing Method
|
||||
date: 2018-04-30
|
||||
-->
|
||||
|
||||
You can find several approaches for debouncing mechanical switches on the Internet, some work better, some not so good.
|
||||
|
||||
One common approach is to ignore events in an ISR when they come too fast:<
|
||||
|
||||
```
|
||||
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);
|
||||
}
|
||||
```
|
||||
|
||||
This works very good when only the tipping of a switch is relevant.
|
||||
|
||||
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.
|
||||
|
||||
Since I couldn't remember the approaches I read about earlier I've sketched this state machine:
|
||||
|
||||

|
||||
|
||||
(The double-lined states are action-states which send out the related information.)
|
||||
|
||||
At least for me, this approach is working very reliable so far, I'm quite happy with it.
|
||||
|
||||
```
|
||||
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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
Reference in New Issue
Block a user