Forum: Mikrocontroller und Digitale Elektronik Lightweight WS2811/WS2812 Library


von Tim  . (cpldcpu)


Lesenswert?

Diese "Über-PWM" ist im Fadecandy implementiert:

https://www.adafruit.com/product/1689

Die Updates hängen natürlich am recht langsamen PWM-Timing, daher wird 
es dort zu verzögerungen kommen. Um sich das Verhalten der LED-Treiber 
genauer anzuschauen, könnte man sich über ein Oszilloskop den 
Eingangsstrom anschauen. Ist einfacher als die visuelle Beurteilung.

von Marco H. (damarco)


Lesenswert?

Das stimmt leider auch nicht ;) Jede Flanke die über >650ns glaube ich 
liegt wird als 1 erkannt.  Reset bei >9ns bis 50ns wenn keine Daten 
kommen.

Der Reset bewirkt im Treiber das dieser die nächsten Daten die kommen 
übernimmt. Nach 24bit rastet der Treiber ein und gibt die weiteren Daten 
am Out aus. Das spiel setzt sich unendlich fort, die Länge spielt keine 
Rolle. Natürlich nur für die Framerate, je länger die Kette um so 
geringer die Framerate.


Der IN wird vom Treiber gesampelt und ob eine 1 oder 0 erkannt wird ist 
nur davon abhängig wie oft der Treiber pro Sample am IN den gleichen 
Pegel einließt.

z.Bsp 3 sampels ist der Pegel 1 -> 0, 5 Samples Pegel 1-> 1.

Der Treiber generiert am OUT ein Signal was unabhängig ist vom IN, also 
von dessen Timing.

Es gab eine Seite die dies genau beschreibt! Ich finde diese leider 
nicht mehr auf Anhieb.

von Conny G. (conny_g)


Lesenswert?

c-hater schrieb:
> Wer Lust hat, kann ja mitknobeln, am Wochenende werde ich mal meine
> bisherigen Ergebnisse aufbereiten und hier posten. Ich werde dafür dann
> einen neuen Thread namens "The secret of WS2812B" eröffnen.

Es wäre ja nur logisch, dass jeder Pixel seine Daten mit einer kleinen 
Verzögerung weitergibt - vorne wird gesampelt, hinten wird rausgegeben.
Aber OUT hängt ja nicht direkt am IN, sondern braucht ein paar 
Taktzyklen, das Signal hinten wieder anzulegen.
Also hat zeitlich gesehen jede weitere LED einen anderen Offset zum 
Latch und übernimmt die Farbe mit etwas Verzögerung.
Wenn also gelatcht wird müsste es wie ein Lauflicht mit ein paar 
Nanosekunden von vorne nach hinten durchlaufen.
Und wenn man eine passende Frequenz der Ansteuerung drüberlegt hat man 
eine schöne Interferenz.

von Marco H. (damarco)


Lesenswert?

Genau ! Deswegen sollte man eine Schlange beim verlegen vermeiden wenn 
man Videos ausgeben will.

Jede Zeile muss dann auf einen extra Ausgang die synchron gestartet 
werden.

von c-hater (Gast)


Lesenswert?

Conny G. schrieb:

> Es wäre ja nur logisch, dass jeder Pixel seine Daten mit einer kleinen
> Verzögerung weitergibt

Ja klar. Das ist aber nicht das Problem, wie meine Versuche 
eindrucksvoll zeigen. Das Problem ist vielmehr, dass bei hoher Framerate 
(und die braucht man, wenn man die PWM erweitern will) die LEDs längst 
nicht mehr bei jedem Latchimpuls die empfangenen Daten in ihre 
PWM-Schaltung übernehmen, sondern sie lassen dabei immer mal wieder 
Frames aus.

Wohlgemerkt: die Daten vom Bus übernehmen sie durchaus in jedem Frame, 
aber man muss eine sehr überlange Latchphase (eine überlange Phase 
Dauer-High tut es übrigens genauso) ausgeben, um sie dazu zu zwingen, 
die vom Bus geholten Daten auch in ihre PWM-Ausgabe zu übernehmen. 
Würden sie das nicht tun, wäre ja auch die Burstansteuerung garnicht in 
der Form möglich, wie sie üblicherweise praktiziert wird...

Blöderweise ist die Erzeugung der überlangen Latchphasen nun zwar 
programmiertechnisch überhaupt kein Problem (genau das machen ja 
letztlich die "Burst-ler"), aber leider für das Ziel einer erweiterten 
PWM wegen der dadurch verursachten massiven Reduktion der Framerate 
völlig unbrauchbar.

Deswegen ist das Spannende die Enschlüsselung des Schemas, nach dem die 
Datenübernahme in die PWM  ausgelassen wird. Das erfolgt nämlich 
keinesfalls zufällig, sondern ist offensichtlich sehr gut durchdacht. 
Bloss ist das Schema dafür ist halt leider nicht dokumentiert. 
Wahrscheinlich hat irgendeine "intellectuell property"-Scheisse das 
verhindert...

Wie auch immer: würde das Schema kennen, könnte man die PWM-Erweiterung 
darauf abstimmen und so letztlich doch noch das Ziel erreichen.

Wie gesagt, am Wochenende poste ich mal, was ich bisher über dieses 
fiese Schema herausgefunden habe und wie ich das gemacht habe.

von Marco H. (damarco)


Lesenswert?

Das kann ich nicht bestätigen.  Alles über >9ns wird als Reset 
interpretiert.  Du musst also zwischen den Frames mindestens 9ns Luft 
lassen.

Ohne das break sind die Treiber noch eingerastet und schieben das Signal 
weiter durch. Im Datenblatt stehen min. 50ns aber ab 9ns funktioniert 
dies schon.

Die maximale Framerate ergibt sich aus der Länge der Kette,Takt,Reset. 
Das Delay zwischen den LEDs ist nicht weiter relevant da es sich ja wie 
ein Domino durch die Kette vorsetzt.


Versuchst du BCM(Binary Code Modulation)? Das wird wohl kaum mit einer 
besseren Auflösung funktionieren da die Framerate viel höher sein muss.

Einfach mal messen zwischen IN/OUT  bei einen erfolgreichen Reset kommt 
am OUT für 24 BIT Zyklen nichts raus.

Schon deswegen wäre die Theorie widerlegt da der nachfolgende Treiber 
dies als Reset erkennen würde ;)

: Bearbeitet durch User
von vorbeieilender (Gast)


Lesenswert?


von Tim  . (cpldcpu)


Angehängte Dateien:

Lesenswert?

Das ganze ist recht einfach erklärt: Der PWM-Timer ist unabhängig von 
der Kommunikationslogik getaktet. Neue PWM-Settings werden nur bei 
Überlauf des PWM-Timers übernommen. Die PWM-Frequenz ist etwa 400 Hz.

Man sieht das im Oszilloskopbild oben sehr schön.

Kanal 2: Datain einer WS2812. Die LED wird abwechselnd auf 000000 und 
FFFFFF geschaltet. Jeder der sichtbaren blöcke entspricht den kompletten 
24 bit.
Kanal 1: Schaltzustand der LEDs, gemessen über Widerstand an VCC.

Wie man sieht, zeigt sich der PWM-Ausgang ziemlich unbeeindruckt von den 
vielen Updates, die an die LED gesendet werden. Erst wenn er überläuft, 
wird der nächste Datenwert übernommen.

: Bearbeitet durch User
von Marco H. (damarco)


Lesenswert?

http://wp.josh.com/2014/05/15/getting-physical-to-uncover-neopixel-pwm-secrets/

genau das wird hier beschrieben ;)

Wenn der Pegel 1->0 fällt wird ein interner Timer gestartet, von 0->1 
wird er gestoppt. Über 6µs nach dem letztens Bit werden die Daten in 
einen Buffer übernommen. Beim nächsten asynchronen PWM Zyklus werden die 
Daten aus dem Buffer auf die LED Ausgegeben.  Das kann im ungünstigen 
Fall ~2ms dauern.

Das bedeutet wenn du die Daten <2ms aktualisierst ist nicht sicher das 
sie auch auf die LED ausgeben werden.

Im Normalfall spielt das aber keine Rolle bei mehr wie einen Pixel.

: Bearbeitet durch User
von Johannes (menschenskind)


Lesenswert?

Hi ich hoffe ich bin hier korrekt.

Habe soeben meinen Code compilieren lassen, allerdings bekomme ich bei 
diesem Ausdruck:
1
ws2812_DDRREG |= maskhi; // Enable output

diese Fehlermeldung:
> pasting "DDR" and "(" does not give a valid preprocessing token

Womit kann das zusammenhängen?

Ich habe das AVR-Studio 5.1.

Danke im Voraus

von holger (Gast)


Lesenswert?

>Womit kann das zusammenhängen?

Mit deinem Programm das hier wie so oft niemand sehen kann?

von Johannes (menschenskind)


Lesenswert?

Das ist der Beispielcode von Tims Githubprojekt und zwar präzise das 
RGB-Blinky-Beispiel
Deswegen hab ich mir gespart, den zu posten, hätte das allerdings 
erwähnen sollen :)

von holger (Gast)


Lesenswert?

>Deswegen hab ich mir gespart, den zu posten, hätte das allerdings
>erwähnen sollen :)

Und ich spare mir es das jetzt selber zu suchen.

von Johannes (menschenskind)


Lesenswert?

Ok, hab den Fehler gefunden:
In der config.h hatte ich den Zielport mit "PORTD" und den Zielpin mit 
"PD6" anstatt nur "D" und "6" angegeben.
Jetzt funktioniert es.

von Ben B. (fataldiskerror)


Lesenswert?

Moin!

Schön zu sehen dass ein 3 Jahre alter Thread immer noch genutzt wird. 
Ich bin auf ihn gestoßen, während ich dabei war, meine eigene 
Implementation in ASM zu schreiben und Probleme bekam.

Vorwort:
Ich bin zwar Beruflich seit vielen Jahren in der Programmierung 
(Hochsprachen wie Java/C#, gelegentlich c++) tätig, im 
Mikrocontroller-Bereich jedoch nur privat und im kleinen. Bisher habe 
ich alle meine Projekte in ASM geschrieben und mit dieser "Library" das 
erste mal c genutzt - funktioniert soweit schon mal super!
Da alles funktioniert geht es mir hier nicht um Lösungen, sondern um 
Verständnis. Ich habe also versucht, das Rad neu zu erfinden - blöde 
Idee, aber ich wollte die Funktionsweise verstehen/nachbauen. Dabei ist 
mir das doch recht knappe Timing bei 16MHz aufgefallen. Ein einfaches 
Bit zu senden ging ja noch problemlos, das ganze über SubRoutinen zu 
lösen dann schon eher nicht mehr (Quarz für 20MHz wollte ich nicht 
nutzen, da gerade nicht griffbereit). rcall und ret zusammen sind ja 
schon 7 Takte. Habe dann das senden von Bits 24 mal hintereinander 
kopiert et voila: ich hatte eine Farbe. Nun ist das leider überhaupt 
nicht wartbar und unglaublich hässlich. Daher Google gefragt und auf den 
Artikel samt diesen Thread gestoßen, Library geladen, auf den attiny 45 
geladen und mich gefreut.

Thema 1:
Nun frage ich mich, wie das mit dem Timing funktioniert und welcher 
Compile-Parameter für das Loop-Unrolling verantwortlich ist.
Stichwort Loop-Unrolling: ich vermute(!), dass der Compiler den asm-Code 
in der Schleife mit korrekt gesetzten ldi´s immer und immer wieder 
hintereinander setzt. Sohätte man nicht das Problem mit der Dauer von 
Aufrufen von SubRoutinen. Stimmt das? In der Doku von gcc habe ich auch 
diverse Compiler-Parameter gefunden, die das machen können, die werden 
jedoch allesamt nicht genutzt. Und bei -O2 (das genutzt wird) steht:
"The compiler does not perform loop unrolling or function inlining when 
you specify -O2".
Oder verstehe ich den Compiler da falsch?

Thema 2:
Ich verstehe das "Protokoll" nicht so ganz. der Teil mit 1,25µs je Bit 
ist klar, 24 Bit je Farbe auch, n 24 Bit Werte hintereinander steuern n 
LEDs an, 50µs low übernimmt die je 24 Bit in die LED und "schaltet" die 
Farbe.
Nicht klar ist mir die Pause zwischen Bits / 24 Bits. Müssen die Bits 
direkt ohne Verzögerung aufeinander folgen (+- Toleranz)? Gleiche Frage 
für die 24 Bits: kann ich dazwischen Pausen <50µs machen, oder stört das 
das Protokoll?

Thema 3:
Kann ich nebenbei noch den Timer nutzen, oder stört das den 
Programmablauf? Ich möchte nur die ganze Ausführung nach z.B. 30 min 
deaktivieren und die LEDs abschalten (ein Pin ist high und schaltet 
damit die Versorgungsspannung der LEDs).
Das Ganze wird ein USB-Powerbank-gestütztes Einschlaflicht für meine 
Kinder.

Danke im Voraus für eure Antworten, Geduld und das Lesen dieses langen 
Posts!

von Tim  . (cpldcpu)


Lesenswert?

Ben B. schrieb:
> Thema 1:
> Nun frage ich mich, wie das mit dem Timing funktioniert und welcher
> Compile-Parameter für das Loop-Unrolling verantwortlich ist.

Die inner-loop ist komplett in Assembler implementiert. Ein Unrolling 
ist nicht notwendig.

Wenn Du das Makefile der Beispiele nutzt, wird als *.lss ein disassembly 
abgespeichert. Dort kannst Du Dir den erzeugten Code ansehen.
>
> Thema 2:
> Nicht klar ist mir die Pause zwischen Bits / 24 Bits. Müssen die Bits
> direkt ohne Verzögerung aufeinander folgen (+- Toleranz)? Gleiche Frage
> für die 24 Bits: kann ich dazwischen Pausen <50µs machen, oder stört das
> das Protokoll?

Jede Pause über 8..9µs wird als reset interpretiert. Die Pausen sollten 
als sicherheitshalber nicht länger als 5µs sein.

> Thema 3:
> Kann ich nebenbei noch den Timer nutzen, oder stört das den
> Programmablauf? Ich möchte nur die ganze Ausführung nach z.B. 30 min
> deaktivieren und die LEDs abschalten (ein Pin ist high und schaltet
> damit die Versorgungsspannung der LEDs).

Du kannst ohne Probleme die Timer verwenden. Allerdings sollten die 
Interrupts während des Schreibens auf der LEDs gesperrt sein, da sonst 
das timing durcheinander kommt.

von Ben B. (fataldiskerror)


Lesenswert?

Moin Tim,

Tim  . schrieb:
> Wenn Du das Makefile der Beispiele nutzt, wird als *.lss ein disassembly
> abgespeichert. Dort kannst Du Dir den erzeugten Code ansehen.

hab ich mal gemacht und was soll ich sagen: wow, das ist mal schwierig 
zu verstehen. Teilweise noch c-Code (Die while-Schleife) und all die 
Sprungbefehle :-) Ich muss mir mal ein einfache Beispiel schreiben und 
lernen .lss zu lesen.

Und ich muss mir einmal genau anschauen, wie das mit dem inline-asm 
funktioniert. Sieht aber recht einfach aus.

Ich habe noch nicht recht verstanden, wie du das Laden der Farbwerte in 
dem knappen Timing schaffst. Theoretisch müsste ja alle 10 Takte ein 
neues Farbwert-Bit gesendet werden (das wird durch die asm-loop-schleife 
mit brne und bitshifting erledigt, soweit ich das verstehe). danach muss 
ja aber auch noch ein neuer Farbwert geladen werden - das ist im 
asm-template nicht drin und bedeutet ja weitere Taktzyklen - wo nimmst 
du die Zeit her? Dumme Frage vielleicht :-) Für mich sieht das so aus, 
als ob die low-Phasen nach jedem Farbwert länger sind. Richtig? Falsch?
Ich hoffe, ihr könnt mein Verständniss für das "wie" etwas vergrößern 
;-)

Dank & Gruß,
Ben

von Johannes (menschenskind)


Lesenswert?

Hi Tim bzw. wer mir gerade helfen kann :):
Mein Problem ist der Betrieb bei F_CPU < 10MHz. Sobald ich von meinem 
Quarz auf 8MHz (interner Takt oder externer Quarz) umsteige, wird das 
vorher funktionierende Leuchtmuster nicht mehr dargestellt.
Ich habe das Troubleshooting durchgegangen und auch "-O3" also "optimize 
most" funktioniert nicht.
Meine F_CPU habe ich in einer config-Header-Datei.

Mein Ziel ist, vom Takt her so niedrig wie möglich zu kommen.
Worin könnte mein Problem bestehen?

Danke im Voraus.

von Tim  . (cpldcpu)


Lesenswert?

Johannes H. schrieb:
> Hi Tim bzw. wer mir gerade helfen kann :):
> Mein Problem ist der Betrieb bei F_CPU < 10MHz. Sobald ich von meinem
> Quarz auf 8MHz (interner Takt oder externer Quarz) umsteige, wird das
> vorher funktionierende Leuchtmuster nicht mehr dargestellt.
> Ich habe das Troubleshooting durchgegangen und auch "-O3" also "optimize
> most" funktioniert nicht.

Bei 8 MHz würde ich eigentlich absolut keinerlei Probleme erwarten. Wenn 
Du davon ausgehst, dass es ein Compiler-Problem ist, könnte es helfen, 
sich das disassmebly-file (LSS) anzuschauen.

Hast Du auch "-Os" probiert? -O3 hilft auf dem AVR meist nicht viel.

> Meine F_CPU habe ich in einer config-Header-Datei.

Am sinnvollsten ist es, wenn man F_CPU im Makefile definiert.

> Mein Ziel ist, vom Takt her so niedrig wie möglich zu kommen.
> Worin könnte mein Problem bestehen?

Prinzipiell sind sogar 4MHz drin. Dort kann es allerdings mit einzelnen 
LEDs schon Probleme geben.

von Tim  . (cpldcpu)


Lesenswert?

Ben B. schrieb:
> Ich habe noch nicht recht verstanden, wie du das Laden der Farbwerte in
> dem knappen Timing schaffst. Theoretisch müsste ja alle 10 Takte ein
> neues Farbwert-Bit gesendet werden (das wird durch die asm-loop-schleife
> mit brne und bitshifting erledigt, soweit ich das verstehe). danach muss
> ja aber auch noch ein neuer Farbwert geladen werden - das ist im
> asm-template nicht drin und bedeutet ja weitere Taktzyklen - wo nimmst
> du die Zeit her? Dumme Frage vielleicht :-) Für mich sieht das so aus,
> als ob die low-Phasen nach jedem Farbwert länger sind. Richtig? Falsch?
> Ich hoffe, ihr könnt mein Verständniss für das "wie" etwas vergrößern
> ;-)

Prinzipiell ist nur das Timing innerhalb eines bits kritisch. Zwischen 
den Bits kann man sich etwas mehr Zeit nehmen (bis zu 3µs). Daher ist es 
kein Problem, ein byte nachzuladen. Eine weitere Optimierung macht hier 
keinen Sinn und schränkte nur die Flexibilität unnötig ein.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Tim  . schrieb:
> Bei 8 MHz würde ich eigentlich absolut keinerlei Probleme erwarten.

 Bei 8MHz funktionert es nur mit ausgerolltem outerloop, ein ganzes
 Port wird fur ein bit gebraucht ('out Port, Wert') und selbst dann
 sind glitches von 125us nicht zu vermeiden.

 Wenn man es mit 'sbi Port, Pin' versucht, konnen die Specs nicht
 eingehalten werden und glitches sind immer noch drin.

 So etwas nenne ich Library fur Optimisten - falls irgendwann mal
 WS28xx rauskommt, mit strengem Timing, ist die Library nutzlos.

Tim  . schrieb:
> Prinzipiell sind sogar 4MHz drin.

 Nein, mit Sicherheit nicht. Wie du es auch machst, mit 4MHz bist du
 weit aus der Specs raus und kannst nur hoffen, dass die WS2811/2812
 Nachsicht mit dir haben.

: Bearbeitet durch User
von Konrad S. (maybee)


Lesenswert?

Marc V. schrieb:
> sind glitches von 125us nicht zu vermeiden.

Und was soll da eintausend Takte dauern, bitteschön?

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Konrad S. schrieb:
> Und was soll da eintausend Takte dauern, bitteschön?

 Das war natürlich ein Schreibfehler, dankeschön.

von Tim  . (cpldcpu)


Lesenswert?

Marc V. schrieb:
> i 8 MHz würde ich eigentlich absolut keinerlei Probleme erwarten.
>
>  Bei 8MHz funktionert es nur mit ausgerolltem outerloop, ein ganzes
>  Port wird fur ein bit gebraucht ('out Port, Wert') und selbst dann
>  sind glitches von 125us nicht zu vermeiden.

Bitte? Sorry, das ist absoluter Unsinn. Lies Dir doch den Thread oben 
einmal durch.


> Tim  . schrieb:
>> Prinzipiell sind sogar 4MHz drin.
>
>  Nein, mit Sicherheit nicht. Wie du es auch machst, mit 4MHz bist du
>  weit aus der Specs raus und kannst nur hoffen, dass die WS2811/2812
>  Nachsicht mit dir haben.

Bei den WS2812B reicht es. WS2812S und SK6812 sind schneller getaktet, 
hier geht es nicht.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Tim  . schrieb:
> Bitte? Sorry, das ist absoluter Unsinn. Lies Dir doch den Thread oben
> einmal durch.

 Kann sein, werde mir mal den Thread genau durchlesen und bei github
 vorbeischauen.

Tim  . schrieb:
> Bei den WS2812B reicht es. WS2812S und SK6812 sind schneller getaktet,
> hier geht es nicht.

 Hier ist es mit Sicherheit reine Glückssache ob es funktioniert, da
 weit aus den Specs.

von Johannes (menschenskind)


Lesenswert?

Hi Tim
Ja jetzt mit der F_CPU im Makefile(über die Toolchain-->Symbols) 
funktioniert es (Auch bei 4MHz internem AtMega32-Oszillator).

Könnte mir das bitte jemand näher erklären? Da fehlt mir eindeutig noch 
tiefergehendes Wissen.

von Tim  . (cpldcpu)


Lesenswert?

Marc V. schrieb:
> Tim  . schrieb:
>> Bei den WS2812B reicht es. WS2812S und SK6812 sind schneller getaktet,
>> hier geht es nicht.
>
>  Hier ist es mit Sicherheit reine Glückssache ob es funktioniert, da
>  weit aus den Specs.

Der Knackpunkt der Sache besteht darin, zu verstehen wie die 
State-machine im WS2812 funktioniert (oben verlinkte Artikel). Es gibt 
nur zwei Timings, die überhaupt relevant sind: Die Zeit von der 
steigenden Flanke Din bis zum Sampling-Point (~400-600 ns) und die 
Reset-Dauer (~5µs).

Der Rest ist komplett unkritisch. Das Protokoll ist deutlich robuster 
als es im Datenblatt erscheint. Und das ist auch gut so, da die WS2812 
selbst natürlich auch Fertigungsschwankungen unterworfen sind. Die in 
der Lib eingestellten Timings berücksichtigen erhebliche Toleranzen. Bei 
4 Mhz können diese nicht immer eingehalten werden. Bei 8 MHz ist das 
kein Problem.

: Bearbeitet durch User
von Tim  . (cpldcpu)


Lesenswert?

Johannes H. schrieb:
> Ja jetzt mit der F_CPU im Makefile(über die Toolchain-->Symbols)
> funktioniert es (Auch bei 4MHz internem AtMega32-Oszillator).
>
> Könnte mir das bitte jemand näher erklären? Da fehlt mir eindeutig noch
> tiefergehendes Wissen.

Wahrscheinlich hat irgendein Teil des Code F_CPU nicht gesehen. Im 
Makefile ist das F_CPU define auf jeden Fall richtig aufgehoben.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Tim  . schrieb:
> Der Knackpunkt der Sache besteht darin, zu verstehen wie die
> State-machine im WS2812 funktioniert (oben verlinkte Artikel). Es gibt
> nur zwei Timings, die überhaupt relevant sind: Die Zeit von der
> steigenden Flanke Din bis zum Sampling-Point (~400-600 ns) und die
> Reset-Dauer (~5µs).

 Da hast du Recht, habe inzwischen auch meine 8 MHz Routine angeschaut.
 Ich habe 6:5 bei Log.1 und 3:8 bei Log.0.
 Und habe auch ordentlich kommentiert, dass die Null auch mit nur
 2 Takten Log.1 geht...


> Der Rest ist komplett unkritisch. Das Protokoll ist deutlich robuster
> als es im Datenblatt erscheint.

 Ja, da hast du wieder Recht.

von Dirk K. (dekoepi)


Lesenswert?

Eine Bibliothek, die "magisch" funktioniert, ist zwar nett - aber so 
etwas eigentlich Einfaches kann man auch selber versuchen zu verstehen 
und umzusetzen.

Ich wollte das mal auf ATmega328 in physischer Form des Arduino Nano 
testen - ohne ASM, ohne besondere Kniffe, direkt als Schnellschuss in 
der Arduino-IDE. Lerneffekt beachtenswert.

Schleifen zum Auseinandernehmen des 24-Bit-Werts je LED führen zu 
falschem Timing. Ich musste manuelles "loop unrolling" verwenden. Nicht 
hübsch, aber funktioniert. Für mehr Ordnung und Übersicht einige 
wiederkehrende Funktionsblöcke als inline eingefügt.

Der letzte Fehler kam daher, dass die Arduino-lib den Timer0 aktiviert 
und für Delay(), millis() und so weiter nutzt. Auch das stört das Timing 
ungemein und führt dann zu Fehlfarben und sporadisch ungewollt 
aufblitzenden LEDs.

Hier die korrekte Lösung (im Sinne von: funktioniert wie gewollt):
http://pastebin.com/312fCpxB

Ist recht einfach und übersichtlich, zudem bei den Timern noch mal 
aufgedröselt, welche Flags das nun sind.

Farbfehler, wenn die Arduino-lib reinfunkt:
https://youtu.be/h7ubECPKRJs

Korrekt sieht es dann so aus:
https://youtu.be/hwUaI-tJ2UE

Bei der Suche findet man so viel Quatsch und teils unverständliche 
Lösungen, da kann eine Variante mehr nicht schaden ;) Vielleicht bastel 
ich das noch für den STM32F030 - gegebenenfalls mit N-FET/P-FET zum 
sauberen Schalten.

von Johannes (menschenskind)


Lesenswert?

Hallo,

Wie müsste man die Befehle fürs Senden modifizieren, damit das Data-Pin 
wie ein OpenCollector/Drain agiert?

Ich möchte nämlich den µC(AVR) an ner 3V Knopfzelle und die LED über 
eine 5V Ladungspumpe betreiben.

Danke im Voraus.

von Tim (Gast)


Lesenswert?

Johannes H. schrieb:
> Wie müsste man die Befehle fürs Senden modifizieren, damit das Data-Pin
> wie ein OpenCollector/Drain agiert?
>
> Ich möchte nämlich den µC(AVR) an ner 3V Knopfzelle und die LED über
> eine 5V Ladungspumpe betreiben.


Die AVR unterstützen, soweit ich weiss, keine echten open-drain 
Ausgänge. Die maximale Spannung an den I/O pins sollte nicht über 
VCC+0.5V liegen, sonst fängt die Clamp-Diode an zu leiten.

Du benötigst also einen echten Pegel-Converter.

von Johannes (menschenskind)


Lesenswert?

Danke Tim, die Charakteristik hätte ich eigentlich selber nachgucken 
müssen...
OK, entweder Levelshifter oder über nen Transistor könnte man es lösen. 
(Transistor und 2 Widerstände sind natürlich günstiger im Preis)

Aber falls ich die Transistorlösung wähle, wird das Signal ja 
invertiert. Welche Zeilen in Deinem Assemblercode müsste man dann 
modifizieren?

von Tim (Gast)


Lesenswert?

Johannes H. schrieb:
> Aber falls ich die Transistorlösung wähle, wird das Signal ja
> invertiert. Welche Zeilen in Deinem Assemblercode müsste man dann
> modifizieren?

Einfach die masken verändern.

https://github.com/cpldcpu/light_ws2812/blob/master/light_ws2812_AVR/Light_WS2812/light_ws2812.c#L110

von Johannes (menschenskind)


Lesenswert?

Hallo Tim

Also einfach eine simple Vertauschung?
1
masklo |= maskhi&ws2812_PORTREG;
2
maskhi = ~ws2812_PORTREG;

von Johannes (menschenskind)


Lesenswert?

Tim?

von Johannes (menschenskind)


Lesenswert?

Ok, hab's gelöst.
Man muss die Übergabe der Masken in dieser Zeile vertauschen:
1
: "r" (curbyte), "I" (_SFR_IO_ADDR(ws2812_PORTREG)), "r" (maskhi), "r" (masklo)

von Lars H. (hufnala)


Lesenswert?

Hallo Tim,
hallo alle,

vielen Dank für die super lib, habe damit und den Beispielen in wenigen 
Stunden bei meinen bescheidenen C-Kenntnissen einen ATMega mit Stripe 
zum laufen gebracht, Animationen erweitert und das Ganze ohne jegliche 
Probleme Out of the Box! Echt einfach.... :-)

Nachdem mein Projekt Richtung 2 Stripes geht, und ich noch Platz in RAM 
& Flash habe, plane ich den 2. Stripe gesondert anzusteuern (weiß noch 
nicht gang genau welche Animationen ich laufen lassen möchte,
auf jeden Fall eher unterschiedliche z.B. 2 Richtungen als exorbitant 
schnelle und viele LEDs eher so Richtung 2*100 LEDs).

Ein erster Test den ws2812_pin als (globale) Variable umzudeklarieren 
war auch gleich mal erfolgreich.

a) Quick and dirty : Ich lasse die globale Variable, ist zwar bäh, aber 
einfach und mit einem Byte RAM :-)

b) Ich bastele an alle Funktionen die ws2812_pin verwenden einfach eine 
Variable dran. Wenn ich das richtig verstehe auch nur ein Byte RAM und 
schöner und richtiger.

c) Mit viel Phantasie könnte man noch das erste Byte des LED Array 
"umwidmen" oder besser vermtl. eine Struct aufbauen. Dann bräuchte man 
nicht mal 2 Aufrufe (wenn zum gleichen Zeitpunkt geschrieben werden 
soll) sondern könnte das Ziel über die Daten mitgeben und diese einfach
manipulieren. Wäre aber rundweg mehr Tipparbeit da die "led" Struktur 
überall in diese Struktur geschoben werden müsste oder wenigstens einmal
zugewiesen / kopiert (Zeiger zugewiesen) werden müsste.

d) Ein weiterer Weg wäre über ein 2. Define, aber wie die Gesamtstruktur 
dann aussieht kann ich mir nicht erklären. Und ich glaube dafür gäbe es
Schläge :-).

Ihr seht, meine Erfahrung hält bei Design und sicherlich auch der 
Umseztung sich in Grenzen. Auch wenn ich über CTRL-C/V ein klein wenig 
hinaus bin tendiere ich zu b). Bin aber nicht sicher ob ich was übersehe 
oder das mit den 2 Stripes aus anderen Gründen nicht geht.

Wenn's mehr Interesse gibt, bin ich auch durchaus bereit das nach 
Empfehlung runter zu tippen und zu testen. Kann aber bei zu vielen 
Zeigern auch scheitern, aber nur so lernt man...

Danke für Eure Unterstützung!

//hufnala

von Conny G. (conny_g)


Lesenswert?

Ich kenne die Lib jetzt nicht so genau, aber es gibt da noch eine 
Challenge:
Die beiden Bitstreams müssen dann gleichzeitig gesendet werden und das 
ist eine Timing-Geschichte.
Ich habe im Kopf, dass das Timing auf Seiten AVR nicht so viel Luft hat 
und mit Berechnung/Vorbereitung der Aminmation und dem Rausstreamen wird 
es dann eng, wenn nicht sogar nicht mehr möglich.

Warum nicht einfach die 2 Streifen hintereinander hängen und einfach 
separat ihre Werte berechnen?
Das ist einfach eine Halbierung der Frame Rate und wird sicher 
funktionieren.

Genau das geschieht umgekehrt mit Deinen Änderungen: Du versuchst eine 
Verdoppelung der Frame Rate.

von Lars H. (hufnala)


Angehängte Dateien:

Lesenswert?

Hi, sorry dass ich mich erst jetzt melde, habe ein paar Tage nicht 
gelesen...

Die Antwort ist einfach : Mein Controller war in der Mitte geplant, dann 
hätte ich zurück gemusst mit der Data Leitung. Über 4m nicht mein 
favorite :-).

Die Frame Rate ist mir egal, ist wie beschrieben eh langsam. Hab mir das 
nochmal angeschaut, sollte mit der Maske ja schon gehen, das habe ich 
übersehen. Aktuell kämpfe ich mit einem anderen Problem.

Die Lib funktioniert nach Umstellung auf meinem ATMega8 mit 16MHz nur 
wenn ich als Optimierung -O3 einschalte. Damit ist der Code aber mehr 
als 2k Byte größer als mit -OS. Mit -OS funktioniert (über 
Falscheinstellung am Code:Blocks auch OS und O3 gemeinsam probiert) 
alles andere (RS232 / IRMP) so dass es irgendwo an der LIB liegen 
müsste. Noch sind die 2k mehr kein Problem, aber schön ist was anderes 
(ging von 4k auf 6.6k rauf).

Das Timing ist irgendwie seltsam. Bei 8Mhz dachte ich eine Gesamtzeit 
des Pakets von 12ms gemessen zu haben. Als es mit 16Mhz nicht 
funktioniert hat,
war das ganze bei 6ms, jetzt wo es geht sind es 3ms was wohl auch den 
100 LEDs entspricht. Das einzige was ich noch geändert habe ist den 
Reset von 300us auf 70us zu ändern, die Einstellung hatte ich übersehen.

Irgendwer eine Idee?
Die IRMP lib ist schon älter, habe nicht den neuesten Stand. Anbei mal 
das Hauptprogramm. Die LIB ist die aktuelle aus dem Git.

Hier mal die Meldungen beim Compilieren. AVR gcc ist 4.8.3., aktuell aus 
Opensuse Leap 42.2.
1
-------------- Build: Release in Stripe_ATMega8 (compiler: GNU GCC Compiler for AVR)---------------
2
3
avr-gcc -Wextra -Wall -DF_CPU=16000000UL -mmcu=atmega8 -O3 -I/usr/include -c rainbow.c -o obj/Release/rainbow.o
4
rainbow.c: In function 'loadEEColor':
5
rainbow.c:203:1: warning: comparison is always true due to limited range of data type [-Wtype-limits]
6
 if ((Addr >= 0) && (Addr <= 8))
7
 ^
8
rainbow.c: In function 'setprogram':
9
rainbow.c:468:2: warning: comparison is always false due to limited range of data type [-Wtype-limits]
10
  if (newprogram < 0) newprogram = lastprogram;
11
  ^
12
avr-g++ -L/usr/lib -o bin/Release/Stripe_ATMega8.elf obj/Release/irmp.o obj/Release/light_ws2812.o obj/Release/rainbow.o  -mmcu=atmega8 -Wl,-Map=bin/Release/Stripe_ATMega8.map,--cref  
13
/usr/bin/avr-ld: warning: -z relro ignored.
14
/usr/bin/avr-ld: skipping incompatible /usr/lib/libm.so when searching for -lm
15
/usr/bin/avr-ld: skipping incompatible /usr/lib/libm.so when searching for -lm
16
/usr/bin/avr-ld: skipping incompatible /usr/lib/libc.so when searching for -lc
17
Output file is bin/Release/Stripe_ATMega8.elf with size 18,73 KB
18
Running project post-build steps
19
avr-size bin/Release/Stripe_ATMega8.elf
20
   text     data      bss      dec      hex  filename
21
   6642       71      432     7145     1be9  bin/Release/Stripe_ATMega8.elf
22
avr-objdump -h -S bin/Release/Stripe_ATMega8.elf > bin/Release/Stripe_ATMega8.lss
23
avr-objcopy -R .eeprom -R .fuse -R .lock -R .signature -O ihex bin/Release/Stripe_ATMega8.elf bin/Release/Stripe_ATMega8.hex
24
avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex bin/Release/Stripe_ATMega8.elf bin/Release/Stripe_ATMega8.eep
25
avr-objcopy --no-change-warnings -j .lock --change-section-lma .lock=0 -O ihex bin/Release/Stripe_ATMega8.elf bin/Release/Stripe_ATMega8.lock
26
avr-objcopy --no-change-warnings -j .signature --change-section-lma .signature=0 -O ihex bin/Release/Stripe_ATMega8.elf bin/Release/Stripe_ATMega8.sig
27
avr-objcopy --no-change-warnings -j .fuse --change-section-lma .fuse=0 -O ihex bin/Release/Stripe_ATMega8.elf bin/Release/Stripe_ATMega8.fuse
28
srec_cat bin/Release/Stripe_ATMega8.fuse -Intel -crop 0x00 0x01 -offset  0x00 -O bin/Release/Stripe_ATMega8.lfs -Intel
29
/bin/sh: srec_cat: Kommando nicht gefunden.
30
Process terminated with status 127 (0 minute(s), 0 second(s))
31
0 error(s), 3 warning(s) (0 minute(s), 0 second(s))

Vielen Dank für Eure Unterstützung! Jetzt ist dann erst mal Party :-)

Ansonsten Euch allen einen Guten Rutsch ins Jahr 2017!!!

//hufnala

von nappel (Gast)


Lesenswert?

Vielen Dank für die großartige Library!!!
Habe sehr schnell viele Animationen realisieren können.

von Daniel B. (phill93)


Lesenswert?

Hallo,

versuche gerade die Bibliothek auf einem attiny48 zum Laufen zu bringen.
Nur Leuchten die LEDs nur in der initial Farbe.

LEDs: https://www.pololu.com/product/2535 (mit der Arduino Bibliothek 
gehen sie)

Kann das sein das der attiny48 nicht supported wird?

Gruß

Phill93

von Daniel B. (phill93)


Lesenswert?

Hat sich erledigt.
Die CKDIV8 Fuse war noch gesetzt.

von Marcel K. (viewer)


Lesenswert?

Hallo Forum Gemeinde,

Erst mal ein großes Dankeschön an Tim. Eine echt tolle Lib die Du da für 
alle zur Verfügung gestellt hast. Auch Deine Blog habe ich schon 
gelesen. Immer wieder toll zu sehen, dass sich Menschen die Zeit nehmen 
sich so zu engagieren und sein Wissen mit anderen zu Teilen.

Zu meinem Anliegen:
Nein, ich habe kein Problem mit der Anwendung der Lib bzw. mit der 
Ansteuerung der LEDs. Es liegt eher am Verständnis des Codes.
Mir reicht es nicht den Code einfach nur zu verwenden sondern ich möchte 
ihn auch gerne verstehen. Daher habe ich mich seit ein paar Tagen mit 
den ASM Befehlen beschäftigt. Somit hat sich die Anzahl der Fragezeichen 
deutlich verringert.
Ich habe zum Verständnis die Funktion auf die wichtigsten Befehle 
reduziert.
1
asm volatile(
2
  " lsl %[dat],#24          \n\t"  // [ 1]  => OK
3
  " movs %[ctr],#8          \n\t"  // [ 2]  => OK
4
  " ilop%=:                 \n\t"  // [ 3]
5
  " lsl %[dat], #1          \n\t"  // [ 4]  => OK
6
  " str %[maskhi], [%[set]] \n\t"  // [ 5]  => OK
7
  " bcs one%=               \n\t"  // [ 6]
8
  " str %[masklo], [%[clr]] \n\t"  // [ 7]  => OK
9
  " one%=:                  \n\t"  // [ 8]
10
  " sub %[ctr], #1          \n\t"  // [ 9]  => OK
11
  " str %[masklo], [%[clr]] \n\t"  // [10]  => OK
12
  " beq  end%=              \n\t"  // [11]
13
  " b   ilop%=              \n\t"  // [12]
14
  " end%=:                  \n\t"  // [13]
15
  :[ctr] "+r" (i)
16
  :[dat] "r" (curbyte), [set] "r" (set), [clr] "r" (clr), [masklo] "r" (masklo), [maskhi] "r" (maskhi)
17
    );
Generell ist mir schon klar was die Funktion machen soll. Es gibt eine 
äußere und innere Schleife. Die äußere Schleife läuft von 8 bis 0 um die 
einzelnen Bit des Bytes zu ermitteln. Die innere Schliefe ermittelt den 
Wert des einzelnen Bits um entsprechend die Dauer der Ansteuerung des 
logischen Pins zu realisieren.
Die äußere Schleife ist die „i“ Schleife. Diese Schleife scheint das
1
ilop%=:
 und
1
b ilop%=
 zu sein. Aber wie funktioniert dieser Befehl? Ist
1
ilop
 ein frei gewählter Name? Worin besteht der Unterschied zwischen
1
%=:
 und
1
%=
? Der
1
b ilop%=
 Branch Befehl scheint die Loop so lange abzuarbeiten, bis der Wert „0“ 
erreicht ist.

Was ich auch nicht verstehe, sind die drei Befehle
1
bcs one%=
,
1
one%=:
 und
1
beq  end%=
. Ich weiß schon, dass
1
bcs
 branch on carry set heißt, aber was heißt das für die Schleife? Zuerst 
wird der logische Prozessor Port Pin auf „high“ gezogen. Aber was 
passiert danach?
Tim hat am 02.02.2014 mal diese Schleife erwähnt:
1
while (--i) {
2
  PORT=HI;
3
  if (data&128) PORT=LO;
4
  data<<=1;
5
  PORT=LO;
6
}

Das verstehe ich schon da ja mit der If Abfrage bereits früher der Port 
Pin wieder auf low gezogen wird, falls das 8. Bit „1“ ist. (dazwischen 
sind ja die unterschiedlichen delay Zeiten)
Aber woher kommt das
1
one
?
1
beq end%=
 heißt vermutlich, das zu einem bestimmten Zeitpunkt (branch if equal) 
zu
1
end%=:
 gesprungen wird. Was wird denn auf
1
eqal
 getestet?

Bereits am 09.09.2013 schrieb „Icke ®“ hier im Forum, dass es genial 
wäre das Datenbit in das Carry zu schieben. Was ist denn das geniale 
daran? Kann mir das einer erklären?

Ich würde mich freuen wenn mir jemand hier im Forum weiter helfen 
könnte.

Viele Grüße und noch einen schönen Tag

von Tim  . (cpldcpu)


Lesenswert?

Freut mich, dass Du den Code genau verstehen musst. Für den Einstieg in 
Assembler ist GCC-inline ASM allerdings gleich ziemlich kryptisch. Hier 
gibt es eine Übersicht:

http://www.ethernut.de/de/documents/arm-inline-asm.html

Marcel K. schrieb:
> Ist ilop ein frei gewählter Name? Worin besteht der Unterschied
> zwischen%=: und%=

ilop%=: ist ein Label. Das wird durch den Doppelpunkt deklariert
"%=" erzeugt einen unique identifier.

https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
1
  " lsl %[dat], #1          \n\t"  // [ 4]  => OK
2
  " str %[maskhi], [%[set]] \n\t"  // [ 5]  => OK
3
  " bcs one%=               \n\t"  // [ 6]
4
  " str %[masklo], [%[clr]] \n\t"  // [ 7]  => OK
5
  " one%=:                  \n\t"  // [ 8]

Deine Fragen zu den Sprüngen zielen auf die Nutzung von Flags ab. Du 
solltest Dir einmal anschauen, welche Befehle welche Flags setzen. Der 
Code ist teilweise etwas verwirrend, da die Sprungbefehle von Flags 
abhängen, die von weiter zurück liegenden Befehlen gesetzt werden.

Im obigen Fall wird das auszugebende bit von "lsl" ins carry-flag 
geschoben. Wenn das bit eine "1" war, wird durch "bcs" "str" 
übersprungen. "str" zieht den Ausgang vorzeitig auf "0", wenn ein kurzer 
Puls ausgegeben wird.

von Marvin (Gast)


Lesenswert?

Hallo zusammen,

nachdem hier die Bibliothek vorgestellt und reichlich diskutiert wurde, 
dachte ich es ist der richtige Ort um meine Frage zu stellen.

Ich verwende den µC LPC11C24 von NXP, der auf einem ARM Cortex-M0 
basiert,
daher möchte ich die light_ws2812_ARM bentutzen.
Da das Vorgehen in der Readme ganz gut beschrieben ist bin ich auch nach 
ihr vorgegangen.
Allerdins habe ich folgendes Problem:
In der Readme steht:
- Change i/o pin settings according to the I/O pin you are using:
  - Define `LIGHT_WS2812_GPIO_PIN=XXX`
  - Define `LIGHT_WS2812_GPIO_PORT=XXX`

Im Code dann:
1
  #define ws2812_port_set ((uint32_t*)&LIGHT_WS2812_GPIO_PORT->SET0)  // Address of the data port register to set the pin
2
  #define ws2812_port_clr ((uint32_t*)&LIGHT_WS2812_GPIO_PORT->CLR0)  // Address of the data port register to clear the pin

Die Adresse des von mir verwendeten Port ist 0x40044084, was ich 
anstelle von &LIGHT_WS2812_GPIO_PORT eingefügt habe. Jedoch ohne Erfolg.
Kann mir jemand ein Beispiel liefern wie die Umsetzung richtig ist ?

Vielen Dank im Voraus.

von Tim  . (cpldcpu)


Lesenswert?

> In der Readme steht:
> - Change i/o pin settings according to the I/O pin you are using:
>   - Define `LIGHT_WS2812_GPIO_PIN=XXX`
>   - Define `LIGHT_WS2812_GPIO_PORT=XXX`

Ich finde leider meinen LPC810 testcode gerade nicht mehr. Generell 
heissen die I/O ports auf den kleinen LPCs aber "LPC_GPIO_PORT".

Also:

#define LIGHT_WS2812_GPIO_PORT LPC_GPIO_PORT
#define LIGHT_WS2812_GPIO_PIN XXX

Sehe auch gerade, dass sich im include wohl ein bug eingeschlichen hat. 
Kann leider nicht alle ARM-Varianten testen.

> Die Adresse des von mir verwendeten Port ist 0x40044084, was ich
> anstelle von &LIGHT_WS2812_GPIO_PORT eingefügt habe. Jedoch ohne Erfolg.
> Kann mir jemand ein Beispiel liefern wie die Umsetzung richtig ist ?

Was genau funktioniert denn nicht? Lässt sich der code compilieren? 
Liegen am Ausgang signale an? Stimmt das timing nicht?

von Marvin (Gast)


Lesenswert?

Ich glaube das erste Problem liegt daran, dass mir nicht ganz klar ist 
was durch
1
#define LIGHT_WS2812_GPIO_PORT LPC_GPIO_PORT
2
#define LIGHT_WS2812_GPIO_PIN XXX
erreicht werden soll.

Über
1
LPC_IOCON_TypeDef->PIO3_0
kann beispielsweise auf den Pin 0 vom Port 3 zugegriffen werden.

"LPC_GPIO_PORT" kann ich in CMSIS nicht finden, gibt es das 
möglicherweise in den LPC-Bibliotheken ?

Um welchen Bug handelt es sich ?

Das Problem liegt darin, dass sich der Code nicht kompilieren lässt.
Ich würde es sehr gerne Testen und dann ein Feedback geben, da ich das 
Projekt sowieso dokumentieren muss.

von Johannes S. (Gast)


Lesenswert?

also ich finde in der lpc11xx.h sofort das hier:
1
#define LPC_GPIO3             ((LPC_GPIO_TypeDef   *) LPC_GPIO3_BASE )

du fummelst im IOCON Register rum, da werden nur Einstellungen zum IO 
Pin vorgenommen.

Bei einem LPC1347 habe ich WS2812B per SPI angesteuert, das geht bei den 
LPC gut weil man den Takt für SPI sehr genau einstellen kann.

von Johannes (menschenskind)


Lesenswert?

Hallo Tim,

Bei meiner funktionierenden Elektronik(ATTiny85 & 3 WS2812b) wollte ich 
noch etwas die Batterielebensdauer vergrößern und den internen Takt von 
8 auf 6,4MHz verringern, was ja einfach mit den CKSEL Fuses eingestellt 
werden kann.
Da der µC dann im ATTiny15 Compatibility Mode ist, beträgt der interne 
Systemtakt dann 1,6MHz lt. Datenblatt.

Ich habe also im Code die F_CPU auf diesen Wert abgeändert, die Fuses 
programmiert und den Code geflasht.
Aber leider ohne erwünschtes Ergebnis.
Gibt es also evtl. noch etwas anderes, was ich anpassen müsste?
Evtl. passt durch den niedrigeren Takt das Timing für das Datensignal 
nicht mehr, aber das kann ich mir mangels Messhardware nicht anschauen.

Gruß
Hannes

von Icke ®. (49636b65)


Lesenswert?

Johannes H. schrieb:
> Systemtakt dann 1,6MHz lt. Datenblatt

Das dürfte deutlich zu wenig sein.

> Evtl. passt durch den niedrigeren Takt das Timing für das Datensignal
> nicht mehr

So ist es, WIMRE sind 4MHz schon sportlich.

von Helmut H. (helmuth)


Lesenswert?

Johannes H. schrieb:
> Da der µC dann im ATTiny15 Compatibility Mode ist, beträgt der interne
> Systemtakt dann 1,6MHz lt. Datenblatt.

Das Timing wird durch NOPs realisiert, siehe
https://github.com/cpldcpu/light_ws2812/blob/master/light_ws2812_AVR/Light_WS2812/light_ws2812.c

"// Warn or throw error if this timing can not be met with current F_CPU 
settings."
Hats beim Compilieren nicht gewarnt?

von Joachim B. (jar)


Angehängte Dateien:

Lesenswert?

Johannes H. schrieb:
> den internen Takt von
> 8 auf 6,4MHz verringern

Helmut H. schrieb:
> Das Timing wird durch NOPs realisiert

das muss man dann nur erweitern zu 6,4 Mhz

alles lösbarbei dder Arduino fast LED Lib musste ich für den 
ATmegas1284p auch eingreifen, der 1284p hat ein Adressregister mehr 
welches für 10% langsameres Timing verantwortlich ist.

#if defined(_AVR_ATmega1284P_)
  #define NS(_NS) ( (_NS * ( (F_CPU*9L/10L) / 1000000L))) / 1000
  #define CLKS_TO_MICROS(_CLKS) ((long)(_CLKS)) / ((F_CPU*9L/10L) / 
1000000L)
#else
  #define NS(_NS) ( (_NS * (F_CPU / 1000000L))) / 1000
  #define CLKS_TO_MICROS(_CLKS) ((long)(_CLKS)) / (F_CPU / 1000000L)
#endif

: Bearbeitet durch User
von Johannes (menschenskind)


Lesenswert?

Joachim B. schrieb:
> das muss man dann nur erweitern zu 6,4 Mhz

Was meinst Du mit erweitern?
Die 6,4MHz sind aber nur der Oszillator! Der Systemtakt ist in diesem 
Compatibility-Mode aber noch durch 4 geteilt, also nur bei 1,6MHz.

Weiterhin muss ich auch erst mal die aktuelle Bibliothek einbinden. Ich 
habe in meinem Projekt noch eine Version von 2018(?).

von Joachim B. (jar)


Lesenswert?

Johannes H. schrieb:
> Was meinst Du mit erweitern?

na die Lib, machte ich ja auch!

Johannes H. schrieb:
> noch durch 4 geteilt, also nur bei 1,6MHz

kann man berücksichtigen!

Ich habe mich damals durch den fremden Code gewühlt, das kannst du heute 
auch.

von Helmut H. (helmuth)


Lesenswert?

Joachim B. schrieb:
> kann man berücksichtigen!

Habe keine Ahnung welche Version der library verwendet wurde.
In der oben zitierten wird die Anzahl der NOPs anhand der F_CPU 
ermittelt.
Weniger als 0 NOPs wären erforderlich, um das Timing bei 1,6 MHz zu 
erreichen. Das mag zwar mathematisch möglich sein, geht aber 
softwaretechnisch (noch?) nicht.
aaO:
1
// Fixed cycles used by the inner loop
2
#define w_fixedlow    2
3
#define w_fixedhigh   4
4
#define w_fixedtotal  8   
5
6
// Insert NOPs to match the timing, if possible
7
#define w_zerocycles    (((F_CPU/1000)*w_zeropulse          )/1000000)
8
9
#define w1 (w_zerocycles-w_fixedlow)
10
#if w1>0
11
  #define w1_nops w1
12
#else
13
  #define w1_nops  0
14
#endif
15
16
// The only critical timing parameter is the minimum pulse length of the "0"
17
// Warn or throw error if this timing can not be met with current F_CPU settings.
18
#define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000)
19
#if w_lowtime>550
20
   #error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?"
21
#elif w_lowtime>450
22
   #warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)."
23
   #warning "Please consider a higher clockspeed, if possible"
24
#endif

: Bearbeitet durch User
von Joachim B. (jar)


Lesenswert?

Helmut H. schrieb:
> Weniger als 0 NOPs wären erforderlich, um das Timing bei 1,2 MHz zu
> erreichen.

aber es geht nicht um 1,2MHz?

Johannes H. schrieb:
> bei 1,6MHz.

sind also nicht weniger als 0 NOPs, ob es für 1,6MHz reicht kann 
getestet werden!

aber stimmt schon ist sportlich, wobei ich kaum glaube das es den 
Aufwand lohnt!

Johannes H. schrieb:
> den internen Takt von
> 8 auf 6,4MHz verringern

von Helmut H. (helmuth)


Lesenswert?

Joachim B. schrieb:
> aber es geht nicht um 1,2MHz?

hatte mich vertippt und später korrigiert.

Die Zeit für low ist zu lang, es braucht 2 Taktzyklen:

((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000)
 (0 + 2) * 1000000 / 1600 = 1250

Deswegen wundert es mich, dass es kompiliert.

: Bearbeitet durch User
von Icke ®. (49636b65)


Lesenswert?

Einfach mal das Datenblatt der WS2812B hernehmen und rechnen.

https://www.digikey.com/en/datasheets/parallaxinc/parallax-inc-28085-ws2812b-rgb-led-datasheet

Eine Null wird bspw. mit 400ns High und 850ns Low codiert, die Eins mit 
800ns High und 450ns Low. Die Toleranz beträgt +-150ns. Bei 1,6MHz 
dauert aber ein Takt schon 625ns und für das Toggeln eines Pins mit SBI 
werden zwei Takte benötigt. Es kann also mit 1,6MHz nicht funktionieren.

von Joachim B. (jar)


Lesenswert?

Johannes H. schrieb:
> Die 6,4MHz sind aber nur der Oszillator! Der Systemtakt ist in diesem
> Compatibility-Mode aber noch durch 4 geteilt, also nur bei 1,6MHz.

und wir wissen nicht mal ob es mit 8MHz (:4) funktioniert.
Sind wir einem Troll aufgesessen?

Icke ®. schrieb:
> Eine Null wird bspw. mit 400ns High und 850ns Low codiert, die Eins mit
> 800ns High und 450ns Low. Die Toleranz beträgt +-150ns. Bei 1,6MHz
> dauert aber ein Takt schon 625ns und für das Toggeln eines Pins mit SBI
> werden zwei Takte benötigt. Es kann also mit 1,6MHz nicht funktionieren

dabei ist es dann egal ob 8MHz oder 6,4MHz

von Icke ®. (49636b65)


Lesenswert?

Joachim B. schrieb:
> und wir wissen nicht mal ob es mit 8MHz (:4) funktioniert.

Der Erfinder der Routine schreibt:

Tim  . schrieb:
> Das Protokoll ist deutlich robuster
> als es im Datenblatt erscheint. Und das ist auch gut so, da die WS2812
> selbst natürlich auch Fertigungsschwankungen unterworfen sind. Die in
> der Lib eingestellten Timings berücksichtigen erhebliche Toleranzen. Bei
> 4 Mhz können diese nicht immer eingehalten werden. Bei 8 MHz ist das
> kein Problem.

von Falk B. (falk)


Lesenswert?

Joachim B. schrieb:
> sind also nicht weniger als 0 NOPs, ob es für 1,6MHz reicht kann
> getestet werden!

Ich hab sowas auch mal programmiert. Unter 5 MHz war da praktisch nix 
mehr machbar, bestenfalls mit "magischen" Frequenzen, die EXAKT die 
Bittimings mit Null NOPs schaffen. Das hab ich dann aber weggelassen, 
wozu auch? Es gibt den Sleep Mode, wenn man Strom sparen will!

Beitrag "Re: Frage zu IR-Remote+LED-Strips an AVR"

> aber stimmt schon ist sportlich, wobei ich kaum glaube das es den
> Aufwand lohnt!

Nö.

von Falk B. (falk)


Lesenswert?

Icke ®. schrieb:
> Der Erfinder der Routine schreibt:
>
> Tim  . schrieb:
>> Das Protokoll ist deutlich robuster
>> als es im Datenblatt erscheint.

Da haben schon viele Leute die gegenteilige Erfahrung gemacht.

von Johannes (menschenskind)


Lesenswert?

Hey Leute,
Danke für den detaillierten Input. Dann muss ich anderweitig sehen, ob 
ich den Stromverbrauch noch weiter senken kann.

Der µC wird am Ende der while in den "Idle" Modus geschickt und mit nem 
TimerInterrupt wieder "Active" gesetzt.
Im PRR habe ich den ADC und das USI deaktiviert. Aber gerade bei Lesen 
des Datenblattes gesehen, dass im ACSR noch der Komparator deaktiviert 
werden kann. Mal sehen, was das bringt.

von Falk B. (falk)


Lesenswert?

Johannes H. schrieb:
> Der µC wird am Ende der while in den "Idle" Modus geschickt und mit nem
> TimerInterrupt wieder "Active" gesetzt.

Schon mal gut, zieht aber immer noch relativ viel. Deutlich mehr spart 
man mit Power Save mode oder gar Power Down. Man kann auch den Watchdog 
Timer zum Aufwachen nutzen.

> Im PRR habe ich den ADC und das USI deaktiviert.

ADC ist sinnvoll, USI nicht, denn das zieht praktisch keinen Strom.

> Aber gerade bei Lesen
> des Datenblattes gesehen, dass im ACSR noch der Komparator deaktiviert
> werden kann. Mal sehen, was das bringt.

20uA.

von Icke ®. (49636b65)


Lesenswert?

Der Sinn des Stromsparens bei dieser Anwendung erschließt sich mir 
ohnehin nicht. Die paar mAh, die sich beim Controller einsparen lassen, 
verbraten die eingeschalteten LEDs in wenigen Minuten.

von Johannes (menschenskind)


Lesenswert?

Falk B. schrieb:
> Schon mal gut, zieht aber immer noch relativ viel. Deutlich mehr spart
> man mit Power Save mode oder gar Power Down. Man kann auch den Watchdog
> Timer zum Aufwachen nutzen.
Hm, das ist ein interessanter Hinweis. Danke! Aktuell nutze ich für die 
Leuchtroutine den Timer1 (Timer0 wird fürs Entprellen genutzt).
Diesen könnte ich dann im PRR deaktivieren, falls das genauso mit dem 
WatchdogTimer funktioniert.

Icke ®. schrieb:
> Der Sinn des Stromsparens bei dieser Anwendung erschließt sich mir
> ohnehin nicht.
Also die Platine ist so ein Wearable für Partys/Festivals und wird mit 
einer 2032 Knopfzelle betrieben. Aktuell im hellsten Modus(für dunkle 
Umgebungen sind die LEDs nur wenig ausgesteuert) hält die rund 8h, aber 
wahrscheinlich durch Qualitätsschwankungen bei den Knopfzellen haben die 
in letzter Zeit wesentlich kürzer durchgehalten.
Von daher versuche ich nun jede zusätzliche Stromsparmöglichkeit 
auszuloten. Im Durchschnitt hat meine Elektronik aktuell maximal 11mA 
Stromaufnahme.

: Bearbeitet durch User
von Gerald B. (gerald_b)


Lesenswert?

Johannes H. schrieb:
> Also die Platine ist so ein Wearable für Partys/Festivals und wird mit
> einer 2032 Knopfzelle betrieben. Aktuell im hellsten Modus(für dunkle
> Umgebungen sind die LEDs nur wenig ausgesteuert) hält die rund 8h, aber
> wahrscheinlich durch Qualitätsschwankungen bei den Knopfzellen haben die
> in letzter Zeit wesentlich kürzer durchgehalten.

Hier eine Knopfzelle verwenden zu wollen, ist in etwas so sinnvoll, wie 
per USB Kabel Starthilfe geben zu wollen.
Es gibt doch kleine, flache LiPos, wie bspw. die Clone vom Nokia bl-5c. 
Wie hast du übrigens Ub gepuffert? Ein 1000µ 6,3V Gel-Elko oder Tantal 
kann durchaus auch nochmal ne Stunde rauholen, wenn die Stromaufnahme 
sehr dynamisch ist. Die Effekte lassen sich da auch anpassen, das wenn 
die Batterie dem Ende entgegen geht, vermehrt stroboartige Effekte oder 
sowas, wie Knightriderblinken, eingesetzt wird.

von Icke ®. (49636b65)


Lesenswert?

Gerald B. schrieb:
> Hier eine Knopfzelle verwenden zu wollen, ist in etwas so sinnvoll, wie
> per USB Kabel Starthilfe geben zu wollen.
> Es gibt doch kleine, flache LiPos, wie bspw. die Clone vom Nokia bl-5c.

Die CR2032 sind nicht für so hohe Ströme ausgelegt. Ihre Kapazität von 
ca. 230mAh erreichen sie nur bei Entladung mit Strömen unter 1mA.

https://www.farnell.com/datasheets/1496885.pdf

Es gibt aber kleine LiPo-Zellen mit 300mAh, die nur unwesentlich größer 
und mit 11mA eher unterfordert sind. Die sollten einen 24h Rave 
problemlos durchhalten.

: Bearbeitet durch User
von Johannes (menschenskind)


Lesenswert?

Icke ®. schrieb:
> Es gibt aber kleine LiPo-Zellen mit 300mAh, die nur unwesentlich größer
> und mit 11mA eher unterfordert sind. Die sollten einen 24h Rave
> problemlos durchhalten.
Ja, ein Akku wäre mir auch lieber gewesen. Aber die Platine habe ich an 
viele Freunde verteilt und es musste eine Energiequelle sein, die man 
einfach austauschen kann, ohne dass extra noch ein Ladegerät nötig ist. 
Und eine USB-Buchse samt Ladeschaltung hätte die Platine auch wieder 
größer gemacht.
Und wenn man die Knopfzellen in größeren Mengen bestellt, dann kommt man 
auf nen Einzelpreis von ~30cent.

Gerald B. schrieb:
> Wie hast du übrigens Ub gepuffert? Ein 1000µ 6,3V Gel-Elko oder Tantal
> kann durchaus auch nochmal ne Stunde rauholen, wenn die Stromaufnahme
> sehr dynamisch ist.
Ich hab einen 4,7µF Keramikkondensator verwendet. Also werde ich mal 
etwas noch größeres einbauen.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.