Hi,
da mich das Stocken an meiner Soft-PWM ein erheblichen Schönheitsfehler
darstellt möchte ich der Sache mal auf den Grund gehen.
Beitrag "Re: 8-Bit Software PWM für mehr als 8 Kanäle"
Wenn ich in der while-schleife in der main bei _delay_ms(); eine
Wartezeit von 10 Millisekunden eintrage, dann stockt die ganze PWM
ziemlich. Ob das ganze regelmäßig ist oder nicht kann ich leider nicht
ganz erkennen. Jedenfalls scheint sie da mehrere Takte lang auszusetzen.
Bei 9ms oder 8ms geht es komischerweise wieder. Unterhalb der 8ms wieder
nicht.
Ich verstehe nun leider nicht warum. Die pwm_update() Funktion setzt das
pwm_change flag zu Beginn erstmal auf 0 um zu verhindern, dass die
COMPB-ISR die neuen Daten übernimmt während sie gerade zusammengestellt
werden. Nachdem die Daten fertig sind wird das pwm_change Flag wieder
auf 1 gesetzt um der COMPB-ISR am Ende des Zyklus zu signalisieren dass
die neuen Daten bereit stehen. Dann können die Zeiger getauscht werden.
Die PWM läuft mit 122,5Hz.
Eigentlich sollte es völlig egal sein wie oft/schnell nun die PWMdaten
geupdated werden. Sie dürfen nur nicht geupdated werden während die
Zeiger gerade getauscht werden, andersrum dürfen die Zeiger nicht
getauscht werden während die PWMdaten geupdated werden. Letzteres wird
dadurch verhindert, dass pwm_change während der Ausführzeit von
pwm_update() auf 0 ist, vorletzeres dadurch, dass pwm_update() während
die ISR läuft nicht ausgeführt werden kann. Daher versteh ich nicht
warum das flackert, und schon garnicht, warum es bei <5..6..7 und bei
10ms flackert, jedoch bei 8ms und 9ms nicht. Dass das ganze lückend
läuft sobald die Update-Frequenz größer ist als die PWM-Frequenz ist
klar, aber das sollte nicht bemerkbar sein. Das stocken ist richtig
deutlich zu erkennen.
Seltsamerweise bessert sich das Stocken sobald ich die COMPB-ISR etwas
verkürze indem ich z.b. die Negationen bei
PORTB &= ~ptr_PORTB_isr[pwm_cycle];
PORTC &= ~ptr_PORTC_isr[pwm_cycle];
PORTD &= ~ptr_PORTD_isr[pwm_cycle];
in die pwm_update Funktion auslagere.
Erkennt da jemand einen Zusammenhang?
lg PoWl
@ Paul Hamacher (powl)
Warum nimmst du nicht den Code aus dem Tutorial und erweiterst ihn auf
mehr Kanäle? Du erfindest das Fahhrad neu, allerdings mit eckigen
Rädern.
Deine PWM Generatorroutine scheint mir ein wenig zu einfach. Ob die
wirklich korrekt läuft? Mit allen möglichen PWM-Kombinationen?
Und warum tauschst du die Zeiger in der ISR? Dort ist es vollkommen fehl
am Platz.
Geh mal davon aus, dass der Autor des Tutorials sich was dabei gedacht
hat . . . ;-)
MFG
Falk
Ich erfinde gern hin und wieder mal das Rad neu um was dabei zu lernen,
noch mehr wie als wenn ich alles übernehme und ein paar mal
drüberschaue. Die Zeiger tausche ich in der ISR.. ja, nun.. warum nicht?
Abgesehen davon dass dadurch die ISR etwas länger wird. Die Zeiger
sollen pünktlich getauscht werden. Und zwar in der Zeit von der letzten
COMPB-ISR bis zur nächsten COMPA-ISR, damit der neue PWM-Zyklus gleich
mit den neuen Daten arbeiten kann.
Daher verstehe ich auch nicht warum das mit der Updatefrequenz
zusammenhängt. Irgendwann stehen eben neue Daten für die ISR bereit und
die Zeiger werden getauscht. Oder habe ich hier einen solchen Fall, dass
die pwm_update() Funktion und die COMPB-ISR sich oftmals überschneiden
während pwm_changed gerade 0 ist und eine Zeit lang nebenherlaufen
wodurch dieses Stocken entsteht?
Und ja, die PWM funktioniert prima, mit allen möglichen Kombinationen,
wieso scheint sie zu einfach? Ich habe alle fälle bedacht und das schon
mehrfach getestet. Bei der Demo sind fast alle Werte von 0 bis 255
mehrfach dabei und die Leucht-Welle läuft zuverlässig durch die 22 LEDs
hindurch.
Ich werde jetzt mal meinen Code mit dem aus dem Tutorial vergleichen.
lg PoWl
push
Noch eine Frage zur PWM im Soft-PWM Artikel. Wieso soll es so wichtig
sein, dass die Zeiger außerhalb der ISR getauscht werden?
In beiden Fällen läuft im Hauptprogramm hin und wieder die pwm_update()
Funktion. Um die Zeiger zu tauschen muss die Funktion nun die tmp-Zeiger
fertig befüllt haben UND der letzte PWM-Zyklus muss durchgelaufen sein.
In meiner Version tauscht die ISR dann selbstständig die Zeiger und
blockiert sich selbst für diese Zeit, d.h. wenn das zu lange dauert wird
ein Zyklus ausgesetzt (was in jedem Fall wohl zu Flimmern führen wird).
Im Soft-PWM Artikel wird ISR durch cli() im Hauptprogramm blockiert und
dort werden dann die Zeiger getauscht. Im Endeffekt kommt das aber aufs
gleiche hinaus -> die ISR kann nicht weiterlaufen während die Zeiger
getauscht werden. Letzere Methode hat meiner Meinung noch auch den
Nachteil, dass das Hauptprogramm auf die Synchronisation warten muss, in
meiner Version kann es schon mit irgendwas anderem weiterarbeiten
während die ISR selbstständig auf den Zeigertausch wartet.
Lieg ich da falsch?
lg PoWl
@ Paul Hamacher (powl)
>Noch eine Frage zur PWM im Soft-PWM Artikel. Wieso soll es so wichtig>sein, dass die Zeiger außerhalb der ISR getauscht werden?
Kann man auch in der ISR machen, das wird aber ggf. komplizierter.
>Nachteil, dass das Hauptprogramm auf die Synchronisation warten muss, in
Synchronisiert werden muss auf alle Fälle. Ob man das ggf. auch ohne
Warten machen kann, ist eine andere Frage.
Man könnte z.B. die neuen Zeiger in Variablen schreiben und in einer
weiteren Variable ein Flag setzen. Die ISR erkennt dies und tauscht dann
selber die Zeiger. So muss weder die ISR noch das Hauptprogramm warten.
Allerdings muss dann die update-Funktion sicherstellen, dass sie VOR dem
wiederbeschreiben der Puffer und Zeiger prüft, ob das letzte Update
schon übernommen wurde. Das kann bei hohen Updatefreqeunzen zu
Wartezeiten führen. Damit hat man an der Stelle am Ende wenig gewonnen.
MFG
Falk
Synchronisieren tu ich ja. Die pwm_update()-Funktion setzt, nachdem sie
fertig ist, das pwm_change Flag auf 1. Dadurch wird der ISR wenn der
letzte PWM-Schritt durchlaufen wurde, dass nun die Zeiger getauscht
werden können.
Allerdings bleibt das Hauptprogramm währenddessen nicht in der
pwm_update()-Funktion hängen sondern kann sich mit etwas anderem
beschäftigen. Die ISR weiß dann beim nächsten mal, dass sie die Zeiger
tauschen darf.
Am Anfang der pwm_update()-Funktion wird das pwm_change Flag wieder auf
0 gesetzt. Falls die ISR bis dahin die Zeiger noch nicht getauscht hat,
hat sie halt pech gehabt und muss bis zum nächsten Tausch warten. Das
wird sich nur als störend bemerkbar machen wenn die Updatefrequenz in
der Nähe der PWMfrequenz liegt. Dann kann es sein, dass es eine Weile
lang immer wieder auftritt, dass die pwm_update()-Funktion gerade die
neuen Daten bereitstellt während die ISR noch läuft. Im letzten
PWM-Schritt sind die Daten dann noch nicht fertig und die ISR verpasst
das Update. Einige Zeit später wenn die ISR wieder kurz vorm Ende steht,
wird wieder die pwm_update()-Funktion aufgerufen. Diesmal zwar etwas
früher, aber es kommt trotzdem wieder dazu, dass die neuen Daten noch
nicht fertig sind und die ISR das Update verpasst. Das geht dann eine
ganze Weile so, bis die Verschiebung wieder so gering ist, dass es
passt. Sowas ist natürlich schlecht. Ließe sich eventuell nur mit einem
intelligenteren Buffering lösen.
Übrigens rührt die Einfachheit meines Codes daher, dass meine Soft-PWM
im Prinzip einfacher funktioniert. Die Soft-PWM aus dem µC.net Artikel
verwendet beim Timer einen niedrigen Prescaler aber nutzt die 16-Bit
Compare Register besser aus, um eine exakt bestimmbare PWM-Frequenz
zuzulassen. Meine hat ihre festen 122Hz wobei ich das Prinzip aus dem
Soft-PWM Artikel besser finde. Werde ich mich auch mal mit beschäftigen.
lg PoWl