Hardware-Encoder für NeoPixel (WS2812)/Einleitung: Unterschied zwischen den Versionen
Admin (Diskussion | Beiträge) (Die Seite wurde neu angelegt: „Zurück zum Inhaltsverzeichnis | Projekt:Hardware-Encoder für NeoPixel (WS2812)/Hardware| Weiter zu Hard…“) |
Admin (Diskussion | Beiträge) K (Admin verschob die Seite Projekt:Hardware-Encoder für NeoPixel (WS2812)/Einleitung nach Hardware-Encoder für NeoPixel (WS2812)/Einleitung, ohne dabei eine Weiterleitung anzulegen) |
(kein Unterschied)
|
Version vom 4. April 2017, 22:40 Uhr
Zurück zum Inhaltsverzeichnis | Weiter zu Hardware ->
Das Grobkonzept
Wir nehmen einen FPGA, statten ihn mit einem UART und einer SPI-Schnittstelle aus, basteln einen Transmitter für das WS2812-Protokoll und hängen das Ganze mit einem kleinen Steuerautomaten aneinander.
Wofür das Ganze?
Wozu nun dieser Aufwand, es gibt doch bereits fertige Software-Bibliotheken? Aus mehreren Gründen:
- Das Protokoll ist nicht sehr kompliziert, also hat man ein relativ gut durchschaubares Beispiel für die Beschäftigung mit VHDL.
- Das Timing des Protokolls mit seinen 800kHz Bit-Takt ist schon wieder so eng, daß grade in kleineren µControllern, wie den ATMegas, ein sehr hoher Aufwand betrieben werden muss, um die Timings einzuhalten.
- Das bedeutet häufig, daß Interrupts abgeschaltet werden müssen.
- Timer mit entsprechenden Routinen sind bereits zu ungenau bzw. haben zu viel Overhead.
- Die Verwendung von Hardware-Schnittstellen wie SPI oder UART kann Interrupt-gesteuert ablaufen.
- Je nach Controller auch unter Verwendung von DMA.
- Die Entkopplung der LEDs vom Host ermöglicht, alles als Host zu benutzen, was eine serielle Schnittstelle hat: PCs, Raspberry Pi usw.
Der LED-Controller
Wie arbeitet dieser ominöse WS2812 jetzt eigentlich? Der WS2812 steuert eine RGB-LED an und zwar mit 8 Bit pro Farbe bzw. 24 Bit Farbtiefe pro LED. Die Reihenfolge der Farbbytes unterscheidet sich dabei vom gewohnten RGB: Die LEDs erwarten ein GRB-Format, also erst das Byte für Grün, dann Rot und zum Schluss Blau. Das höchstwertigste Bit wird zuerst übertragen.
Wenn mehrere NeoPixel hintereinander geschaltet werden, übernimmt der Controller der ersten LED in der Kette die ersten 24 Bit in sein Farbregister, alle weiteren Bits gibt er über seinen Datenausgang an den nächsten Pixel weiter. Angezeigt werden die Farben jedoch erst nach einem RESET-Signal.
Das Übertragungsprotokoll sieht so aus: Ein Bit wird innerhalb von 1,25µs übertragen. Innerhalb dieser Zeit geht die Signalleitung für eine gewisse Zeit auf High- und anschließend auf Low-Pegel. Für ein "1"-Bit sind es 0,8µs High und 0,45µS Low, für ein "0"-Bit 0,4µs High und 0,85µs Low. Pro Phase sind bis zu 0,15µs Abweichung erlaubt. Liegt die Signalleitung für mehr als 50µs auf Low, wird das als Reset-Signal interpretiert und die übertragenen Farbwerte werden angezeigt.
Hier gibt es das ausführliche Datenblatt[1].
Takte, Timings, LEDs
Ein paar Eckdaten sind noch zu klären:
- Wie viele LEDs kann ich (sinnvoll) hintereinander schalten?
- Wie knapp sind die Timings wirklich?
Rechnen wir mal ein bisschen:
- Für flüssig erscheinende Animationen brauchen wir mindestens 24 Bilder/Sekunde. Dabei flackert es aber noch sichtbar.
- Moderne Computermonitore arbeiten mit 60 Bildern/Sekunde.
In Anlehnung an Monitore werde ich auch hier von FPS (Frames Per Second - Bilder pro Sekunde) sprechen, wenn ich vollständige Übertragungszyklen zu den NeoPixeln meine, auch wenn das vielleicht nicht ganz korrekt ist.
Die erste Frage können wir damit schon mal beantworten: Technisch gesehen können wir beliebig viele LEDs hintereinander schalten, je mehr es werden, desto niedriger wird unsere Bildrate. Interessant ist also vielmehr, wie viele LEDs bei 30 oder 60 FPS hintereinander gehängt werden können. Betrachten wir das Ganze einmal für 60 FPS:
- Wir haben für einen Durchlauf 1/60s Zeit, also 16,7ms.
- Um einen Pixel mit 24 Bit zu übertragen, brauchen wir 24 * 1,25µs = 30µs.
- Pro Durchlauf kommt noch ein Reset mit 50µs dazu.
In eine Formel gegossen könnte das so aussehen:
- T_FRAME = x * T_LED + T_RESET.
Dabei ist x die gesuchte Anzahl der LEDs, T_LED = 30µs und T_RESET = 50µs. T_FRAME = 16,7ms ist die Zeitspanne für einen Durchlauf. Umgestellt nach x erhalten wir:
- x = (T_FRAME - T_RESET) / T_LED
Mit eingesetzten Werten:
- (16700µs - 50µs) / 30µs = 555 LEDs
Die nächstkleinere Zweierpotenz wären 512 LEDs. Halten wir also fest: Mit 512 LEDs erreichen wir unsere 60 FPS, 1024 LEDs können wir immer noch mit 30 FPS ansteuern. Zum Vergleich: Die gängigen LED-Streifen auf Rolle haben 30 oder 60 LEDs / Meter, das wären dann 150 bzw. 300 LEDs pro 5m-Rolle.
Bleiben noch die Timings
Wie knapp wird es denn jetzt wirklich? Nehmen wir als Beispiel einen ATMega mit 20 MHz Takt, wie er von den Arduinos bekannt ist. Diese Mikrocontroller benötigen für die meisten Befehle einen Takt, für Sprünge zwei.
- Bei 20 MHz dauert ein Takt 1 / 20.000.000 Hz = 0,000.000.050 s oder 50 ns.
- Die kürzeste Schaltdauer sind 400 ns (T0H).
- Diese Zeitspanne entspricht 400 ns / 50 ns = 8 Takten (+-3 Takte Toleranz).
- Ein ganzes Bit entspricht 25 Takten.
Das ist unter Einsatz von taktoptimiertem Assemblercode gerade noch machbar. Wenn wir mit höheren Takten arbeiten können, entspannt sich die ganze Situation ein wenig, allerdings nicht in einem Maße, als daß es uns von handoptimiertem Code erlösen würde.
20MHz 40MHz 60MHz 80MHz 100Mhz T1H 16 32 48 64 80 T1L 9 18 27 36 45 T0H 8 16 24 32 40 T0L 17 34 51 68 85 Sequenz 25 50 75 100 125 RST 1000 2000 3000 4000 5000