89 lines
3.4 KiB
Markdown
89 lines
3.4 KiB
Markdown
# 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:
|
||
|
||
```cpp
|
||
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.
|