Forum: Compiler & IDEs Schätzfrage, reichen 128 Takte hierfür aus?


von Paul H. (powl)


Lesenswert?

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

von Roland P. (pram)


Lesenswert?

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!)

von Paul H. (powl)


Lesenswert?

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 :-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Roland P. (pram)


Lesenswert?

> 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

von Falk B. (falk)


Lesenswert?

@ 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

von Hagen R. (hagen)


Lesenswert?

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

von Paul H. (powl)


Lesenswert?

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
Noch kein Account? Hier anmelden.