www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Software-PWM stockt bei unpassender Updatefrequenz


Autor: Paul H. (powl)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  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

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ 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

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.
ISR(TIMER1_COMPB_vect)
{
  pwm_cycle++;

  PORTB &= ~ptr_PORTB_isr[pwm_cycle];
  PORTC &= ~ptr_PORTC_isr[pwm_cycle];
  PORTD &= ~ptr_PORTD_isr[pwm_cycle];

  if(ptr_timing_isr[pwm_cycle] != 0)
  {
    OCR1B = ptr_timing_isr[pwm_cycle];
  }
  else if(pwm_change) // <-- Syncronisation!
  {
    pwm_change = 0;

    [Zeiger tauschen]
  }
}

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ü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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.