Hi, ich habe 27 LEDs, welche ich per Multiplexing ansteuere. Konkret handelt es sich um einen LED Cube nach diesem Schaltplan: http://www.ledstyles.de/index.php?page=Attachment&attachmentID=5162&h=14fd27d042c0853320f7f045e11838b113fe8373 Als Mikrocontroller verwende ich einen ATmega88. Dieser bietet mir über den Timer/Counter0 die Möglichkeit Hardware PWM zu realisieren. Jetzt stellt sich mir die Frage, inwiefern es möglich wäre mit nur einer Hardware PWM Leitung (nämlich Pin PB1) die Helligkeit aller LEDs zu regulieren. Ich dachte mir nun, dass ich einen weiteren Transistor einbaue, dessen Basis an PB1 kommt und der über Kollektor und Emitter die anderen drei Transistoren ein- und ausschaltet (bzw. dessen Stromfluss zwischen Emitter und GND). Prinzipiell müsste das klappen, aber jetzt stellt sich das Problem, dass ich die Hardware PWM irgendwie mit dem Multiplexing synchronisieren müsste, damit sich das PWM wirklich auf die aktuell leuchtende Ebene bezieht und nicht noch Ein- bzw. Auszeiten von der Ebene "davor" übernommen werden. Wahrscheinlich könnte man die Hardware PWM Zeiten einfach klein genug wählen, sodass der Effekt nicht so stark ausfällt, aber gefallen tut mir das trotzdem nicht. Wie würdet ihr das Ganze lösen? Ich hoffe, dass ich das Problem verständlich erläutern konnte.
Unlucky2012 schrieb: > Prinzipiell müsste das klappen, aber jetzt stellt sich das Problem, dass > ich die Hardware PWM irgendwie mit dem Multiplexing synchronisieren > müsste Du nimmst einfach für beides den gleichen Timer, Mode: Fast-PWM. Peter
Peter Dannegger schrieb: > Unlucky2012 schrieb: >> Prinzipiell müsste das klappen, aber jetzt stellt sich das Problem, dass >> ich die Hardware PWM irgendwie mit dem Multiplexing synchronisieren >> müsste > > Du nimmst einfach für beides den gleichen Timer, Mode: Fast-PWM. Warum einfach, wenn es auch kompliziert geht ;). Danke!
Peter Dannegger schrieb: > Du nimmst einfach für beides den gleichen Timer, Mode: Fast-PWM. Wäre es möglich das nochmals ein wenig genauer zu beschreiben? Welche Interrupts soll/muss ich verarbeiten. Ich habe mir das Ganze jetzt so vorgestellt: Ich benutze Timer/Counter0 mit einem Prescaler, den ich so wähle, dass der Bereich um 100 Hertz "optimal" eingestellt werden kann. Interrupt-technisch reagiere ich jeweils auf Timer Overflows. In der Interrupt Service Routine wechsle ich dann jeweils die aktuelle Ebene (Multiplexing eben). Parallel dazu benutze ich den Output Compare (OC0A), um über den Fast PWM Modus mein PWM zu implementieren. Dabei setze ich OC0A zunächst auf MAX (bei nicht invertierter PWM Logik), um ein konstantes Signal zu erhalten. Bei Bedarf senke ich diesen Wert dann. Ich wäre sehr erfreut, wenn das eben jemand bestätigen könnte, oder aber mir aufzeigen könnte wo ich hier Denkfehler mache. Das Timer/Counter Kapitel im Datenblatt ist wohl nicht das Einfachste ;). Vielen Dank schon einmal!
Übrigens: Der Grund warum ich noch einmal nachfrage ist, dass meine oben präsentierte Lösung, zumindest nach meinem Verständnis, noch nicht "perfekt" ist. Da ich zumindest noch einen anderen Interrupt verarbeiten muss, kann es natürlich vorkommen, dass der Timer/Counter0 Interrupt nicht "sofort" ausgeführt wird, sondern um ein paar Takte verzögert wird. Dadurch würden die LEDs aber etwas länger an (bzw. bei invertierter Logik aus) bleiben als gewollt. Mir ist bisher jedenfalls kein besserer Ansatz eingefallen und ich vermute, dass dies aufgrund der fehlenden Möglichkeit Interrupts zu verschalten, auch nicht möglich ist (ohne jetzt auf irgendwelche "dreckigen" Hacks zurückzugreifen, bei welchen man innerhalb einer ISR Interrupts wieder aktiviert, usw.). Was sagt ihr dazu?
> Ich dachte mir nun, dass ich einen weiteren Transistor einbaue
Warum sollte man eine Lösung wählen,
die ein zusätzliches Bauelement kostet,
wenn man es auch komplett in Software erledigen könnte,
ganz ohne zusätzlichen Timer ?
Andere Leute schaffen das auch...
MaWin schrieb: > Warum sollte man eine Lösung wählen, > die ein zusätzliches Bauelement kostet, > wenn man es auch komplett in Software erledigen könnte, > ganz ohne zusätzlichen Timer ? Das zusätzliche Bauelement kostet 4 Cent. Ich denke, dass man das (im Hobbybereich) nicht unbedingt als Kosten bezeichnen kann. Die Software Lösungen halte ich persönlich für "Ausweichlösungen" (und so verstehe ich auch diverse Anleitungen und Wiki Artikel), wenn man entweder mehr PWM Leitungen braucht als man zur Verfügung hat oder keine Timer mehr zur Verfügung hat, weil diese mit anderen Dingen beschäftigt sind. Beides trifft bei mir jedenfalls nicht zu und wieso soll ich nicht etwas verwenden, was der Mikrocontroller mir bietet. Peter Dannegger schrieb: > Beitrag "Jumbo-LED Uhr" Danke. Schaue ich mir in jedem Fall näher an.
An sich bietet sich hier eine Software-Lösung wirklich an. Die Frequenz zum umschalten der LED ebenen ist relativ niedrig und entsprechend hat man genug Zeit um PWM auch in Software zu machen. Dabei solle man PWM am besten auch nur auf die Länge eines Pulses beziehen, also einfach die Zeit variabel machen die die LEDs einer Ebene an sind. Geht es darum alle LEDs einer Ebene gleich zu dimmen, variiert man einfach die Zeit bis zum Umschalten auf die nächste Ebene und fügt dann noch ein mehr oder weniger lange Pause ein. Für die Variable Zeit kann da natürlich auch den Timer nutzen. Sollen alle LEDs einzeln gedimmt werden, fehlen für HW PWM ohnehin die Kanäle - für Software PWM reicht die Zeit schon recht weit, zumindest wenn man es effektiv in ASM programmiert, aber selbst in C sollen bei so wenig LED noch 8 Bits machbar sein.
Andererseits, der Baustein hat 4 HW-PWM, es würde genügen, die Pins anders zu verteilen. Aber warscheinlich ist es der ähnliche Aufwand als SW pwm zu betreiben.
Ok, vielen Dank. Werde mich dann wohl doch zu der Lösung mittels Software PWM entschließen. Gibt es eigentlich eine Möglichkeit im Vorfeld zu überprüfen, ob der Mikrocontroller das Ganze mitmacht? Die nächste Stufe wäre nämlich ein 8x8x8 Würfel, d.h. es wären jeweils 64 LEDs zu steuern (über entsprechende Latches natürlich). Wie sieht es hier mit Software PWM aus. Oder ist ein ATmega32 hier schon zu schwach auf der Brust?
> Die Software Lösungen halte ich persönlich für "Ausweichlösungen"
Andere dachten, daß Mikrocontroller genau dazu erfunden wurden.
Na so unterschiedlich sind die Menschen.
Michael Bertrandt schrieb: > Andere dachten, daß Mikrocontroller genau dazu erfunden wurden. Das Problem das ich mit Software PWM habe ist halt, dass es nicht wirklich 100% genau ist. Auf ein Interrupt kann auch mal verspätet (oder im schlimmstem Fall gar nicht) reagiert werden. Beim Hardware PWM habe ich solche Probleme i.A. nicht, wenn der Timer/Counter einmal eingestellt ist wird da stets fröhlich vor sich her geschalten, ganz unabhängig davon was im eigentlichen Programm gerade passiert. Ich hoffe einfach mal, dass sich so etwas durch "vernünftige" Programmierung in den Griff kriegen lässt.
So lange man nur eine Interruptquelle hat, und das sollte hier der Fall sein, gibt es eigentlich keinen Grund einen Interrupt zu verpassen. Auch die Verzögerungen halten sich normal in Grenzen. Man muss dabei auch nicht mit Interupts arbeiten - es geht auch einfach über die Laufzeit, zumindest wenn man in ASM Programmiert. Ohne ASM ist die Software PWM Lösung auch deutlich langsamer. Den Code für 8 Bit Software PWM kriegt man in ASM sehr gut hin - das ist eines der Beispiele wo ein C-Compiler mit der Optimierung an die Grenzen stößt weil z.B. C keine 1 Bit Variablen kennt. So lange man nur jede Ebene als Ganzen Dimmen will, ist die Softwarelösung auch bei 8x8x8 LEDs kein Problem. Will man da aber jede LED einzeln dimmen wird es mit Soaftware PWM wohl zu kapp - da würde vielleicht mit viel Anstrengung noch etwa 6-7 Bit an Auflösung hinzukriegen sein. Wenn man mit Software PWM alleine nicht mehr ganz hinkommt, gäbe es noch eine halb analoge Methode um für eine nichtlineare Teilung der PWM Schritte zu sorgen. Damit ist dann die begrenzte Zahl von Helligkeitsstufen besser nutzbar und man könnte auch mit nur 4-5 Bit PWM noch gut klar kommen, etwa so wie mit 8 Bit bei linearer Teilung. Die Hardware Lösung ist auch nicht so einfach. Zum einen braucht man viele HW Kanäle, schon bei der 3x3x3 Größe wären 9 Kanäle passend. Zum anderen müsste man die Umschaltung der Ebenen mit dem PWM synchronisieren - dafür würde man dann vermutlich auch so etwas wie einen zeitkritischen Interrupt brauchen. Zu der reinen Ansteuerung kommt dann immer auch noch das Auffrischen der Bildinformationen. So ein kleiner µC hat einen doch relativ begrenzten Speicher.
Ok, danke für deine Ausführung. Wie gesagt, werde jetzt einmal die Software PWM Lösung anvisieren, mal sehen, ob ich damit zufrieden zustellen bin ;). Übrigens: Ich programmiere zur Zeit in Assembler, insofern passt das ganz gut. Ob mein Code allerdings brauchbar und schnell genug ist, weiß ich (noch) nicht :). Da ich C aber halbwegs beherrsche, frage ich mich gerade wie die folgende Aussage zu verstehen ist: Ulrich schrieb: > weil z.B. C keine 1 Bit > Variablen kennt. C bietet ja zumindest sog. Bitfelder an, welche hier (https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Bitfelder) auch beschrieben werden. Im Endeffekt passiert dabei doch das selbe, wie wenn ich es zu Fuß per Assembler mache. Einzelne Bits innerhalb eines Registers werden zu "Variablen". Auch in Assembler ist ja das kleinste was ich so ansprechen kann mindestens ein Byte lang. Gut es gibt die Instruktionen "sbi", "sbr", usw. aber es hindert ja auch C (bzw. den Compiler) keiner daran von diesen Gebrauch zu machen. Oder sehe ich hier etwas falsch? Das soll jetzt keine "C vs. Assembler" Diskussion werden, sondern lediglich dem Verständnis dienen.
Ich habe das eben durch gerechnet. Wenn ich keinen groben (Denk-)Fehler drin habe, dann sehe ich absolut keine Möglichkeit das mittels Soft-PWM umzusetzen. Gehen wir mal von einem Takt von 16 MHz aus. Als Prescaler wähle ich 1. Meine benötige Frequenz setzt sich ja wie folgt zusammen: 3 100 256. Die 3 steht dabei für die Anzahl der Ebenen, die 100 für die "Auffrischungsrate", d.h. das Bild soll 100 Mal pro Sekunde aufgebaut werden und die 256 für meinen Software PWM Counter. Laut Datenblatt berechnet sich das Ganze wie folgt: f_OC0A = (f_clk / (2 N (1 + OCR0A))) Wobei N für den Prescaler steht. Stelle ich das Ganze um, ergibt sich: OCR0A = ((f_clk / (f_OC0A 2 N)) - 1) Setze ich alles ein, so ergibt sich ein Wert von 103,16. Das heißt ich bekomme jeden 103. Takt einen Interrupt, welchen es zu bearbeiten gibt. Bedenkt man, dass ich innerhalb meiner ISR dann 9 Ports vergleichen und steuern muss und die Ebenen multiplexen muss, so komme ich zu dem Schluss, dass das nicht machbar ist. Dazu kommt ja dann noch, dass ich außerhalb der ISR gerne noch das nächste Bild berechnen würde ;). Demnach taugt mein Ansatz nichts. Irgendwelche anderen Vorschläge ;)?
Ganz so schlimm ist das mit der Rechnung nicht. Das vergleichen der Werte mit dem PWM Wert dauert in ASM nicht wirklich lange. Wenn ich mich richtig erinnere waren es für den stark optimierten Code 5 oder 6 Zyklen die man für jeden PWM-Kanal braucht, sofern die IO Ports günstig liegen, also 8 Stück zusammen. Das heißt von den 103 Zyklen die man Zeit hat, bleibt noch etwa die halbe Zeit über. Das geht aber nur mit wirklich gut optimiertem Code. Einen Trick den man in ASM machen kann (aber kaum ein C Compiler) ist die Ergebnisse aus den Vergleichen direkt per Shift Befehl (z.B. ROL) in ein Byte zusammen zu fassen. Zum Weiterzählen des Index gibt es dann auch Befehle wie LD R1, x+, und die "Schleife" für die 9 LEDs einer Ebene kann man auschreiben. Dazu kommt noch, das ich zumindest auf die Doppelt Rechenzeit komme die man zur Verfügung hat, nämlich rund 200 Zyklen für einen PWM Schritt. Es ist aber zu überlegen ob man da wirklich Interrupts nutzt, denn schon für den Interrupt hat man einen Overhead von rund 16 Zyklen, selbst ohne retten der Register außer dem Status. Das Umschalten der Ebenen bracht fast keine Rechenzeit - da ist der Aufwand minimal, denn das kommt nur einmal auf 255 Schritte. Was man machen kann, ist die Zahl der Interruptsaufrufe zu reduzieren. Mit einem Schritt der nur die halbe Länge aktiv ist kann man das unterste Bit quasi direkt ausgeben, könnte also die Zahl der Aufrufe fast halbieren.
> Das vergleichen der Werte mit dem PWM Wert dauert in ASM > nicht wirklich lange Man muß überhaupt nicht vergleichen. Man muß die darzustellenden Werte nur ordentlich strukturiert im Speicher halten. Wenn a mit den bits a7 a6 a5 a4 a3 a2 a1 a0 und b mit den bits b7 b6 b5 b4 b3 b2 b1 b0 und c ... die Dimmwerte der LEDs sind, dann legt man folgende Bytes im Speicher ab Block1: Zeile1: a0b0c0d0e0f0g0h0 i0j0k0l0m0n0o0p0 Zeile2: x0y0z0... Block2: Zeile1: a1b1c1d1e1f1g1h1 i1j1k1l1m1n1o1p1 Zeile2: x1y1z1... : Block7: Zeile1: a7b7c7d7e8f7g7g7 i7j7k7l7m7n7o7p7 Zeile2: x7y7z7... und schiebt Block1 in Zeile 1 raus, macht kein Pause, schiebt Block2 in Zeile 1 raus, macht so lange Pause daß die Gesamtzeit doppelt so lange ist, schiebt Block3 in Zeile1 raus, macht eine 4 mal so lange Pause ... und schiebt Block 7 raus und macht eine 256 mal so lange Gesamtpause (in der man jede Menge nützliches tun kann, z.B. die Daten strukturieren). dann geht's nach Umschalten des Spaltentreibers zur Zeile 2 mit Block1. Bei 70Hz Bildwiederholfrequenz und 8 Spalten im Multiplex sind 1700us pro Zeile, bei 8 bit Helligkeitsauflösung muss Block2 7us nach Block1 rausgeschrieben werden, ein AVR mit 16MHz kann 1 Byte in 187ns verschieben, schafft also 37 bytes oder displays mit 300 x 8 = 2400 LEDs (4800 bei 7 bit Helligkeitsauflösung, 9600 bei 6 bit) in realtime zu dimmen und ein Video ablaufen zu lassen welches in 95% ungenutzer Rechenzeit berechnet werden kann - wenn man die Hardware nicht vergurkt hat sondern 8 bit weise ausgeben kann und programmieren kann.
MaWin schrieb: > dann legt man folgende Bytes im Speicher ab > > Block1: > Zeile1: a0b0c0d0e0f0g0h0 i0j0k0l0m0n0o0p0 > Zeile2: x0y0z0... > Block2: > Zeile1: a1b1c1d1e1f1g1h1 i1j1k1l1m1n1o1p1 > Zeile2: x1y1z1... > > : > Block7: > Zeile1: a7b7c7d7e8f7g7g7 i7j7k7l7m7n7o7p7 > Zeile2: x7y7z7... Könntest du das vielleicht nochmal ausführlicher beschreiben? So ganz schlau werde ich da nämlich nicht daraus. Was ist x, y bzw. z?
Ulrich schrieb: > Dazu kommt noch, das ich zumindest auf die Doppelt Rechenzeit komme die > man zur Verfügung hat, nämlich rund 200 Zyklen für einen PWM Schritt. Wie hast du denn gerechnet? Habe das eigentlich schon öfter in den Taschenrechner gehackt. Ich komme da immer auf einen Wert von 103 für OCR0A. Und selbst wenn es 200 Takte wären. Durch den Interrupt fallen einige weg und die Berechnungen (bzw. der Tausch der Buffer) nimmt ja auch nochmal ein paar Takte in Anspruch. Also wenn überhaupt, dann läuft das Ding am "Limit". Ich weiß nicht, ob es sich vor dem Hintergrund, dass jeder Befehl das Aus sein könnte, so gut programmiert ;).
Ich habe so gerechnet: Je Sekunde braucht man 3 * 100*256 Interruptaufrufe. Und bei Teilen von 16000000 durch diese Zahl komme ich auf 208,... . Die Idee von Mawin ist auch möglich. Das ist die Idee mit dem halb langen Intervall einfach weiter gedacht. Man erhält dann kein klassisches PWM mehr, sondern eine Reihe von zunehmend länger werdenden Intervallen, die je 1 Bit des PWM Wertes darstellen. Vielleicht hilft ein Beispiel für da Verständniss: um einen PWM Wert von z.B. 19, in Binärdarstellung 00010011 darzustellen fängt man mit einem kurzen Intervall für Bit0 an, in dem die LED an ist. Dann kommt ein doppelt so langes Intervall für Bit 1 , hier ist die LED auch wieder an. Dann kommen weitere je doppelt so lange werdende Intervalle wo die LED dann 2 mal aus und ein mal an ist. Insgesamt muss die LED für den 8 Bit PWM Wert nur maximal 8 mal umgeschaltet werden. Die Werte die man da jeweils an die Ports ausgeben muss kann man schon vorher umrechnen - das braucht auch nicht mehr Speicher als die PWM Werte in normaler Speicherung. Im Interrupt muss man also nur noch vorberechnete Werte aus der Tabelle in die Port übertragen und insgessamt kommen nur 24 (3 Ebenen mal 8 Bits) Interrupts für die komplette Darstellung eines Bildes. Der einige kleine Nachteil ist, dass es ein paar mehr Umschaltvorgänge gibt und damit eventuell etwas mehr Störungen. Normal sollte das aber nicht so schlimm sein.
> Könntest du das vielleicht nochmal ausführlicher beschreiben? Nicht wirklich, war doch schon lang genug der Beitrag. > So ganz schlau werde ich da nämlich nicht daraus. > Was ist x, y bzw. z? Weitere Bytes und deren Bits y0 y1 ... x0 x1 ... Im Speicher befinden sich Bytes hintereinander a0b0c0d0e0f0g0h0 i0j0k0l0m0n0o0p0 : mit den gemischten (hier niederwertigsten) bits der Dimmwerte, in jenau der Anordnung in der die LEDs adressiert sind. Bit a0 wird also so ausgegeben, daß LED_a dann leuchtet wenn es 1 war, bit b0 lässt LED_b leuchten. Hat sie lange genug geleuchtet, kommen die höherwertigen Bits Bit a1 wird also wieder auf der Leitung von LED_a ausgegeben bit b1 auf der Leistung von LED_b, und die LEDs leuchten dann doppelt so lange, weil ja auch die Wertigkeit der bits 2^1 ist. Eine LED a die 0x5C = 92/256tel an sein soll, ist also a0=0 a1=0 a2=1 a3=1 a4=1 a5=0 a6=1 a7=0 Aus 1us warten Aus 2us warten Ein 4us warten Ein 8us warten Ein 16us warten Aus 32us warten Ein 64us warten Aus 128us warten zusammen 92us der 256us an, nicht in Form von einem Puls der 92us dauert, sondern flimmernd, bitweise.
Ulrich schrieb: > Je Sekunde braucht man 3 * 100*256 Interruptaufrufe. Und bei Teilen von > 16000000 durch diese Zahl komme ich auf 208,... . Ja, nur sagt das Datenblatt das man im CTC Modus noch durch 2 teilen sowie 1 subtrahieren muss. Demnach müsste wohl 103 im Register landen. Wenn ich den alternativen Vorschlag gerade richtig verstehe, dann brauche ich anstatt von 256 Interrupts für das PWM "nur" noch 8. Das hört sich prinzipiell schon ganz brauchbar an, wobei ich mir da noch Gedanken machen muss, wie man das in Assembler am Besten umsetzt. So recht weiß ich jedenfalls noch nicht wie ich da am Besten vorgehen sollte.
MaWin schrieb: > zusammen 92us der 256us an, nicht in Form von einem Puls > der 92us dauert, sondern flimmernd, bitweise. Ok, das Prinzip hab ich zwar verstanden, nur frage ich mich gerade wie man das mit den Interrupts jetzt am Besten organisiert, da sich die Wartezeit ja immer verdoppelt. Ich habe mir überlegt, dass man im CTC Modus den OCR0A Wert immer verdoppeln könnte. Nur müsste man dann dafür Sorge tragen, dass der ursprünglich kleinergleich 15 bleibt. Und das ist wohl nicht immer möglich :(.
> nur frage ich mich gerade wie > man das mit den Interrupts jetzt am Besten organisiert Gar nicht, das macht man in der Programm-Hauptschleife wenn man das letzte bischen Geschwindigkeit rauskitzen will weil dann der Interrupt-Overhead entfällt, obwohl es technisch kein Problem ist, im Interrupt den nächsten Interruptzeitpunkt neu festzulegen.
Die ganz kurzen Zeiten (die ersten 2 Werte) macht man ggf. direkt per Warteschleife (sind ggf. nur ca. 200 Zyklen), es geht aber auch per Interrupt. Die Längeren Zeiten kann man durch verändern der Wartezeit für den Interrupt machen. Zumindest mit einem 16 Bit Timer ist das kein größeres Problem: die Zeiten für den Interrupt kann man vorab berechnen. Mit einem 8 Bit Timer geht es auch, vor allem wenn der Prescaler (z.B. 256) passt und die erste Zeit per Warteschleife kommt. Bei der genauen Frequenz muss man ggf. ein paar Abstriche machen, aber die genaue Wiederholfrequenz ist meist nicht so wichtig.
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.