Forum: Mikrocontroller und Digitale Elektronik PWM für Displaymatrix


von Jan (Gast)


Lesenswert?

Hallo allerseits

Ich möchte eine Displaymatrix aus etwa 250 LEDs aufbauen. Die LEDs
möchte ich mit einem µC ansteuern und über diesen in der Helligkeit
regeln. Es könnten etwa im RAM Daten über die Helligkeit der LED
abgelegt werden, die der µC dann liest und über seinen Timer eine PWM
realisiert.

Leider ist der µC den ich benutze (AVR) zu langsam dafür, so daß ich
die Matrix in kleinere Untereinheiten aufteilen wollte, die dann
seriell miteinander kommunizieren.

Nun genügt das meinen Berechnungen nach auch nicht - immernoch zu
langsam. Ich komme auf 30 LEDs pro AVR, allerdings auch mit 256 Stufen
in der Hellikeit. Bei dieser Rechnung habe ich dann auch kaum noch Zeit
für meine serielle Übertragung, die die Daten ja im Worst-Case
vollständig austauschen müssen, damit nicht auf veraltete Daten
zugegriffen wird :-/

Was habe ich da für Chancen? Wie realisiert man solch eine große
Matrix?

Kann man soetwas über einen D/A-Wandler machen, dessen Ausgang man dann
multiplext? Mehr fällt mir nämlich nicht mehr ein...

Gruß,
Jan

von Stefan Kleinwort (Gast)


Lesenswert?

Hi Jan,

ich habe das vor langer Zeit mal gemacht, mit einem 68HC11 (40 LEDs).
Das Problem, was Du zusätzlich hast: der Helligkeitseindruck ist nicht
linear mit der eingestellten PWM. Such mal hier, vor ca. 2 Wochen haben
wir darüber diskutiert.

Meiner Meinung nach brauchst Du eine PWM-Auflösung von 10-11 Bit, um
einen stufenlosen Eindruck zu erhalten. Bei kleinerer PWM-Auflösung
siehst Du bei sehr geringer Helligkeit die Stufung zwischen den
einzelnen Helligkeitsleveln. Du kannst PWM und Multiplexen mischen
(hatte ich damals gemacht), d.h.: jede LED erhält einen Zeitschlitz von
10% (Beispiel), in dem sie zwischen 0% bis 100% angesteuert wird. Du
könntest z.B. eine Matrix 10 * 24 aufbauen, sprich: Du brauchst 24
PWM-Kanäle. Je nach Anforderung an die PWM ist das vielleicht gerade
noch mit einem mc machbar.

Wichtig bei dem Verfahren: der maximale Puls-Strom für die LEDs muss
Deinen Anforderungen genügen, die neuen Power-LEDs kann man leider
nicht so hoch (1/10) pulsen.

Datenübertragung: ausser PWM könnte so ein LED-Matrix-mc fast nichts
anderes mehr machen. denkbar ist, die Datenübertragung von einem
Host-mc per SPI zu machen (schnell), und zwar in einem
Zusatz-Zeitschlitz (in dem keine LEDs angesteuert werden, sondern nur
Datenübertragung passiert).

Viel Spass, Stefan

von Jan (Gast)


Lesenswert?

Hallo Stefan

Hmm, Du hast meine Gedanken ein bißchen geordnet :-)

Das Problem der Nicht-Linearität ist mir auch schon aufgefallen, denn
eigentlich sind 8 Bit Auflösung mehr als genug. Nur bei kleiner
Helligkeit gibt es eben Probleme. Da ich es auch nicht schaffe, die
LEDs auf 0% runterzuregeln, muss ich diesen Zustand abfangen und
getrennt prorammieren. Tja, und so komme ich inzwischen auf 29 Befehle
pro LED.

Wenn ich jetzt meinen Timer auf CK/1024 takte (also alle 1024 Takte den
Timer erhöhe), dann habe ich auch 1024 Takte Zeit, meine LEDs zu regeln.
Rechne ich 300 Takte für die Datenübertragung, also den
"Extra-Zeitschlitz" wie Du ihn treffend nennst, bleiben noch rund 700
Takte. 700 Takte / 29 Takte pro LED ~ 24 LED. Und das ganze bei einer
benötigten Taktfrequenz von 256 (Helligkeitsstufen) * 1024 (Timertakt)
* 50Hz (damits nicht flimmert) = 13MHz. Nicht sehr berauschend :-((

Gibt es "Hardware-PWM"-Lösungen? Was ist mit der Idee des DAC?

Ich stelle mir das so vor: DAC-Werte liegen im RAM, werden geladen,
dann gewandelt und zu guter letzt noch gemultiplext. Einen Wandler der
schnell genug ist, müsste sich finden lassen...

Aber wie in aller Welt hast Du das mit einem 68HC11 und 40 LEDs
hinbekommen Stefan? Der ist ja noch langsamer - nicht schlecht!

Gruß,
Jasper

von Christof Rieger (Gast)


Lesenswert?

Die Frage ist was der Controller sonst noch machen muß, wenn er z.B. nur
alle 20ms die 250 Heligkeitswerte up-Daten muss könnte es mit einer
effizient programmierten Software PWM und einem ATmega162 oder
ATmega169 bei 16 Mhz gehen. Die Matrix würde ich 16x16 auslegen. Da
braucht man die wenigsten Ports.
@Jan
sag mal um die visuelle Liniarität zu erzeuge muß die Auflösung doch im
"Dunkleren Bereich" höher sein, oder lieg ich da falsch.

von Stefan Kleinwort (Gast)


Lesenswert?

Naja, so schlecht war der 68HC11 ja auch nicht: der Timer hatte 4
Output-Compare und konnte mit 2Mhz laufen. Die 4OC musste ich nur
zyklisch mit den Werten für jeweils 10 LEDs laden, die Matrix
weiterstellen - das wars. Ach ja, 24 Potis und 40 Taster hat so ein
Modul auch noch abgefragt. Das Programm lief in den 512 Bytes im
EEPROM, in dem Thread findest Du den Code, wenn es Dich interessiert:

http://www.mikrocontroller.net/forum/read-1-118198.html#118389

Ich würde die Programmierung etwas anders angehen. Nicht in einzelnen
LEDs denken. Sondern gleich in 8-Bit-Ports. Da hast Du 8 auf einen
Streich ;-) Oder besser noch: in 3 8-Bit-Ports gleichzeitig. Stell Dir
eine Tabelle vor, in dem definiert sind:

Bitmuster Port 1 (LEDzeile 1-8)
Bitmuster Port 2 (LEDzeile 9-16)
Bitmuster Port 3 (LEDzeile 17-24)
Counter-Compare

Die Tabelle hat 24 Einträge, also für jede LED eine. Die Tabelle mit
Werten zu füllen ist etwas komplexer, aber dazu später mehr. Von dieser
Tabelle gibt es 10 Stück, also für jede LEDspalte eine extra Tabelle. In
der Tabelle speicherst Du nur Änderungen an den Ausgängen, und den
Counter-Stand, an dem diese Änderung passiert. Die Tabelle ist so
geordnet, dass die Counter-Compare-Werte aufsteigend geordnet sind.
Also z.B. so:

;********  Port1     Port2     Port3  Counter-Compare
   db  00000001b 00000000b 00000000b  20  ;LED0 in Port 1 an
   db  00000001b 00000000b 00000010b  35  ;LED1 in Port 2 dazu
   db  11000001b 00000000b 00000010b  67  ;LED 6+7 in Port 1 dazu
... usw.

Der Software-PWM-Teil Deines Programm wird jetzt sehr einfach: den
Counter mit dem aktuellen Counter-Compare-Wert in der Tabelle
vergleichen, bei Gleichstand die Port-Tabellenwerte auf die 3 Ports
ausgeben, auf den nächsten Tabelleneintrag weiterschalten (TabPtr+4),
fertig.

Wenn wirklich 8 Bit PWM reichen, dann hast Du dafür Zeit: 100Hz (nicht
drunter, bei LEDs siehst Du sonst das Flackern!)  10 Spalten  256 =
256khz, also 4us.
Das sollte dann sogar als Interrupt gehen. In diesem Fall besser die
Delta-Zeit statt die absolute Compare-Zeit in der Tabelle halten,
wieder eine zeitkritische Rechnung weniger ...
Bedenke, dass der IR nicht alle 4us auftritt, sondern nur 24-mal in
einer ms (100Hz*10). Die 4us sind minimum-worst-case zwischen 2 IR.
D.h. für das main gibt es genügend Zeit, um neue Werte vom Host zu
empfangen und in die Tabellenwerte umzurechnen.

Viele Grüße, Stefan

von Markus (Gast)


Lesenswert?

Hallo Jan,

ich verstehe Dein Problem noch nicht so ganz. Beschreib doch mal
genauer, wie Du die einzelnen LEDs ansteuerst.

Wenn Du die 250 LEDs als 16x16 Matrix an einem(!) Mikrocontroller
ansteuerst und 250 Helligkeitsstufen mit 50Hz willst, dann brauchst Du
dazu 16*250*50=200.000 Interrupts/s. Bei 16MHz sind das 80 Takte pro
Interrupt, das ist etwas vermutlich zu knapp. Wenn Du mit den
Anforderungen etwas runtergehst (z.B. 50 Helligkeitsstufen und nur
25Hz) geht das aber ganz sicher (das wären dann 800 Takte pro
Interrupt).

Ich verstehe auch noch nicht so ganz, warum Du 29 Takte für eine
einzelne LED brauchst, das sollte auch schneller gehen. Deine
Sonderbehandlung für LED aus sollte sich eigentlich auch vermeiden
lassen, beschreib doch mal näher, warum Du sie nicht einfach so auf 0%
runterregeln kannst.

Datenkommunikation will man in Deinem Fall nur mit
Hardwareunterstützung machen, also einen AVR benutzen, der einen
Hardware U(S)ART bzw. SPI hat (z.B. die ATMegas). Denn dann kann man
Daten im Hintergrund senden und empfangen, während die CPU gleichzeitig
etwas anderes macht.

Markus

von Jan (Gast)


Lesenswert?

Puh, das war jetzt starker Tobak! Also folgendes:

Markus, ich hab von PWM an sich eher wenig Ahnung. Deshalb hatte ich
mir ein kleines Test-Programm geschrieben, das mir eine Software-PWM
realisiert. Nach dem Optimieren hatte ich eine Laufzeit von pauschal 15
Takte für Aufruf und Schleifenbedingung plus 29 Takte für jede LED im
Worst-Case. Daher kommen die genannten 29 Takte.
Das Programm nimmt einen Soll-Timerstand und vergleicht ihn mit dem
Ist-Timerstand. Sind beide identisch, wird die entsprechende LED
invertiert und der neue Soll-Timerstand berechnet, indem zu dem
Ist-Timerstand der Wert der Leuchtstärke (bzw. dessem invertierter
Wert, falls die LED aus sein soll) addiert wird. Ich errechne also
immer den Abstand und benutze keinen festen Schwellwert fürs
umschalten.
Das Problem mit dem auf 0% runterregeln tritt auf, wenn ich die
Leuchtstärke einer LED auf 0 setze und der Zustand dieser LED dann
abgefragt wird. Er nimmt den Ist-Timerstand, addiert die 0 hinzu und
schaltet die LED an. Der Timer-Clock ist langsamer als der System-Clock
und so trifft er beim nächsten Durchlauf wieder auf die LED. Denn: Ist-
und Soll-Timerstand (wurde ja mit 0 aufaddiert) stimmen überein und er
schaltet die LED wieder aus. Den nächsten Soll-Timerstand errechnet er
diesmal zu FFh (eben 0 invertiert) und solange bleibt die LED aus.
Eigentlich ist die LED also nur sehr kurz an - trotzdem ist das nicht
gleich "aus" und man sieht sie schwach leuchten :-( Deshalb die
Sonderbehandlung.
Bei der Verdrahtung bin ich noch ziemlich offen. Entweder als 16x16
Matrix oder auch mit 1zu16-Dekodern, was natürlich auch auf eine 16x16
Matrix hinausläuft.

Stefan, ich musste ein bißchen nachdenken, bis ich Deinen Vorschlag
verstanden hatte. In diese Richtung hatte ich schon gedacht, bin dann
aber wieder davon abgekommen, weil das Sortieren der Tabelle meiner
Meinung nach zu lange gedauert hätte. Denn im Worst-Case, bei dem ich
die gesamte Tabelle neu berechnen müsste, hätte die Zeit nicht
ausgereicht. Es müssen dann alle 24*10*4=960 Einträge neu berechnet
werden. Wie soll das gehen, ohne daß das PWM-Signal verloren geht? Ok,
mit einem Interrupt klappt das insofern, als daß ich nicht 100% gültige
Daten benötige, also ich auch noch auf die veralteten Daten zugreifen
könnte...

Also ich werde das mal ausprobieren mit dieser Methode! Vielleicht hab
ich ja was übersehen bei meiner Überschalgsrechnung ;-)

Danke für die vielen Tipps,
Jan


P.s.: Ja, die Auflösung muß im unteren Bereich größer sein. Ehrlich
gesagt sieht man bei 8 Bit auch schon eine Abstufung, aber ich hab mit
den 240 LEDs schon genug zu kämpfen ;-)

von Markus (Gast)


Lesenswert?

@Jan:
Ich hätte das mit der Software-PWM so gemacht:
Du nimmst a) einen Zähler, der von 255 - 0 zählt und bei jedem 16.
Interrupt um 1 verringert wird und b) pro LED einen Helligkeitswert.
Dann vergleichst Du für jede LED (das sind aber bei einer 16x16 Matrix
nur 16 LEDs pro Interrupt) den Helligkeitswert mit dem Zähler. Ist der
Helligkeitswert größer als der Zähler, dann machst Du die LED an, sonst
aus. Dadurch bekommst Du zwar keine 100% Helligkeit (sondern nur 99,6%)
aber auf der anderen Seite eben auch 0% ohne irgendwelche Tricks.

@Stefan:
Mir ist noch noch nicht so ganz klar, wozu man das Output-Compare
braucht. Das geht doch auch ohne. Ich denke aber, daß die Tabelle
größer wird. Bei 250 LEDs (=32 Byte) und 256 Helligkeitsstufen braucht
man eigentlich 32*256=8KB RAM. Oder mißverstehe ich Dich hier?

von Stefan Kleinwort (Gast)


Lesenswert?

Hi Jan,

ich habe nochmal nachgerechnet. Das Ganze sollte in einem Interrupt
funktionieren, ein IR hat fast nichts zu tun, ich komme auf 29
Clockzyklen inkl. Ein- und Aussprung.
Dann hat das Main-Programm alle Zeit der Welt, um diese Tabellen zu
berechnen. (ich schätze ca. 1ms pro Tabelle).
Das Prinzip mit IRs ist folgendes:

* Bei Beginn eines PWM-Blocks werden alle Ports auf inaktiv (LEDs aus)

  gesetzt
* Der Timer (OCR) wird mit dem Comparewert aus dem ersten
  Tabelleneintrag geladen. Ist der Timer abgelaufen, wird der IR
  ausgelöst:
* der IR lädt die Portwerte aus der Tabelle und gibt sie auf die Ports
  aus. Damit ist die erste LED eingeschaltet (codiert in den
  Portwerten)
* der Timer (OCR) wird mit dem Comparewert des nächsten
  Tabelleneintrags geladen
* das Ganze wiederholt sich 24-mal, dann ist ein PWM-Block
  abgeschlossen (ca. 1ms)

Das funktioniert natürlich nur, wenn "etwas" optimiert wird: der IR
muss eigene Register reserviert bekommen, damit die push/pop entfallen
können.

Ich hoffe, das Ganze ist einigermassen verständlich erklärt...?


@Markus:
Du brauchst nicht für alle 256 PWM-Werte einen Tabelleneintrag, sondern
nur für die, bei denen sich auch wirklich was ändert. Also maximal 24.
Jede Tabelle hat also 4 Bytes * 24 = 96 Bytes, 10 Tabs braucht man.

Ich habe übrigens 10 Spalten (statt 16) aus 2 verschiedenen Gründen
genannt:
* der Peak-Strom ist nur *10 statt *16
* die verfügbare Zeit pro PWM-Block ist größer -> bessere
  PWM-Auflösung

Viel Spass, Stefan

von Markus (Gast)


Lesenswert?

@Stefan:
Ich habs immernoch nicht verstanden. Wenn man für jede LED eine andere
Helligkeit haben möchtest, dann muß man doch für jede LED die
Helligkeit speichern.

Du brauchst maximal 24 Byte pro Spalte, das ganze mal 10 Spalten gibt
240 Byte. Du rechnest da "4 Bytes * 24 = 96 Bytes". Woher kommen
diese 4 Byte?

Markus

von Stefan Kleinwort (Gast)


Lesenswert?

@Markus:
Du teilst die LEDs in 10 PWM-Blöcke a 24 LEDs.
In jedem dieser PWM-Blöcke gibt es 24 Ereignisse - nämlich immer dann,
wenn eine LED sich ändert. Der Trick besteht jetzt darin, nicht das
Ganze bitweise aufzudröseln, sondern bei jedem dieser 24 Ereignisse den
kompletten Portzustand auf die LED-Zeilenports auszugeben. Das ist zwar
ziemlich brute-force, ergibt aber einen sehr schnellen Interrupt.

Jedes dieser Ereignisse besteht also aus:
  * 3 Byte für 3 Portzustände -> 24 Bits -> 24 LEDausgänge
  * 1 Byte Counterwert bis zum nächsten Ereignis

Also 4 Byte (3 für Ports + 1 für Counter) mal 24 Ereignisse -> 96
Bytes.

Stefan

von Markus (Gast)


Lesenswert?

@Stefan:
Jetzt verstehe ich es. Du brauchst also pro Spalte (=PWM-Block) 96
Byte, bei 10 Spalten also 960 Byte insgesamt. Das ist etwa das
vierfache im Vergleich zu meiner Lösung, allerdings hast Du dadurch
viel weniger Prozessorlast.

Markus

von Florian Scharf (Gast)


Lesenswert?

Hi Jan,

lustigerweise bin ich grad dabei so gut wie das selbe zu bauen (Matrix
aus 12x3 RGBW-Pixeln). Sind also insgesamt 144 LEDs, die ich auf 4µCs
aufgeteilt habe, die nichts anderes machen, als zu PWMmen. Zusätzlich
kommt noch ein µC hinzu, welcher dann die Berechnung macht, was
angezeigt wird, nen 16x2 Display, nen paar Knöpfe und eine serielle
Schnittstelle hat.

25Hz als PWM Grundfrequenz anzunehmen ist ne Größenordnung zuwenig :(

Hintergrund ist, dass man zwar die LED dauerhaft leuchten sieht, aber
wenn man mal das Auge bewegt, den totalen Stroboskopeffekt hat. Ich hab
jetzt meine LEDs mit nem knappen Kilohertz getaktet, das geht gerade so.
Die Kommunikation mache ich derart, dass ich den PWMµC mit einem
Timertakt füttere, in dem aus einem Datenarray der jeweils aktuelle
Wert auf die Ports geschrieben wird. Das hat höchste Priorität. In der
verbleibenden Rechenzeit geschieht die Kommunikation mit dem SteuerµC
und die Berechnung des Datenarrays.

Zur Kommunikation habe ich einen Parallelbus, an dem der Steuer µC
einen Helligkeitswert anlegt, und auf einem extra Kanal ein Strobe
sendet. Der jeweilige Controller speichert den Wert ins Array, erhöht
einen Counter und zieht einen ACK-Pin kurz auf High. Der SteuerµC geht
dann einfach zum nächsten Wert, sendet den Strobe, und wartet darauf,
dass der PWMµC sich den Wert abgespeichert hat, usw. So kann ich in
etwa 1/15s das komplette "Display" beschreiben. Reicht für meine
Anwendungen. In früheren Versuchen hab ich immer ein Flackern in der
PWM gehabt, jedesmal wenn die Daten über den Bus kamen, deshalb hab ich
die PWM als höher priorisiert als die Daten gesetzt.

Kannst dich ja mal bei mir per icq melden, meine Nummer ist die
72227556.

der Flo

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.