Hi! Ich möchte mir eine Soft-PWM bauen, allerdings 10-Bit und nicht 8-Bit, da ich eine exponentielle Kurve verwende um die Lichtempfindlichkeit des Auges etwas nachbilden und Farben besser darstellen zu können. Bei einer Taktfrequenz von 16Mhz, einem Vorteiler von 128 und einer Auflösung von besagten 10-Bit (1024 Werte) ergibt das eine flimmerfreie PWM-Frequenz von flimmerfreien 122Hz. Mein Problem besteht jedoch darin, dass ein Vorteiler von 128 bedeutet, dass ich zwischen einem Timertakt nur 128 MCUtakte habe. In dieser Zeit müssen neue Farbwerte für R, G und B geladen werden, der entsprechende exponentielle Wert aus der Tabelle geholt werden, alle Werte aufsteigend sortiert werden und noch bestimmen in welchem Timerzyklus der Wert liegt (ich habe nur einen 8-Bit Timer, allerdings eine 10-Bit PWM. Um einen PWM-Wert von 800 zu erreichen muss der Timer dann erst 3 mal durchlaufen um im 4. Zyklus dann die ISR auszulösen). Meint ihr das reicht dafür oder wirds eng? Bei 20Mhz (die ja der ATtiny26 schon offiziell nicht mehr packt) kann ich den Vorteiler auf 256 setzen weil eine PWM-Frequenz von 70Hz noch ausreichen könnte (vll flimmerts auch da schon). Ansonsten müsste man das ganze verkomplizieren indem man die Berechnungen der nächsten Werte irgendwann zwischendrin ausführt. lg PoWl
Warmum machst du die Berechnung im Timer? Ich würd die Berechnung in ner Methode machen, die von der Mainloop aufgerufen wird und nur die 3 pwmvariablen setzt. im Timer erhöhst du dann einen Counter (und fängst bei 4096 wieder von vorn an) und vergleichst die 3 Variablen mit dem Counter. Wenn du drunter liegst, dann setzt den Portpin, wenn drüber dann löscht ihn. sollte sich in 30-40 Takten erledigen lassen:
1 | cntr++ |
2 | cntr &= 0x03FF // 10 Bit |
3 | if (cntr < redPWM) { PORTA |= 1 << PA1; } else { PORTA &= ~(1 << PA1); } |
4 | if (cntr < greenPWM) { PORTA |= 1 << PA2; } else { PORTA &= ~(1 << PA2); } |
5 | if (cntr < bluePWM) { PORTA |= 1 << PA3; } else { PORTA &= ~(1 << PA3); } |
oder evtl sogar kürzer:
1 | cntr++ |
2 | if (cntr == 4096) { |
3 | PORTA=0 |
4 | cntr=0 |
5 | }
|
6 | if (cntr == redPWM) { PORTA |= 1 << PA1; } |
7 | if (cntr == greenPWM) { PORTA |= 1 << PA2; } |
8 | if (cntr == bluePWM) { PORTA |= 1 << PA3; } |
(code ungetestet!)
Ja, es gibt hier ja einen Soft-PWM artikel. In dem wird das auch so gemacht. Hier wird halt der Vorteil des Timer compare-modes ausgenutzt. Nach der klassischen Methode wird zu jedem Timer-Overflow ein Interrupt ausgeführt und darin geprüft ob sich an den Ports was verändern soll oder nicht. Nun habe ich dann allerdings 1020 unnötige Interrupts die mir entsprechend viel MCU-Zeit auffressen. Daher liegt es nahe die wichtigen Aktionen einfach in aufsteigender Reihenfolge zu sortieren und direkt zum geplanten Zeitpunkt auszuführen. Das mag halt auch berechnet werden. Trotzdem danke natürlich :-)
MCU-Zeit zu verbrauchen stört doch nicht, wenn Du in der Zeit nix anderes tun musst... Falls es vorberechnet werden soll, muss die Vorberechnung doch nicht in einer ISR gemacht werden. Die Rechnung wird gemacht und wenn der erste PWM pro Zyklus dran ist (zB im Overflow-IRQ), wird eine Kaskade von OC-IRQs ausgelöst. 128 Ticks sind nicht viel, Du musst ja immer noch den ISR-Overhead einrechnen. Falls eine IRQ schon getriggert wird währen noch die ISR aktiv ist, gibt's eben einen kleinen Jitter, der aber wohl undramatisch ist und nur auftaucht, denn 2 Dutys nebeneinander liegen. Und dann bleibt immer noch der Weg, von Hand zu optimieren. Anschauen, was gcc so macht (*.s), rauswerfen, was unnötog ist (zB push/pop vom zero_reg) und als *.S zum Projekt dazu.
> Nun habe ich dann allerdings 1020 unnötige Interrupts ...
da hast du natürlich recht, ist natürlich sinnlose Verschwendung von
Rechenzeit.
ich denke/hoffe mal dass die PWM's alle an einen Port liegen
dann könntest du dir "außen" die markanten Stellen berechnen, wann du
was machen musst.
Du legst dir also eine Tabelle an, mit dem Portstatus und dem nächsten
Compare-Wert an. Ein Cursor zeigt dann immer auf den aktuellen Eintrag.
Wenn du die Tabelle lang genug machst, und im Portstate einfach den
gleichen Status wieder rein schreibst, müsstest du das auch mit den
Overflows des 8 Bit-Timers hinbekommen können.
somit ist maxCursor = #Anzahl der Events + 4 (für 4 Overflows)
1 | int nextComp[8]; |
2 | int portState[8]; |
3 | int cursor; |
4 | int maxCursor; |
5 | ISR(TIMER1_COMPA_vect) { |
6 | OCR1A = nextComp[cursor]; |
7 | PWM_PORT = portState[cursor]; |
8 | cursor++; |
9 | if (cursor >= maxCursor) { cursor = 0; } |
10 | }
|
Ich mach mal ein Beispiel: Rot 10% = 102 Takte, Grün 40% = 409 Takte, Blau 80% = 819 Takte, da alle Events zu unterschiedlichen Takten statt finden muss maxCursor = 7 sein cursor Comp RGB-Port 0 0 000 Reset bei 0 1 205 001 1024-819 = 205 -> Blau ein 2 0 001 Port unverändert lassen, erster 8-Bit Overflow 3 0 001 Port unverändert lassen, zweiter 8-Bit Overflow 4 103 011 1024-409 = 615 = 2*256 + 103 -> Grün ein 5 0 011 Port unverändert lassen, dritter 8-Bit Overflow 6 154 111 1024-152 = 992 = 3*256 + 153 -> Rot ein 0 0 000 Reset bei 0 (der 8-Bit timer ist nun 4x durch) Die Berechnung der Tabelle ist vermutlich schon etwas tricky, aber dafür bleibt die ISR-Routine sehr schlank. Vielleicht bringt dich diese Idee etwas weiter. Gruß Roland
@ Roland Praml (pram) >Die Berechnung der Tabelle ist vermutlich schon etwas tricky, aber dafür >bleibt die ISR-Routine sehr schlank. Ist es, aber es geht. Soft-PWM MFG Falk
schau mal hier rein: Beitrag "Glühwürmchen in Rotkohlglas gefangen" In diesem Projekt werden an 4 Pins des AVRs (ATTiny45) 12 LEDs per Charlieplexing mit einer 8 Bit PWM bei 122Hz (8MHz Takt) betrieben. Die minimale Rechenzeit der PWM Routine (übrigens mit Wave-Mustern angesteuerte LEDs) beträgt 0.76 Prozent, die maximal nötige Rechenzeit beträgt 1.46 Prozent. Der Prescaler ist auf 64 eingestellt bei 8 Mhz Takt. Die Timer Overflow Routine (in Datei isrs.S) benötigt aber maximal 192 Takte an Rechenzeit. Also länger als ein Timertakt eigentlich ist. Das ist aber garnicht ein Problem, es fallen quasi 3 Timertakte unter den Tisch. Den Dutycycle/Helligkeit kann man also nur von 0 bis 252 einstellen. Bei den längeren Dutyclyce der LEDs kann unser Auge keine Unterschiede mehr wahrnehmen und somit stört es im Grunde nicht wenn man den Duty nur bis 252 einstellen kann. Dafür hat man dann aber die Berechnungen der Output Compare Tabelle für die LEDs in der Timer Overflow ISR integriert. Dh. die komplette PWM Erzeugung läuft in ISRs und benötigt keienrlei separaten Berechnungscode ausserhalb, zb. in Main() etc.pp. Es werden 2 ISRs benötigt. Einmal die Timer Overflow ISR die bei jedem Overflow erstmal alle LEDs ausschaltet, dann die OCR/PORT Werte für den nächsten Zyklus berechnet, dabei wird in meinem Projekt die Helligkeit der LEDs aus einer Wavetabelle im FLASH geladen, und dann die Berechnung der OCR/PORT Werte durchführt und diese in einer Tabelle im SRAM abspeichert. Die zweite ISR, Output Compare, dauert dann nur 18 Takte und lädt aus dieser Tabelle die aktuellen OCR und PORT Werte um die LEDs anzusteuern. Es hängt also eher davon ab wieviele LEDs du gleichzeitig betreiben möchtest und wie diese am AVR verschaltet wurden. Wie gesagt, bei 8MHz, 122Hz und 12 LEDs an 4 Pins per Charlieplexing benötigtst die MCU nur 1.46 % an Rechenzeit im Worstcase. Würde man in meinem Source die Wavetabellen Unterstützung entfernen so reduziert sich die MCU Auslastung durch die PWM normals. Schätzungsweise auf 1.1% im Worstcase. Dein Vorhaben ist also sehr wohl realisierbar, allerdings hats du uns verschwiegen wieviele LEDs du hast und wie diese verschaltet werden sollen. Gruß Hagen
Hi, danke für die Antworten! Habe zwar noch nicht alles intensiv lesen können aber werde ich demnächst tun :-) Es sind nur 3 LEDs (RGB), können ja aber später auch mehrere werden, also das Programm lässt sich sicher ausweiten. Dutycycle soll wie gesagt zwischen 0 und 1023 eingestellt werden, also eine 10-bit PWM. Ihr habt jedoch recht, die Berechnungen, welche LED in welchem Timer-Zyklus geschaltet werden muss, kann ja vor dem nächsten durchlauf irgendwann zwischendrin gemacht werden. lg PoWl
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.