mikrocontroller.net

Forum: Projekte & Code AVR Soft-PWM mit max. 256 LEDs


Autor: Horst Meier (horst)
Datum:
Angehängte Dateien:

Bewertung
12 lesenswert
nicht lesenswert
Und hier kommt - die 1000ste Variante der LED-Soft-PWM mit AVR.
Das Rad ist ja wahrscheinlich auch mehr als einmal erfunden worden...
Freilich geht das viel eleganter mit PWM-Treibern oder gleich 
WS2812-Stripes.
Und auf den ersten Blick könnte es scheinen, als ginge es alternativ zum 
gewählten parallelen Ansatz auch seriell via SPI, z.B. mit 
'595-Schieberegistern - würde eine Menge Pins am Controller sparen, aber 
für 256 LEDs ist die erforderliche Durchsatzrate an einem SPI (oder auch 
an zwei) nicht zu schaffen.


Bei der Beschäftigung mit der Frage, wieviele LEDs wohl von einem AVR8 
mit Soft-PWM gesteuert werden können, habe ich dann die 
Bitwinkelmodulation nochmal neu kreiert (den Begriff fand ich erst 
später im Internet, Beschreibung z.B. 
https://www.mikrocontroller.net/attachment/207919/...).
Um's auf die Spitze zu treiben, hab ich einen Proof of Concept mit 256 
LEDs aufgebaut. Ist ziemlich Retro geworden...
Die LEDs sind nicht gemultiplext, jede LED hat ihren eigenen 
Treiber-Ausgang. Da ich noch einige 74ALS574 hier liegen hatte, kamen 
diese zum Einsatz. 24 mA pro Ausgang bei Low-Pegel reichen dicke für die 
Standard-LEDs. Die LEDs sind diffuse 3mm Ultrahell-Typen, die alle ihre 
ca. 20 mA bekommen. Das ist möglicherweise etwas zu viel des Guten - 
sind alle an, strahlen locker 1000 Cd. Die ganze Konstruktion ist auf 
Lochraster (zwei 200x100 Platinen, in der Mitte zusammengenäht) 
aufgebaut, in den vier Ecken sitzt je ein 1,7 A-StepDown-Wandler, der 
die 5V Versorgungsspannung für jeweils 64 LEDs + Treiber bereitstellt. 
Die gesamte Schaltung wird von einem 12V/36W-Netzteil gespeist.
Als Antrieb dient ein ATmega1284P mit 20 MHz Takt (oder ein bißchen 
mehr, sagen wir 22).
Passend zur Jahreszeit ist's ein Stern geworden (vierzackig, die 
üblichen fünf Zacken lassen sich auf dem Lochraster nicht vernünftig 
umsetzen). Es sind 108 blaue, 88 rote und 60 grüne LEDs, mit dieser 
Verteilung gibt's tatsächlich so etwas wie weißes Licht, wenn alle 
zusammen eingeschaltet sind.
Die anhängende Schaltung soll nur das das Prinzip der Ansteuerung 
verdeutlichen und stellt keine Nachbauanleitung dar (alle ICs sind 
natürlich mit einem Stützkondensator versehen, die LEDs haben 
entsprechend ihrer unterschiedlichen Flußspannung auch unterschiedliche 
Vorwiderstandswerte usw.).
Im Prinzip wäre es auch möglich, die CLK-Eingänge der Treiber über einen 
1-aus-16 Decoder (2 x '238 oder ein '4514) zu steuern, statt "sbi 
PORTx,y" mit zwei Taktzyklen wäre dann halt "ldi r16,y : out PORTx,r16" 
mit je einem Taktzyklus zu verwenden, damit blieben ein paar Portpins 
verfügbar.

Bei der Inbetriebnahme hat sich gezeigt, daß durch die unvermeidlich 
weitläufige Verdrahtung und die schnellen Treiber Übersprecheffekte 
auftreten, durch die auch nicht direkt angesteuerte LEDs mit geleuchtet 
haben. Ein 220 pF-Kerko an jedem Treiber-CLK-Eingang schaffte 
zuverlässig Abhilfe.

Die softwaremäßige Umsetzung trägt vielleicht doch den einen oder 
anderen neuen Aspekt zum Thema bei.
Erreicht wurde ein 10 Bit-PWM-Äquivalent mit gut 110 Hz "PWM"-Frequenz 
(bei 20 MHz Controller-Takt), ein Flimmern ist nicht wahrnehmbar.

Die Interrupt-Routine ist vollkommen linear - weniger Jitter geht nicht 
- und verändert keine Flags.
;r15=0
interrupt:                   ;4 clocks for interrupt latency
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTA,0          ;2
        out PORTA,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTA,1          ;2
        out PORTA,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTA,2          ;2
        out PORTA,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTA,3          ;2
        out PORTA,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTA,4          ;2
        out PORTA,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTA,5          ;2
        out PORTA,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTA,6          ;2
        out PORTA,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTA,7          ;2
        out PORTA,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTD,0          ;2
        out PORTD,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTD,1          ;2
        out PORTD,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTD,2          ;2
        out PORTD,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTD,3          ;2
        out PORTD,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTD,4          ;2
        out PORTD,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTD,5          ;2
        out PORTD,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTD,6          ;2
        out PORTD,r15        ;1
        ld r14,X+            ;2
        out PORTB,r14        ;1
        ld r14,X+            ;2
        out PORTC,r14        ;1
        sbi PORTD,7          ;2
        out PORTD,r15        ;1
        ld r14,X+            ;2
        sts (OCR3AH),r14     ;2
        ld r14,X+            ;2
        sts (OCR3AL),r14     ;2
        ld r14,X+            ;2
        ld XH,X              ;2
        mov XL,r14           ;1
        reti                 ;5
                             ;165 clock cycles

Die vom Interrupt verwendeten Register sind exklusiv reserviert und 
werden deshalb nicht gesichert.
Die im RAM liegende Tabelle enthält für jeden Interrupt-Zyklus das 
auszugebende LED-Bitmuster, den Timer-Wert für den nächsten Zyklus und 
einen Pointer auf die Tabellendaten für den nächsten Schritt (im letzten 
Schritt dann halt wieder zurück zum Tabellenanfang).
Das Setzen des Bitmusters für eine LED in der Tabelle erfordert ca. 6 
us, die Berechnung der Effekte erfolgt in der letzten Periode (512 
Bitzeiten, ca. 4,5 ms) - Zeit genug, um bei Bedarf alle LEDs upzudaten. 
Der Prozessor wartet im Sleep-Modus auf den nächsten Timer-Interrupt, 
auch hier gibt's also keinen Jitter.

Beim Testen von langsamen Helligkeitsänderungen hat sich noch ein 
grundsätzliches Problem mit dieser Methode der LED-Modulation gezeigt - 
bei einigen Schritten war ein deutliches Flackern zu erkennen.
Ursache sind Inhomogenitäten im Ausgabe-Bitstrom bei kritischen 
Übergängen.

Beispiel (4 Bit, sonst wird's zu unübersichtlich):

Änderung von 7 auf 8
_________7________ _________7________ _________8________ _________8________
1|11|1111|00000000 1|11|1111|00000000 0|00|0000|11111111 0|00|0000|11111111
Für eine statische 7 werden acht Null-Bitzeiten ausgegeben, für eine 8 
entsprechend sieben.
Beim Wechsel von 7 auf 8 gibt es jedoch 15 Null-Bitzeiten 
hintereinander, die LED flackert (bei Änderung von 8 auf 7 sind's 15 
Eins-Bitzeiten, es flackert ebenfalls).
Bei 10 Bit Auflösung sind's natürlich entsprechend mehr Bitzeiten, wenn 
z.B. von 511 auf 512 gewechselt wird.
Ich habe mir dazu etwas einfallen lassen, bei Übergängen auf den beiden 
höchstwertigen Ausgabebits 8/9 werden die niederwertigen Bits 
entsprechend ausmaskiert bzw. auf 1 gesetzt. Damit ließ sich eine 
deutliche Verbesserung erreichen, ein Flackern ist nicht mehr 
wahrnehmbar. Das Handling erhöht die Zeit für das Setzen des Bitmusters 
pro LED auf 7.2 us (@20 MHz), immer noch kurz genug, um pro PWM-Zyklus 
alle LEDs updaten zu können (inkl. Effektberechnung).

Die Definition der Effekte erfolgt über Tabellen, der Code selbst ist 
sehr kompakt. Das Mapping der LED-Nummern auf die Ausgänge der Treiber 
wird mit einem Macro (bzw. 16 Sub-Macros) beim Assemblieren gemacht und 
kostet deshalb keine zusätzliche Rechenzeit des Controllers; es ist also 
vollkommen egal, welche LED an welchem Ausgang hängt.
Hier gibt's ein Beispielvideo, die Smartphonekamera war allerdings 
deutlich überfordert.
https://youtu.be/GGLlUPStPn8

Autor: Mw En (Firma: fritzler-avr.de) (fritzler)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da klappt einem glatt die Kinnlade runter!

Damit man noch länger an dem Gerät freude hat empfehle ich dir die 
Stepdowns auszutauschen.
Die dort verwendeten sind leider ziemliche Chinaböller.
So stimmt zB nichtmal der angegebene PWM Takt und beim durchtsten hatten 
die auch nen ziemlich miesen Wirkungsgrad.
Wer weis wann die Vollstoff auf die LEDs geben :/

Autor: Checker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

richtig geil, muss man neidlos anerkennen. Bienchen. Nur der Sinn vom 
Sleepmode erschliesst sich einem nicht.

Autor: Andreas I. (andy5macht)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mw E. schrieb:
> Da klappt einem glatt die Kinnlade runter!
>
> Damit man noch länger an dem Gerät freude hat empfehle ich dir die
> Stepdowns auszutauschen.
> Die dort verwendeten sind leider ziemliche Chinaböller.
> So stimmt zB nichtmal der angegebene PWM Takt und beim durchtsten hatten
> die auch nen ziemlich miesen Wirkungsgrad.
> Wer weis wann die Vollstoff auf die LEDs geben :/

Die sehen für mich wie die von muRata aus, mit denen habe ich bisher nur 
gute Erfahrungen gemacht.

Autor: Mw En (Firma: fritzler-avr.de) (fritzler)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
@Andreas,
murata wär ja gut, aber der sieht nah dem hier aus:
Ebay-Artikel Nr. 192349921551

Ich will ja nur, dass man länger Spaß hat an dem Stern :)

: Bearbeitet durch User
Autor: Axel Schwenke (a-za-z0-9)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mensch Meier!

Autor: Andreas I. (andy5macht)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mw E. schrieb:
> @Andreas,
> murata wär ja gut, aber der sieht nah dem hier aus:
> 
Ebay-Artikel Nr. 192349921551
>
> Ich will ja nur, dass man länger Spaß hat an dem Stern :)

Stimmt diese Bauform gibts von murata nicht.

Autor: Berthold (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Superprojekt :)

Wie lange hast du für den Aufbau gebraucht? Kannst du ein Video vom 
Stern in Betrieb auf youtube einstellen?

Autor: Julian W. (julian-w) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Berthold schrieb:
> Kannst du ein Video vom
> Stern in Betrieb auf youtube einstellen?

Ist im ersten Post verlinkt ;)

Autor: Mampf F. (mampf) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Horst M. schrieb:
> habe ich dann die
> Bitwinkelmodulation nochmal neu kreiert (den Begriff fand ich erst
> später im Internet, Beschreibung z.B.
> https://www.mikrocontroller.net/attachment/207919/...).

Oha, das hab ich vorher auch noch nicht gesehen - macht aber völlig Sinn 
und ist sogar noch einfach! 😍

Autor: Horst Meier (horst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Checker schrieb:
> Nur der Sinn vom Sleepmode erschliesst sich einem nicht.
Durch das Warten auf den nächsten Interrupt im Idle-Status ist 
sichergestellt, daß immer eine identische Anzahl von Prozessortakten bis 
zum Sprung in die Interrupt-Routine verwendet wird. Arbeitet der 
Prozessor irgendwelchen Code ab, kann es - abhängig von der gerade 
ausgeführten Instruktion - ein bis vier Takte dauern, bis es mit der 
Interruptbearbeitung losgeht --> Jitter.
Ob das eine wahrnehmbare Rolle spielt (Flicker), habe ich nicht 
untersucht, denn mit dem Sleep ist die Synchronisation auf die 
Interrupt-Zyklen auch viel einfacher.

Berthold schrieb:
> Wie lange hast du für den Aufbau gebraucht?
Naja, das ist schon eine ziemliche Verdrahter- und Löterei gewesen, da 
stecken etliche "zweite Schichten" nach dem regulären Feierabend drin...

Bzgl. StepDowns: Ja, das sind diese Chinaböller (einer war gleich von 
Anfang an defekt). Den Murata OKI515W36C hatte ich bei der Suche bereits 
im Fokus, aber zum Probieren waren mir vier Stück bei den üblichen 
Verdächtigen (Reichelt etc.) zu teuer.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.