1.8 KiB
Quadratur-Encoder
Dateien: hal/encoder.h, hal/encoder.cpp, config/pins.h
Hardware
4 mechanische Quadratur-Encoder mit je 2 Phasen-Pins (A, B). Pins sind INPUT_PULLUP. Encoder-SW-Tasten laufen nicht durch diesen HAL, sondern durch den Matrix-Scan (COL_0).
Dekodierung
4-State-Lookup-Table über (prev_state << 2) | cur_state:
Zustand = (A << 1) | B → 4 Bits: 00 / 01 / 10 / 11
LUT[prev<<2 | cur] → Roh-Vorzeichen, 0 (ungueltig/Prellen)
Mechanische Encoder erzeugen 4 Flanken pro Raste → Akkumulator zählt Halbschritte.
Ein Event wird erst gefeuert wenn |accum| >= 4 (= ein vollständiger Klick).
Die VersaPad-PCB-Verdrahtung liefert das Quadratur-Vorzeichen gegenueber der sichtbaren Drehrichtung invertiert. Deshalb bleibt die LUT konventionell, aber der HAL dreht das Vorzeichen vor dem Callback mit ENCODER_DIRECTION_SIGN = -1. Nach aussen gilt weiterhin:
direction = +1->ENC_CWdirection = -1->ENC_CCW
ISR-Aufbau
8 ISR-Wrapper (je einer pro Pin, da attachInterrupt keinen Parameter unterstützt):
static void isr_enc0_a() { handle_encoder(0); }
static void isr_enc0_b() { handle_encoder(0); }
// ... analog für Encoder 1–3
attachInterrupt(..., CHANGE) auf beiden Pins jedes Encoders.
handle_encoder() → encoder_cb(enc_id, direction) → CEventQueue::push(ENC_CW / ENC_CCW)
ISR-Sicherheit
s_state[]unds_accum[]sindvolatileCEventQueue::push()ist ISR-sicher (atomare Index-Inkremente auf Single-Core-M0+, kein Heap)- Der Callback-Pointer
s_cbwird einmalig insetup()gesetzt, bevor Interrupts aktiviert werden
Initialisierung
Initialer Zustand von A/B wird beim encoder_init() gelesen damit der erste Interrupt korrekt ausgewertet wird (kein "Phantom-Step" beim Einschalten).