Hardware-Encoder für NeoPixel (WS2812)/Einleitung: Unterschied zwischen den Versionen

Aus Hackerspace Bielefeld Wiki
Zur Navigation springen Zur Suche springen
K (Bild eingefügt)
Zeile 2: Zeile 2:


== Das Grobkonzept ==
== Das Grobkonzept ==
Wir schreiben einen WS2812-Encoder-Modul, statten es mit einem UART und einer SPI-Schnittstelle aus und verbinden das Ganze über einen kleinen Steuerautomaten. Daraus erzeugen wir eine Konfiguration für ein FPGA und sind fertig.
Wir schreiben einen WS2812-Encoder-Modul, statten es mit einem UART und einer SPI-Schnittstelle aus und verbinden das Ganze über einen kleinen Steuerautomaten. Daraus erzeugen wir eine Konfiguration für ein FPGA und sind fertig. Soweit die Theorie...


[[Datei:WS_Encoder_top.png|right|thumb]]
[[Datei:WS_Encoder_top.png|right|thumb]]

Version vom 7. Mai 2017, 16:49 Uhr

Zurück zum Inhaltsverzeichnis | Weiter zu Hardware ->

Das Grobkonzept

Wir schreiben einen WS2812-Encoder-Modul, statten es mit einem UART und einer SPI-Schnittstelle aus und verbinden das Ganze über einen kleinen Steuerautomaten. Daraus erzeugen wir eine Konfiguration für ein FPGA und sind fertig. Soweit die Theorie...

Wofür das Ganze?

Wozu nun dieser Aufwand, es gibt doch bereits fertige Software-Bibliotheken?

Nachteile von Softwarelösungen:

  • Die Timings des WS-Protokolls sind sehr eng und dadurch schwierig auf einem Mikrocontroller einzuhalten.
  • Wegen der Timings können keine Interrupts während der Ausgabe auf die LEDs benutzt werden.
  • Hardware-Timer mit entsprechenden Routinen sind bereits zu ungenau bzw. zu langsam (durch Overhead).

Vorteile der Hardwarelösung:

  • Das Protokoll ist nicht sehr kompliziert, dadurch hat man ein relativ gut durchschaubares Beispiel für die Beschäftigung mit VHDL.
  • 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öchstwertige 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 und damit ca. 17 m bzw 8,5 m gesamtlänge der Streifen.

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