Forum: Mikrocontroller und Digitale Elektronik Debounce von Tasten mit dem Candidate-Pattern (Platformunabhängig, Theorie)


von Andreas T. (drice)



Lesenswert?

Entprellen von Tasten mit dem Candidate-Pattern
-----------------------------------------------

Ein einfacher und robuster Stabilitätsfilter für 
Mikrocontroller-Eingaben.

Die folgende Idee habe ich für ein ATMega-Projekt (DIY Stream-Deck) 
entwickelt. Ich möchte die Idee mit diesem Artikel teilen.

Es geht hier mehr um eine Idee als um Code für eine spezielle Platform.

Einleitung
----------
Um mechanische Tasten und Schalter software-seitig zu entprellen,
habe ich folgende Idee entwickelt und nenne sie Candidate-Pattern.

Das Candidate-Pattern ist ein Stabilitätsfilter: Ein neu beobachteter 
Zustand wird zunächst als möglicher Kandidat betrachtet und erst dann 
übernommen, wenn er über mehrere Messzyklen hinweg unverändert bleibt.

Der Algorithmus ist bewusst plattformunabhängig:

- funktioniert auf praktisch jedem Mikrocontroller oder Mikroprozessor
- ist unabhängig davon, wie die Tasten eingelesen werden
- funktioniert mit GPIO, Tastaturmatrix, IO-Expander oder anderen 
Eingabemechanismen

Der Debounce-Algorithmus arbeitet ausschließlich auf dem bereits 
gelesenen
Rohzustand der Tasten.

Warum Taster entprellt werden müssen
------------------------------------

Beispiel eines prellenden Signals:
1
Rohsignal (Bounce)
2
3
HIGH ────────┐   ┌─┐ ┌──┐ ┌─┐
4
              │   │ │ │  │ │ │
5
LOW  ─────────┴───┘ └─┘  └─┘ └────────
6
7
Zeit →

Der Mikrocontroller würde mehrere Flanken erkennen,
obwohl der Benutzer nur einmal gedrückt hat.

Nach der Entprellung entsteht ein stabiler Zustand:
1
Debounced Signal
2
3
HIGH ────────────────┐
4
5
LOW  ────────────────┴──────────────
6
7
Zeit →



Grundidee des Candidate-Patterns
--------------------------------

Der Algorithmus arbeitet mit drei Zuständen:
1
Variable        Bedeutung
2
--------------------------------------------
3
rawMask         aktueller Rohzustand der Tasten
4
candidateMask   möglicher neuer Zustand
5
stableMask      zuletzt bestätigter stabiler Zustand

Zusätzlich existiert ein Zähler:
1
candidateTimer

Ein neuer Zustand wird nicht sofort akzeptiert.
Er wird zunächst als Kandidat gespeichert.

Die gemessenen Eingangswerte (z.b. Tastenmatrix-Scan) sind der 
Candidate
Erst wenn dieser Kandidat lange genug unverändert bleibt, wird er zum 
neuen stabilen Zustand.
Ändert sich der gemessene Zustand, gilt dieser als neuer Kandidat und 
der Zähler wird zurückgesetzt (reset).

Periodischer Aufruf des Algorithmus
-----------------------------------
Der Algorithmus kann einen Timer verwenden, muss aber nicht.

Er kann beispielsweise durch einen Hardware-Timer regelmäßig aufgerufen 
werden, funktioniert aber ebenso gut innerhalb einer zyklischen 
Hauptschleife.

Der interne candidateTimer zählt lediglich, wie viele Update-Zyklen 
der Kandidat unverändert geblieben ist.
Entscheidend ist nicht der Timer selbst, sondern ein *regelmäßiger 
Aufruf des Debounce-Algorithmus*.

Variante 1 – Timer-Interrupt
1
Timer Interrupt (1 ms)
2
    -> scanKeys()
3
    -> debounceUpdate()

Variante 2 – Hauptschleife
1
while (1)
2
{
3
    scanKeys();
4
    debounceUpdate();
5
}

Solange der Algorithmus in einem annähernd konstanten
zeitlichen Raster ausgeführt wird, arbeitet er zuverlässig.



Ablauf des Algorithmus
----------------------

Bei jedem Update passiert Folgendes:

1. Rohzustand der Tasten lesen (rawMask)
2. Prüfen, ob sich der Kandidat geändert hat
3. Wenn ja → neuen Kandidaten setzen und Zähler zurücksetzen
4. Wenn nein → Zähler erhöhen
5. Wenn stabil → neuen stabilen Zustand übernehmen und Event setzen



Mehrere Tasten mit Bitmasken
----------------------------
1
Bit 0 -> Taste 0
2
Bit 1 -> Taste 1
3
Bit 2 -> Taste 2

Beispiele (gemessener raw/candidate wert):
1
00000000  keine Taste gedrückt
2
00000001  Taste 0 gedrückt
3
00000101  Taste 0 und Taste 2 gedrückt



Press- und Release-Events
-------------------------
1
pressedMask  = newState & ~oldState
2
releasedMask = oldState & ~newState

Damit erhält man

- neu gedrückte Tasten
- losgelassene Tasten



Event-Semantik
--------------

Ein Event ist ein einmaliger Impuls.

Typische Events:

- PRESSED
- RELEASED



Zusatzinformation im Event
--------------------------
1
typedef struct
2
{
3
    uint32_t pressedMask;
4
    uint32_t releasedMask;
5
    uint16_t stabilityCount;
6
} ButtonEvent;



Beispielimplementierung (Arduino)
---------------------------------

[cpp]

void MatrixKeys::Loop()
{
    uint8_t scannedIndex = ScanRow(0);
    if (!scannedIndex) scannedIndex = ScanRow(1);
    if (!scannedIndex) scannedIndex = ScanRow(2);

    if (scannedIndex == m_candidateIndex)
    {
        if (millis() - m_startMillis > BUTTON_DEBOUNCE_TIME)
        {
            if (m_currentKeyIndex != m_candidateIndex)
            {
                m_currentKeyIndex = m_candidateIndex;
                m_eventFlag = 1;
            }
        }
    }
    else
    {
        m_candidateIndex = scannedIndex;
        m_startMillis = millis();

        if (m_candidateIndex == 0 && m_currentKeyIndex != 0)
        {
            m_currentKeyIndex = 0;
            m_eventFlag = 1;
        }
    }
}
[/cpp]



Beispiel-Pseudocode
-------------------
1
void debounceUpdate(uint32_t rawMask)
2
{
3
    if (rawMask != candidateMask)
4
    {
5
        candidateMask = rawMask;
6
        candidateTimer = 0;
7
    }
8
    else
9
    {
10
        candidateTimer++;
11
12
        if (candidateTimer >= DEBOUNCE_COUNT)
13
        {
14
            if (candidateMask != stableMask)
15
            {
16
                pressedMask  = candidateMask & ~stableMask;
17
                releasedMask = stableMask & ~candidateMask;
18
19
                stableMask = candidateMask;
20
            }
21
        }
22
    }
23
}



Typische Parameter
------------------
1
Aufrufintervall: 1–5 ms
2
DEBOUNCE_COUNT: 5–20

Zusammenfassung
---------------

Das Candidate-Pattern ist eine einfache und robuste Methode zur
Entprellung mechanischer Taster.

Voraussetzung ist, dass das Eingangssignal zu einem stabilen Zustand 
gelangen kann. Bei mechanischen Tastern und Schaltern ist dies in der 
Regel der Fall, wenn sie einmal die entsprechende Endlage erreicht 
haben, während sie beim herunterdrücken/loslassen oft den "Prell-Effekt" 
haben. Eine fein-Justierung und Glättung kann durch Aufrufhäufigkeit und 
der minimal erforderlichen Stabilitäts-Zeit eingestellt werden.

**Vergleich mit anderen Debounce-Algorithmen**

Es existieren verschiedene Methoden, um das Prellen mechanischer 
Kontakte zu
unterdrücken. Die folgenden Ansätze sind in Mikrocontroller-Projekten 
besonders
verbreitet.

RC-Filter (Hardware)
- Prinzip: Analogfilter glättet das Signal
- Vorteile: sehr einfach, keine CPU-Last
- Nachteile: zusätzliche Bauteile, feste Zeitkonstante

Timer-Debounce
- Prinzip: nach einer Flanke wird eine feste Sperrzeit gestartet
- Vorteile: leicht zu implementieren
- Nachteile: reagiert träge

Integrator / Counter
- Prinzip: Zähler integriert den Eingang über mehrere Zyklen
- Vorteile: robust gegen Störungen
- Nachteile: etwas komplexer

*(Neu)*: Candidate-Pattern
- Prinzip: neuer Zustand wird erst nach stabiler Dauer akzeptiert
- Vorteile: flexibel, effizient
- Nachteile: benötigt zyklischen Aufruf

**Einordnung des Candidate-Patterns**

Das Candidate-Pattern lässt sich als Stabilitätsprüfung eines 
beobachteten
Zustands verstehen.

Im Gegensatz zu einem klassischen Timer-Debounce wird keine feste 
Sperrzeit
gestartet. Stattdessen wird kontinuierlich geprüft, ob ein neu 
beobachteter
Zustand lange genug stabil bleibt.

Daraus ergeben sich einige praktische Vorteile:

- keine individuellen Timer pro Taste notwendig
- funktioniert mit vielen Eingängen gleichzeitig
- unterstützt mehrere gleichzeitig gedrückte Tasten
- gut geeignet für zyklische Scan-Systeme (z.B. Tastaturmatrizen)

Der Algorithmus eignet sich besonders für Systeme, in denen Eingänge 
ohnehin
regelmäßig gescannt werden, beispielsweise:

- Tastaturmatrizen
- GPIO-Scanning
- Polling-basierte Eingabeverarbeitung

**Wann andere Methoden sinnvoll sein können**

Andere Debounce-Methoden können in bestimmten Situationen besser 
geeignet sein:

- RC-Filter bei sehr einfachen Schaltungen ohne Softwarelogik
- Timer-Debounce bei einzelnen Tastern mit Interrupts
- Integrator-Methoden bei stark verrauschten Signalen

In vielen Mikrocontroller-Anwendungen mit zyklischer Eingabeerfassung 
stellt
das Candidate-Pattern jedoch eine sehr einfache und flexible Lösung dar.

Vorteile:

- plattformunabhängig
- geringer Rechenaufwand
- funktioniert mit beliebig vielen Tasten
- keine Timer pro Taste notwendig
- saubere Event-Erzeugung

: Bearbeitet durch User
von Rahul D. (rahul)


Lesenswert?

Andreas T. schrieb:
> Ich möchte die Idee mit diesem Artikel teilen.

Dann solltest du mal in der Artikelsammlung nach anderen Lösungen 
gucken.
Peter Dannegger hat sowas (zwar kryptischer, aber kürzer) schon vor 
diversen Jahren veröffentlicht

von Andreas T. (drice)


Lesenswert?

Danke Dir. Das dachte ich mir schon, dass die Idee wahrscheinlich schon 
uralt ist. Ich schaue mir den Artikel von Peter mal an - vermutlich hab 
ich den einfach übersehen.

LG, Andy

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.