3.4 KiB
LED-System (WS2812)
Dateien: hal/ws2812.h, hal/ws2812.cpp, CButton.h, CButton.cpp
Hardware-Treiber (ws2812 HAL)
Dünner Wrapper um Adafruit NeoPixel (bit-bang, kein DMA, kein SERCOM).
| Funktion | Bedeutung |
|---|---|
ws2812_init() |
begin() + clear() + show() |
ws2812_set(idx, r, g, b) |
setPixelColor() – schreibt in RAM-Puffer |
ws2812_fill(r, g, b) |
Alle LEDs gleiche Farbe |
ws2812_show() |
Bit-Bang-Übertragung (~600 µs, Interrupts gesperrt) |
ws2812_clear() |
clear() + show() |
ws2812_show() wird in CMainController::updateLEDs() nur aufgerufen wenn mindestens ein Button dirty war – 600 µs Blockzeit werden so vermieden wenn keine Änderung nötig ist.
Warum kein DMA? DMA + SERCOM-SPI würde ~1,5 KB extra RAM (1440 Byte Kodier-Puffer) und erhebliche Implementierungskomplexität erfordern. Bei 20 LEDs und ~20 ms Loop-Rate sind 600 µs gesperrte Interrupts (= 3 % der Loop-Zeit) unkritisch.
2-Schicht-Modell (CButton)
Jeder MX-Button hat zwei LED-Schichten:
override (aktiv wenn set_override() aufgerufen) ← temporär (GUI-Benachrichtigungen)
base (Idle-Farbe aus NVM) ← dauerhaft
Aktive Farbe = override wenn aktiv, sonst base. clear_override() kehrt sofort zu base zurück, ohne base zu verändern.
Animationen
| Animation | Typ | Verhalten | Endbedingung |
|---|---|---|---|
STATIC |
— | Feste Farbe | — |
BLINK |
Helligkeit | An/Aus, period_ms = Halbperiode |
endlos |
PULSE |
Helligkeit | Lineares Dreieck 0→255→0 | endlos |
FADE_IN |
Helligkeit | Einmalig schwarz → voll | → STATIC (voll) |
FADE_OUT |
Helligkeit | Einmalig voll → schwarz | → STATIC (base=schwarz) |
COLOR_CYCLE |
Farbe | Hue-Sweep, ignoriert base/override | endlos |
COLOR_FADE |
Farbe | Crossfade from→to | → STATIC (base=to) |
Helligkeits-Animationen (compute_scale): Multiplizieren die aktive Farbe mit einem Skalierungsfaktor 0–255. Formel: (channel * scale) / 255.
Farb-Animationen (compute_rgb): Berechnen RGB direkt; base/override werden nicht verändert (außer bei Abschluss).
COLOR_CYCLE – Hue-Arithmetik (kein Float)
Hue 0–255 aufgeteilt in 6 Segmente à 43 Einheiten. Innerhalb jedes Segments steigt/fällt ein Kanal linear:
Seg 0: R=255, G steigt (Rot → Gelb)
Seg 1: R fällt, G=255 (Gelb → Grün)
Seg 2: G=255, B steigt (Grün → Cyan)
Seg 3: G fällt, B=255 (Cyan → Blau)
Seg 4: B=255, R steigt (Blau → Magenta)
Seg 5: R=255, B fällt (Magenta → Rot)
Ausgabe wird auf 40 % Helligkeit skaliert (Faktor 102/255) damit die LEDs nicht blenden.
Adafruit_NeoPixel::ColorHSV() ist nicht nutzbar: verwendet intern float (kein FPU auf M0+).
Phasenversatz (Regenbogen-Wellen)
set_anim(COLOR_CYCLE, period, phase_offset_ms): m_anim_start_ms = millis() - phase_offset_ms. Die 20 MX-Buttons werden in init_buttons() gleichmäßig phasenverschoben initialisiert:
uint16_t phase = (mx_idx * period) / 20;
Render-Pipeline
CMainController::updateLEDs()
für jeden CButton:
CButton::render_led()
if !dirty && !is_animating(): return false
rgb = compute_rgb()
ws2812_set(led_index, rgb.r, rgb.g, rgb.b)
dirty = false
return true
if any returned true:
ws2812_show()
dirty wird gesetzt bei: init(), set_base(), set_override(), clear_override(), set_anim(), clear_anim(), Animations-Abschluss.