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
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
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
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.
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
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
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 ;-)
@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?
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
@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
@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
@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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.