PIC2WS2812 Steuerung von WS2812-LEDs mit 8-Bit PIC Microcontroller
Grundlagen
PIC Lösung
Software-Module:
Basis
Patternprozessor
Patternmover
Microstep
Timer
Demo
Defines

Die Basis Routinen

Die Basis-Software enthält den Startup-Programmcode, der die verwendeten Hardware-Register und das RAM initialisiert, die Interrupt-Routine (ISR) und ein paar Service-Unterprogramme. Die ISR sendet die Daten aus dem RAM-Pufferspeicher an die LEDs, nachdem das Hauptprogramm die LED-Daten errechnet und in den Pufferspeicher geschrieben hat.

Am Ende der Datei "PIC_2_WS281x.asm" befindet sich eine einfache Muster-Anwendung (Label "user_main") für das Basis-Modul, die aktiviert wird, falls das Demo-Modul nicht eingebunden wird. Es handelt sich dabei um einen simplen seriell-zu-WS2812 "Protokoll-Übersetzer", der Daten mit 1 MBit/s über UART-Rx empfängt und in das FIFO schreibt, um sie dann im WS2812-Protokoll an die LEDs zu senden.

Der Sendepuffer

Der Pufferspeicher für die LED-Daten kann entweder als Pixelspeicher oder als FIFO genutzt werden. Beim Pixelspeicher ist jeder LED eine feste Speicheradresse zugeordnet, so daß die Anwendung jederzeit und beliebig die Daten für jede einzelne LED ändern kann, ohne eine bestimmte Reihenfolge einhalten zu müssen. Allerdings kann so nur eine begrenzte Anzahl von LEDs gesteuert werden (Anzahl RGB-LEDs = Größe des Pufferspeichers/3). Da beim PIC12F1840 maximal 240 Bytes RAM für den Puffer zur Verfügung stehen, können so bis zu 80 RGB-LEDs angesteuert werden.
Beim FIFO (First In/First Out) werden Daten kontinuierlich aus dem FIFO an die LEDs gesendet, während die Anwendung die Daten im FIFO immer wieder auffüllt. So sind praktisch beliebig viele LEDs ansteuerbar, allerdings kann die Anwendung nicht wahllos beliebige LEDs ansprechen, sondern muß die Daten in der gleichen Reihenfolge in den Puffer schreiben, wie sie an die LEDs ausgegeben werden. Der Puffer dient hier also nur zur zeitlichen Entkoppelung zwischen Berechnung und Ausgabe der Daten. Dadurch darf die Berechnung der Daten für einzelne LEDs auch ruhig mal etwas länger dauern, ohne daß eine "Lücke" im Ausgabe-Datenstrom entsteht. Im Durchschnitt müssen nur ausreichend schnell Daten nachgeliefert werden, damit der Puffer nie ganz leer wird.
Für die Funktion des Pufferspeichers werden zwei Variablen benutzt:
ptr_rd = Lesezeiger, zeigt auf das Datenbyte, das als nächstes durch die ISR ausgegeben wird.
ptr_wr = Schreibzeiger (nur bei FIFO!), zeigt auf die Speicherzelle, die als nächstes durch das Hauptprogramm  beschrieben wird. 
Die ISR gibt solange Daten aus dem Puffer aus, bis der Puffer leer ist (ptr_rd == ptr_wr).
Für den Zugriff auf den Sendepuffer wird FSR0 mit "Linear Data Memory"-Adressierung benutzt. Dazu muss FSR0H permanent den Wert 0x20 enthalten! Die ISR verlässt sich darauf, daß FSR0H nicht im Hauptprogramm verändert wird.

Puffer als LED-Pixelspeicher

In diesem Fall muß ptr_wr den Wert 0 enthalten. Zum Senden des Pufferinhalts wird ptr_rd auf 0 gestellt und die Übertragung gestartet (durch "call start_tx"). Die ISR gibt alle Daten aus, bis das Pufferende erreicht ist und erzeugt dann einen ca. 50µs langen Low-Pegel am Ausgang. Wenn das ATXEN-Bit in der Variablen flags gesetzt ist, wird anschließend durch die ISR die Ausgabe automatisch erneut gestartet, so daß sich die Anwender-Software nicht mehr weiter um die Übertragung kümmern muss.
Die Variable bufsize bestimmt die Anzahl der Datenbytes, die in einem Zyklus gesendet werden.
Die Unterprogramme send2buf, send_triple und chkbuffer sind für Pixelspeicher-Betrieb nicht geeignet!

Puffer als FIFO-Speicher

Hier gibt die ISR solange Daten aus, bis die "Puffer leer"-Bedingung (ptr_rd == ptr_wr) erfüllt ist. Dann bleibt der Ausgabe-Port für 50µs auf low. Der automatische Neustart ist im FIFO-Mode nicht vorgesehen, daher muss das Bit flags.ATXEN hier auf 0 bleiben!
Die Anwendersoftware muss ausreichend schnell Daten in das FIFO schreiben, um zu vermeiden, daß das FIFO leerläuft und die Übertragung vorzeitig beendet wird.
Nachdem die Daten für alle LEDs ins FIFO geschrieben wurden, lässt das Hauptprogramm das FIFO leerlaufen, um die 50µs Pause am Ende der Übertragung zu erzwingen. Bei Ausgabe des letzten Bytes aus dem Puffer setzt die ISR das Statusbit flags.TXRUN zurück, was vom Hauptprogramm als Indikator verwendet werden kann. Sobald flags.TXRUN zurückgesetzt wurde, können wieder neue Daten ins FIFO geschrieben und danach die Übertragung durch Aufruf von start_tx erneut ausgelöst werden. In den meisten Fällen ist es jedoch zweckmäßig, die Ausgabe erst zu starten, wenn das FIFO ganz voll ist. Die chkbuffer-Routine  erledigt das ggf. automatisch.

Unterprogramme:

start_tx: startet den Sendevorgang. Ausgabe der Daten aus dem Puffer ab [ptr_rd].

send2buf: schreibt Daten (W) ins FIFO. Vorsicht! Prüft nicht auf Pufferüberlauf (Ggf. vorher chkbuffer aufrufen)!

send_triple: schreibt Daten für eine RGB-LED (3 Bytes) in das FIFO. Wartet ggf., bis genügend Platz dafür im FIFO frei ist.
Quelldaten: [FSR1]. FSR1 wird dabei um 3 erhöht. Die Ausgabe wird gestartet, falls sie noch nicht aktiv ist.
Die Variable led_idx wird um 1 erhöht. Das Bit flags.EOCHAIN wird auf 1 gesetzt, wenn led_idx den Wert CHAINLENGTH erreicht hat, d.h. die Daten für alle LEDs ins FIFO geschrieben wurden.
Für eine korrekte Funktion muss ptr_wr durch 3 teilbar sein! Das ist automatisch der Fall, wenn die Daten immer nur in 3er-Gruppen (also jeweils für eine RGB-LED) bearbeitet werden.

chkbuffer: Wartet ggf., bis genug Platz für 3 Bytes im FIFO ist. Falls das FIFO voll ist und die Ausgabe noch nicht gestartet wurde, wird sie gestartet.

sys_tm_adj: sorgt für korrekten Inhalt der Systemzeit-Variablen (sys_time, zählt Millisekunden). Kann beliebig oft aufgerufen werden, muss jedoch spätestens 4 ms nach dem letzten Aufruf erneut aufgerufen werden. Routinen, die auf irgendwas warten (z.B. chkbuffer) haben sys_tm_adj-Aufrufe eingebaut, so daß meist ein separater Aufruf nicht mehr notwendig ist.

Variablen/RAM:

(Sendepuffer): beginnt bei Adresse 0x20 in Bank 0, bankübergreifend bis max. 0x6F in oberer RAM-Bank, Länge maximal 240 Bytes.

Common RAM, ab 0x70:

flags: verschiedene Status- und Steuerbits
ptr_rd, ptr_wr: für Lesen/Schreiben von Sendepuffer.
bufsize: Größe des Sendepuffers. Wird bei der Initialisierung automatisch auf maximale Größe oder auf FIFOSIZE gesetzt.
temp: allgemeiner Zwischenspeicher
sys_time: 16-Bit Millisekunden-Zähler
sys_ts: (wird intern von sys_tm_adj verwendet)
w16: allgemeines 16-Bit Arbeitsregister, wird nicht im Basis-Modul verwendet!
led_idx: 16-Bit LED-Nummer
(Sonstige): nicht im Basis-Modul verwendet!


Weiter geht's mit dem Pattern-Prozessor.


Kontakt