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:
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
intnextComp[8];
2
intportState[8];
3
intcursor;
4
intmaxCursor;
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