AVR ASM ws2811 / ws2812 Ansteuerung mit FastPWM

#2968694
Lesenswert?

Hi,

anbei mal meine Version der ws2811 (Controller) / ws2812 (RGB LED mit 
eingebautem Controller) Ansteuerung im schnellen 800KHz Modus.

Ich habe FastPWM mit dem Timer 0 verwendet. Getestet mit einem ATtiny45 
bei 16MHz.

Es gibt zwei Versionen der Ausgabefunktion, mit/ohne Gammakorrektur per 
Lookuptabelle.

Das Timing für die High-und Lowzeiten des seriellen Protokolls kann 
durch ändern der entsprechenden Definitionen (in Grenzen) geändert 
werden.

Files:

ws2812.h : Headerfile
ws2812.s : ASM Source
ws2812test.c : Kleines C Testprogramm
gammacorrection.h : Ein paar Makros für Gammakorrektur
asmdefs.h : Ein paar nützliche Makros...

(Build mit Atmel Studio 6)

Grüße
Markus
Angehängte Dateien:
#3257246
Lesenswert?

Hi,

ich bin seit gestern auch im Besitz von WS2811 LED Stripes. Habe jetzt 
mal 1m an einen Atmega88-20PU angeschlossen und verwende einen 20MHz 
Quarz, das ganze steckt auf dem Pollin AVR Evaluationsboard.
Ausgabeport ist an die Stripes angeschlossen, und GND vom 
Evaluationsboard mit GND des Stripes verbunden.

nach einigem Hin und her habe ich nun die Fuses auf "Ext. Full Swing 
Crystal, 16K CK / 14CK + 0ms" gesetzt. Stimmt das?

Die 2 Compiler Flags im AVR Studio (das ich heute installiert habe) 
stehen auf "F_CPU=20000000UL" bzw. "-DF_CPU=20000000" (Hinweis: In der 
Readme steht einmal DF_CPU und einmal DC_CPU, ich denke letzteres ist 
ein Fehler? )

Ich weiß nicht wie das Ergebnis aussehen soll, aber ich glaube bei mir 
ist es nicht richtig :)
Beim Programmstart läuft schwaches Rot von LED 1 bis ca. 20-25, sort 
flackert es herum, danach ist dunkel. Habe 400000 und 800000 als stripe 
timing getestet, gleiches Ergebnis?!

Ich habe noch nie mit dem AVR Studio gearbeitet, und auch wenig Ahnung 
von dem ASM dort. Habe nur mal etwas MIPS gelernt aber das ist lange 
her.

Daher fällt mir die Fehlersuche gerade etwas schwer, über einen Tipp 
würde ich mich sehr freuen :)
Vielleicht wie ich das testprogramm dazu bewege einfach mal alles "grün" 
zu machen oder so. Ich habe hier RAND durch 255 bei Grün ersetzt, aber 
das hatte nicht den gewünschten Effekt. Denke es gibt hier zu viele 
Variablen für mich an denen es liegen könnte:)

Viele Grüße,

CC1337
#3294354
Lesenswert?

Hallo Alle.

Gaebe es von eurer Seite aus Bedarf fuer eine WS2812 Ansteuerung, die 
lediglich den internen 8MHz benoetigen wuerde?

Ich haette eine Roh-Implementierung und wuerde gern wissen ob sich 
aufraeumen lohnt, ob es jemand anderes gebrauchen koennte?

Klarer Vorteil duerfte wohl sein, das man preiswerte "ATmegas" ohne 
grossartig externe Komponenten verwenden koennte...


MfG
#3294828
Lesenswert?

@Axel: Ja genau. Das ist aber willkürlich, man kann natürlich auch eine 
eigene Tabelle für die Gammakorrektur mit 8 Bit -> 8 Bit verwenden.

Mit den 256 Stufen die die WS2812 anbieten wird das aber nicht viel 
bringen. Gerade im unteren Helligkeitsbereich ist ja die Abstufung 
kritisch, und da nützen einem die 8 Bit dann auch nichts.

So hat man das Bit 7 dann noch frei um irgendwas für die interne 
Verarbeitung zu signalisieren, z.B. Effekte auf "Pixelbasis" 
an/ausschalten etc.

Grüße
Markus
#3294845
Lesenswert?

Hi Stephan,

soweit ich weiß gibt es da ja schon Lösungen mit "Bit Banging".

Meine Intention hinter der Lösung war ein möglichst präzises Timing 
durch verwenden der FastPWM bzw. sogar eine Ausgabe im "Hintergrund" 
durch verwenden von DMA des XMega.

In Sachen "Sparsamkeit" ist die Methode zu Fuß sicherlich am 
effektivsten. Wirklich cool wäre Code, der sich der entsprechenden 
CPU-Frequenz (F_CPU) anpasst, also dann per Makro o.ä. zusätzliche NOPs 
einfügt, damit auch bei anderer Frequenz das Timing stimmt (gibt es aber 
vlt. auch schon?)

Grüße
Markus
#3294866
Lesenswert?

Markus M. schrieb:
> Wirklich cool wäre Code, der sich der entsprechenden
> CPU-Frequenz (F_CPU) anpasst, also dann per Makro o.ä. zusätzliche NOPs
> einfügt, damit auch bei anderer Frequenz das Timing stimmt

Hallo Markus M.

Ja genau so ist es geplant.
Per "#if F_CPU" werden entsprechende Header included.

Derzeit werden 8MHz und 16MHz unterstuetzt.
Die Timings sind dabei (Annahme: clock stabil) bis auf 0.05 uS genau.

Das bei 8MHz zu erreichen ist nicht ganz einfach. Ich musste dazu eine 
Funktion schreiben die ein RGB-Array in eine spezielle Bytecodesquenz 
konvertiert. Spezielle im Flash aligned Funktionen nutzen dann die 
unteren 8Bit des Z-Registers um per "ijump" den Bytecode zu 
interpretieren...
..die Funktionen selbst geizen dann um jeden cycle ;-)
(z.B. wird "out" mit 2 verschiedenen Registern benutzt um "sbi" zu 
vermeiden)

Aber andere Loesungen zu sehen ist immer wieder interessant...

MfG Stephan
#3296444
Lesenswert?

>
> In Sachen "Sparsamkeit" ist die Methode zu Fuß sicherlich am
> effektivsten. Wirklich cool wäre Code, der sich der entsprechenden
> CPU-Frequenz (F_CPU) anpasst, also dann per Makro o.ä. zusätzliche NOPs
> einfügt, damit auch bei anderer Frequenz das Timing stimmt (gibt es aber
> vlt. auch schon?)

Das kann meine Library für AVR von 4-20MHz. In der ARM-Version wird die 
Anzahl der notwendigen NOPS sogar während die compilierung berechnet, so 
dass optimales Timing von 8-60MHz erreicht wird. Wenn ich mal Zeit 
finde, werde ich dieses auch noch für die AVR-Variante umsetzen.

Code hier:

https://github.com/cpldcpu/light_ws2812

Exakteres Timing scheint für die WS2812 nicht notwendig zu sein. Ich 
vermute dass eine Implementierung per PWM und SPI hier keine Vorteile 
bringt, insbesondere da die benötigt CPU-Zeit ja die gleiche ist. Aber 
es ist halt etwas interesssanter :)
#3296445
Lesenswert?

Stephan B. schrieb:
> Das bei 8MHz zu erreichen ist nicht ganz einfach. Ich musste dazu eine
> Funktion schreiben die ein RGB-Array in eine spezielle Bytecodesquenz
> konvertiert. Spezielle im Flash aligned Funktionen nutzen dann die
> unteren 8Bit des Z-Registers um per "ijump" den Bytecode zu
> interpretieren...

Hm... also 8 Mhz ist kein Problem. 4 Mhz geht auch. Dafür muss man 
keinen extremen Aufwand betreiben.
#3296560
Lesenswert?

Tim .  schrieb:
> m... also 8 Mhz ist kein Problem. 4 Mhz geht auch. Dafür muss man
> keinen extremen Aufwand betreiben.

Vielen Dank Tim.

Das wundert mich jetzt auch ;-). Ich werde mir definitiv mal deinen Code 
detaillierter ansehen.

Auf den ersten Blick wuerde ich aber sagen: Mein Code kann das Array 
immer wieder periodisch (wie einen 1D-Torus) auslesen und so auch mit 
kleineren Arrays mehr LEDs (mehr als 256) ansteuern.

Bei deiner Lib ist alles 8-Bit Code und laeuft daher schneller...

...ich kann ja meinen bisherigen Code demnaechst einmal posten damit ein 
besserer Vergleich moeglich ist.
Fuer manche Strips koennte ich mir aber durchaus mehr als 256 LED von 
praktischer Relevanz vorstellen.

MfG Stephan
#3296571
Lesenswert?

Ich verstehe leider nicht alle von Deinen Anmerkungen, da ich Deinen 
Code nicht sehen kann :)

>Auf den ersten Blick wuerde ich aber sagen: Mein Code kann das Array
>immer wieder periodisch (wie einen 1D-Torus) auslesen und so auch mit
>kleineren Arrays mehr LEDs (mehr als 256) ansteuern.

Das Protokoll reagiert auf kurze Pausen zwischen den LEDs relativ 
tolerant. Die Daten werden erst nach der "Reset"-Bedingung übernommen. 
Es ist daher kein Problem, zwischendruch den Array-Pointer neu zu 
initialisieren und so mit einem kleinen Array lange Strings zu 
beschreiben.
#3296584
Lesenswert?

Tim .  schrieb:
> Ich verstehe leider nicht alle von Deinen Anmerkungen, da ich Deinen
> Code nicht sehen kann :)

Hier die Vorabversion meines Codes, welcher sicherlich noch aufgeraeumt 
werden muss...

Ich gebe zu, das sogesehen ueber eine RGB-Sequenz hinaus, ein 100% 
korrektes Timing nicht erforderlich ist...

...ggf. nuetzt es ja dennoch jemanden...

MfG Stephan
Angehängte Dateien:
#3334713
Lesenswert?

Es sind einfach fragen wie:
Brauch ich ein externen Quarz, und wo dieser dann angeschlossen wird?
Brauche ich für jede LED die ich mit einer anderen Farbe ansteuern will 
ich ein neuen PWM "Pin"?
-->Kann ich aus anderer Pins durch Sw. zu PWM Pins machen?
Wenn ich sie Parallel schalten will(wie in der PDF) sind  r1 und c1 
vorhanden, welche werte brauche ich da oder gibt es einen besseren Weg?
Ist R1 der vorwiederstand welcher die LED schützt?

(AW: Habe den avr Dragon als Programme)

Vielen dank für die schnellen Antworten!
MfG Marcel
#3334786
Lesenswert?

Hallo

Marcel Jung schrieb:
> Brauch ich ein externen Quarz
Bis 8MHz nein. Wenn man zusaetzlich die interne Taktkalibrierung benutzt 
(OSCCAL), sollten die Timings der WS2812 kein Problem sein.

Marcel Jung schrieb:
> Brauche ich für jede LED die ich mit einer anderen Farbe ansteuern will
> ich ein neuen PWM "Pin"?

Nein, bitte lese das Datenblatt! Es ist ja gerade die Besonderheit da 
WS2812 kaskadiert werden kann. Egal wie viele man ansteuern will, man 
brauch nur eine Datenleitung!

Marcel Jung schrieb:
> -->Kann ich aus anderer Pins durch Sw. zu PWM Pins machen?

Je nach Implementierung des WS2812 Ansteuerung reicht sowieso ein 
gewoehnlicer GPIO aus. (z.B. fuer meinen Code: 
Beitrag "Re: AVR ASM ws2811 / ws2812 Ansteuerung mit FastPWM" )

Marcel Jung schrieb:
> Wenn ich sie Parallel schalten will(wie in der PDF) sind  r1 und c1
> vorhanden, welche werte brauche ich da oder gibt es einen besseren Weg?
> Ist R1 der vorwiederstand welcher die LED schützt?

WS2812 braucht im 5V Betrieb keinen Vorwiderstand (sie hate einen 
eingebauten Konstantstromtreiber). Jede WS2812 LED braucht laut 
Datenblatt ihren eigenen 0.1uF Kondensator moeglichst nahe bei der LED 
zwischen VCC und GND.


MfG
#3359035
Lesenswert?

Marcel Jung schrieb:
> Leider verstehe ich den Code von oben nicht ganz

Hallo Marcel

Ein zur WS2812 gesendeter Bitwert wird durch die Zeit eines Wechsels vom 
5V-Pegel zum GND-Pegel codiert. Wenn keine Daten uebertragen werden 
liegt die Signalleitung auf GND.

0: 0.4us 5V und dann Wechsel zu GND fuer min. 0.85us
1: 0.8us 5V und dann Wechsel zu GND fuer min. 0.45us

(Ein Bit dauert also 1.25us)

Es werden immer 24Bit von einer LED gespeichert und alle darauffolgenden 
Bits an die naechste LED weitergeleitet. (Die davon auch wieder 24Bit 
fuer sich speichert und dann weiterleitet...)

Wenn laenger als 50us die Sinalleitung auf GND liegt - somit nichts 
weiter uebertragen wurde - werden die zuvor gesendeten Daten von allen 
LEDs uebernommen und die entsprechende Farbe angezeigt.

Eine neue Uebertragung startet dann wieder bei der ersten LED welche 24 
Bits fuer sich speichert bevor sie wieder weiterleitet...

Da diese Zeiten relativ genau eingehalten werden muessen, gibt es 
verschiedene Moeglichkeiten diese Pegel mit einem AVR zu generieren.
Die oben eingesetzte Möglichkeit ist via GPIO mittels Assemblercode.

Bei kalibrierten 8MHz entspricht der Uebertragungsdauer von einem Bit 
(1.25 us) also genau 10 Takten. 10 Takte in denen man zwischen GND zu 5V 
mit einer Zeitaufloesung von theoretisch einem Takt den GPIO umschalten 
muss. Da die Maschienenbefehle vom AVR fest definierte 
Ausfuehrungszeiten haben (mache 1 Takte, manche mehr) verwenet man 
ueblicherweise Assemblercode um die Zeiten indirekt (ueber die Wahl des 
Befehls) genau zu definieren.

MfG
#3427480
Lesenswert?

Hi
Tolle Eagle-Library! Kann man wirklich gut gebrauchen danke!!!
Noch eine Frage: Ich hab irgendwie einen Denkfehler also wollte ich den 
Code von Oben anschauen.... Welchen brauche ich da? --> Ich brauche 
dringend ein Erfolgserlebnis;)
Danke für die Library --> Ich bin gerade am entwickeln

MfG
#3450435
Lesenswert?

Ich habe einen touch Panel, welcher über ein Atmega32 die WS2812 LED 
ansteuern sollte(Farbe usw). Da dieser Ansatz zur Zeit nicht läuft, habe 
ich es erst einmal über Poti oder einen Taster zu versuchen. Leider geht 
das auch nicht! Ich glaube also, dass ein fehler in meinem Code ist. Um 
diesen auszubessern bzw einen neuen code zu erstellen wollte ich ein 
beispiel.
Vielen dank für die genommene Zeit.
MfG
Gast #3568416
Lesenswert?

Hallo Markus,

ich habe gerade mal deine WS2812 lib auf meinem XMega32A4 testen wollen, 
bekomme aber leider kein gültiges Datensignal aus meinem Port raus, 
komischerweise nur konstante 1,6V statt der gewünschten 5V.
Ich shifte die datenlevel mit einem MAX3002 auf 5V hoch, bei normalen 
Triggern der Ports liegt auch ein 5V level an.
Muss in deinem ASM Source noch etwas für den xm32a4 angepasst werden?
Bin nicht so ASM affin so das ich die anpassungen leider nicht selber 
vornehmen könnte falls welche nötig sein sollten.

Eventuell hast du oder jemand anderes ja eine Idee wodran es liegen 
könnte.


Gruß Michael
#3570936
Lesenswert?

Port D0... D.h. Du hast statt dem TCC0 Timer den TCD0 Timer verwendet?

Im ws2812.h im Abschnitt zum "WS_ARCH_XMEGA" müssten folgende Änderungen 
gemacht sein:

#define     WS_TCC          TCD0        // Timer/Counter to use
...
#define     WS_EVSYS_MUXVAL 0b11010000  // Event selection == TCC0OVF
...
#define     WS_OUTPORT      PORTD       // output port
...

Grüße
Markus
#3571217
Lesenswert?

Habs nun nochmal mit deinen Angaben probiert (Bin noch nicht so fit in 
den xMega Registern) aber bekomme immernoch dasselbe Ergebnis.
Nur ein Maximallevel von 1,7V auf den Datenleitungen. Bekomme zwar ein 
Signal am Oszilloskop angezeigt, wird aber immer überlagert durch 
langatmige Noises.
Versuche nun mit nem DSO mal rauszufinden was das sein könnte.
#3571232
Lesenswert?

...Du könntest ja als ganz einfachen Test anstatt der LED-Daten erstmal 
programmatisch ein Rechteck-Signal ausgeben.

Also nach der Initialisierung mit WS_Init() im C-Code dann sowas wie:

while(1) {
   PORTD.OUT = 0xff;
   PORTD.OUT = 0x00;
};

Hier ist übrigens ein ganz nettes (wie ich finde) Tutorial zum XMega:

http://www.stromflo.de/dokuwiki/doku.php?id=xmega-c-tutorial

Grüße
Markus
#3572647
Lesenswert?

So ich habe nun nochmal alles Hardwaretechnisch überarbeitet, der 
Levelshifter funktioniert soweit nun auch und deine Library insoweit 
auch.
Nur hab ich noch 2 kleinere Probleme. Und zwar laeuft die Library nicht 
wenn ich mit dem PLL arbeite wie in deinem Beispiel auf 32MHz 
verdopplet.
Nur wenn ich den normalen 16MHz Quarztakt nehme geht die Library.
Das andere ist das wenn ich Farbwerte über 200 oder auch 255 nehme wird 
statt der vollen Helligkeit der Farbe eine sehr sehr abgeschwächte 
Helligkeit angezeigt. Liegt das an der Gammatable?
Edit: Und bei mehr als 32 bzw 32 LEDS bleibt es haengen
#3575436
Lesenswert?

Hi,

zu den Problemen...

TAKT

das mit dem Takt ist merkwürdig. Hast Du auch F_CPU im Makefile bzw. im 
AtmelStudio Projekt an den zwei(!) Stellen entsprechend auf 32000000 
gesetzt dann?

GAMMA TABELLE

Das mit der Gammatabelle ist korrekt. Die Tabelle ist nur für 7 Bit 
(0...127) ausgelegt bzw. berechnet. Wobei 127 dann auf die max. 
Helligkeit 255 übersetzt wird. Größere Werte zu übergeben führt also 
definitiv zu falschen Helligkeiten bzw. den von Dir beschriebenen 
Effekt.

Du kannst natürlich auch auf die Helligkeitsnpassung verzichten (oder 
selbst eine Tabelle berechnen...) und mit 8 Bits arbeiten. Ich habe sie 
nur für 7 Bit berechnet, da die 8-Bit PWM von den LEDs kaum die Dynamik 
hergibt, so dass sich eine 8 Bit Tabelle lohnen würde.

LED ANZAHL

Ich habe den Code mit 320 LEDs getestet, es sollte also grundsätzlich 
funktionieren. Hast Du es mit dem Beispielcode getestet?

Grüße
Markus
#3587655
Lesenswert?

Hi,

sorry für die späte Antwort, aber nun geht es.
Nun hab ich mal die Frage ob man das irgendwie so implementieren kann 
das ich auch den Ausgabeport bzw das man evtl mehrere Ausgabeports 
definieren kann.
Ich habe auf meiner Platine 4 Ausgaenge die ich nutzen wollte und daher 
auch auf den xMEGA gesetzt habe.
Da deine Routine aber auf Double Buffering aufbaut wird wahrscheinlich 
mit deiner Routine max. 2 Ausgaenge möglich sein oder?

Michael
#3596571
Lesenswert?

Hi,

also mit 4 Ausgängen ist das so erst einmal nicht möglich, eben wg. dem 
Double-Buffering.

Es sollte gehen, dass man das ganze für zwei Ausgänge programmiert. Dazu 
müsste man den Code aber quasi einmal duplizieren und dann entsprechend 
die anderen beiden DMA-Kanäle, anderen Timer etc. nehmen.

Wenn man ganz viel Speicher übrig hat, könnte man natürlich die 
RGB-Daten komplett im voraus in die Timerwerte umrechnen, dann kann 
der DMA vom Anfang bis zum Ende durchlaufen. Die Programmierung dafür 
wäre recht einfach:

1. RGB Daten in Timerwerte umrechnen und in Buffer schreiben
2. Timer und DMA entsprechend konfigurieren
3. Timer und DMA starten

Aber dafür benötigt man Anzahl_LEDS x 3 x 8 Bytes im Speicher. Für 1000 
LEDs also dann z.B. 24 kByte.

Dafür ist die Lösung natürlich recht elegant und benötigt überhaupt 
keine CPU-Zeit mehr während der Ausgabe (ein paar Zyklen für die 
Speicherzugriffe).

Ciao...
Markus
Gast #3713564
Lesenswert?

Hi Markus,

entschuldige die späte Antwort aber ich war durch das Tagesgeschaeft 
etwas ausgelastet :)
Das Problem ist nur das ich mich mit den xMega's eigentlich so gut wie 
garnicht auskenne, hab zwar viel mit ATMega's und PICs gemacht aber mit 
xMEGA und den extremen aenderungen und der vielen peripherie bin ich 
etwas überfordert ;P
Evtl hast du ja einen kleinen Ansatz für mich auf dem ich aufbauen kann

Gruß
Michael
Gast #3788496
Lesenswert?

Hi Markus,

ich habs nun nach langer Zeit nochmal versucht deine Routine auf 2 
Kanäle umzubauen aber irgendwie bin ich bisher nicht so weit gekommen 
das ich eine Ausgabe bekomme. Meine Assemblerkenntnisse sind leider auch 
immernoch nicht besser geworden :(

Ich hab mal den Source von der ASM File hochgeladen. Evtl siehst du ja 
wo ich meinen Fehler habe das der Kram nicht geht. Der eine Kanal geht 
ja wunderbar auch auf Port D0....

http://pastebin.com/rTx4Rd8s

Vielen Dank schonmal

Gruß Michael

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren