Forum: Mikrocontroller und Digitale Elektronik Frequenz eines PWM Signals ändern??


von dili (Gast)


Lesenswert?

Hallo zusammen

Ich habe mich heute zum ersten mal mit PWM-Signal-Erzeugung
auseinandergesetzt.
Das Tastverhältnis kann man bekanntlicherweise mit OCR1A resp. OCR1B
einstellen.

Nun kann man mit dem Prescaler die Frequenz festlegen. Ich möchte aber
die Frequnez während dem Betrieb fein verändern. z.B. von 1Hz bis
1000Hz mit einer Schrittweite von 1 Hz.

Ist das mit einem ATMEGA8 überhaupt möglich?

Bitee um euere Hilfe!

Danke!

von Kriki (Gast)


Lesenswert?

Dürfte auch mit einem Atmega8 zu schaffen sein =).

Also mal grundlegend:

PWM wird mit einem Timer gemacht.
Wenn er von 0 startet, gibt es einen Endwert (endweder Register oder
Overflow). Das ergibt die (Grund)-Frequenz, wenn du nun in der Hälfte
dein Signal umschaltest (per Register).

Du kannst aber auch, nachdem du doch relativ niedrige Frequenzen hast,
den Timer so einstellen, dass er für den Interrupt(->Overflow) eine
Zeit von 1ms benötigt.
In der Interruptroutine erhöhst du ein Register. damit kannst du deine
Zeit auf 1ms genau einstellen (max.255ms). Wenn du mehr brauchst,
kannst du als übertrag des Registers ein anderes register verwenden ->
255*255ms = 65535 ms (etwas mehr als 65sec.). Und damit kannst du dann
im Interrupt vergleichen, ob:
1. Du über der Hälfte der Zeit liegst (zum umschalten)
2. Du über dem Maximalwert liegst (wieder ausschalten)

von dili (Gast)


Lesenswert?

Also kann ich das nur mit einem Timer machen.
Die PWM-Modi kann ich dann vergessen oder?

Den mit den PWM-Modi, kann die Frequenz nur beschränkt verändert werden
oder?

Danke!

von Kriki (Gast)


Lesenswert?

PWM funktioniert immer mit einem Timer ... ist nur ein spezieller
Modus.

Also in deinem Fall ist es so, dass du den Timer so einstellen musst,
dass er die Zeit packt.

-> Mit den Prescalern wird mal grob eingestellt, sodass die niedrigste
Frequenz erreicht werden kann. Das heisst bei 1Hz = 1s Periodendauer =
500ms pro Hälfte.

Da gibts sicher was mit 512ms. Also würde der Timer jetzt von 0 bis 255
laufen. 512ms sind aber zuviel. darum gibts ein CompareRegister.

-> 512ms = 255
   500ms = x

x= 500ms*255/(512ms) = 249

Mit dem Wert 249 im CompareRegister kommst du nun auf deine 500ms.
Der Timer zählt von 0 bis 249, im normalmodus wird hier das OCn
(OutputCompare Pin n) umgeschaltet und zählt wieder bis 249, wo wieder
umgeschaltet wird. Juhu, wir haben ein simples Signal mit der Frequenz
von 1Hz.

Wenn du nun eine höhere Frequenz willst, musst du nur einen niedrigeren
Wert ins Compare Register schreiben.

Nun gibt es ein kleines Problem: Niedrigster Wert -> 2ms -> 500Hz.
Also musst du den Prescaler so einstellen, dass der Timer 2 mal so
schnell wird, sprich nur noch 256ms braucht bis er auf den max. 255
ist.

-> 2 Bereiche: 1-500Hz
               501-1000Hz

von dili (Gast)


Lesenswert?

Merci!

Ich werde es mal probieren, wenn es funzt, dann gut, ansosnten frage
ich halt nochmals!

Tschau!

von Kriki (Gast)


Lesenswert?

Kein Problem =)

von Hagen (Gast)


Lesenswert?

Das können die meisten AVRs von Hause aus, ohne Tricks.

Du öffnest im Datenblatt die Seite mit der Tabelle aller PWM Modis.
Dort suchst du einen PWM Modus bei dem der TOP Wert über ein Register
einstellbar ist, zb. ICP1. Dieses register bestimmt mit dem Prescaler
zusammen die Frequenz. Mit den entsprechenden OCRxA und OCRxB Registern
stellst du den Dutycycle zwischen 0 und TOP = ICP Register ein.

Gruß Hagen

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

dann ändert sich allerdings mit der Frequenz auch die Auflösung. Wenn
das ein Problem ist kann man Hardware-PWM vergessen. Und Software-PWM
wird bei 1kHz und einer Auflösung > 8Bit schon leicht schwierig.

Matthias

von Peter Dannegger (Gast)


Lesenswert?

Hier wird ja alles total durcheinander gewürfelt:


Eine PWM dient dazu, eine Analogspannung einzustellen. Dazu muß die
Trägerfreqenz ausgefiltert werden, d.h. sie spielt überhaupt keine
Rolle.
Ein Ändern der Trägerfrequenz ist deshalb nonsens.


Will man eine Rechteckfrequenz erzeugen, kan man nicht deren Amplitude
einstellen, die ist immer VCC (5V).
Eine PWM ist daher nonsens.


Um nun eine Frequenz zu erzeugen, vergißt man also die PWM ganz schnell
und nimmt den Clear on Compare Modus mit Toggle des Portpins.


Peter

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

@peter
Du solltest nicht immer alles als nonsens darstellen für das dir
keine Einsatzzweck einfällt. Ich hab eine einstellbare PWM-Frequenz
(ja, tatsächlich die Frequenz) z.B. mal benötigt um die ideale
Ansteuerung für ein Proportionalventil zu ermitteln. Es mußte eine
Ideal ermittelt werden zwischen kleinem Stromripple und einem leichten
Vibrieren des Propventilstößels (der darf nicht absolut stillstehen).
Da war es dann wichtig im laufenden Betrieb (mit Eingriff eines
Reglers, also Änderung des Tastverhältnis) auch die Frequenz in einem
Bereich von etwa 50Hz bis 300Hz einstellen zu können um die Reaktion
des Systems beurteilen zu können. Im fertigen System war die Frequenz
natürlich fix, aber zur Entwicklungszeit konnte man die variieren.

Matthias

von Peter Dannegger (Gast)


Lesenswert?

@Matthias,

klar gibt es für alles eine Ausnahme von der Regel.


Hier gibt es ja ständig Posts, weil die PWM nicht funktioniert und dann
im 20. Post rücken sie endlich mit der Sprache heraus, daß sie gar keine
Analogspannung haben wollen, sondern ein Rechtecksignal.


Klare Begriffe helfen einfach, viel schneller ein Problem zu lösen bzw.
es überhaupt erstmal richtig zu erkennen.


Peter

von dili (Gast)


Lesenswert?

Ja genau!
Ich brauche ein Rechtecksignal, bei dem ich sowol die Frquenz ändern
kann, als auch das Tastverhältnis.
Das ist das Problem, wäre es nur das Tastverhältnis, ist das für mich
kein Problem.
Ich muss beide Parameter verändern können.

Danke für eure Hilfe!

von Peter Dannegger (Gast)


Lesenswert?

"Ich brauche ein Rechtecksignal, bei dem ich sowol die Frquenz ändern
kann, als auch das Tastverhältnis."


Rein technisch geht das schon (Tabelle 39 Mode 14 oder 15).

Du mußt bloß bei Frequenzänderung das Tastverhältnis neu berechnen und
setzen. Sollte man also in C machen.

Zwischendurch mußt Du auch den Prescaler umschalten, um den Bereich
1Hz..1kHz überstreichen zu können.

Zu allererst mußt Du natürlich festlegen, wie genau und in welchen
Schritten das Tastverhältnis einstellbar sein soll.


Peter

von dili (Gast)


Lesenswert?

Nun habe ich es endlich geschafft, hurra es funktioniert.
Ich habe da nur noch ein Problem:

PB1 ist mein Ausgangssignal (Rechteckssignal mit veränderbarer Frequnz
resp. Tastverhältnis)

DDRB = (1<<PB1);
PORTB = ~(1<<PB1);


Ich habe mich für den Modus 14 entschieden (Fast PWM)

TCCR1A = (1<<COM1A1) | (1<<COM1A0) | (1<<WGM11);
TCCR1B = (1<<WGM12) | (1<<WGM13) | (1<<CS12) | (1<<CS10);

Die Berechnung der Endwerte (TOP) sieht folgendermassen aus:

ICR1 = (F_CPU/N/f)-1;    //  Endwert für Frequenz
OCR1A =ICR1*(100-T)/100;  //  Endwert für Tastverhältnis

, da nach Datenblatt sich die Ausgangsfrequenz so berechnet:

fout=F_CPU/(N*(1+TOP)), berechnet sich der Endwert wie oben
dargestellt.

Aber nun das Problem:

Am Anfang und manchmal wenn ich die Frequenz verändere, dann geht es
sehr lange, bis die Frequenz am Ausgang (PB1) eingestellt ist, wegen
der Berechnungzeit des MCs.

Habt ihr da eine Idee?
Wie könnte ich die Berechnungszeit verringern?

Danke für eure Hilfe!

von Kriki (Gast)


Lesenswert?

Könntest z.B. eine Tabelle machen.

D.h. alle Werte ausrechenen und fix fertig in den Atmega reinreiben.

Schaut dann ca. so aus:

Werte berechnen abwechselnd ICR1 und OCR1A ins RAM schreiben,
r30=Startadresse, r21 = ICR1, r22 = OCR1A , r22 = Frequenz

Dann kannst du direkt mit r21 = r30+r22*2 bzw r22 = r30+r22*2 + 1 die
Register auslesen.

Wie genau du das in C machen kannst weiss ich nicht, aber in Assembler
ugf. so:

GetFrequenz:
                LDZ startadresse
                move r22,a
                asl 1,a,a
                addz r22
                LD Z+,r21
                ST r21,ICR1
                LD Z+,r22
                ST r22,OCR1A
                ret
Einfach, schnell und effektiv.

von dili (Gast)


Lesenswert?

Der Bereich wandert zwischen 1Hz bis 300Hz mit einer Schrittweite von
1Hz.
Das heisst also. dass es insgesamt 300 Frequnezwerte hat und 300
Tasverhältniswerte.

Das sind für einen ATMEGA 8 viel Werte oder nicht?

Habe mich bis jetzt noch nie mit dem Speicherinhalt resp. Kapazität
auseinandergesetzt, denn habe frisch begonnen.

Danke!

von Peter Dannegger (Gast)


Lesenswert?

"Am Anfang und manchmal wenn ich die Frequenz verändere, dann geht es
sehr lange, bis die Frequenz am Ausgang (PB1) eingestellt ist, wegen
der Berechnungzeit des MCs."


Ist bestimmt nicht die Rechenzeit, sondern Du bist warscheinlich am
Comparewert vorbeigeschlittert und dann muß der Zähler komplett rum
laufen (65536 * Prescaler).


Es müßte helfen, nach jedem Ändern des TOP-Wertes den Zähler auf 0 zu
setzen.


Peter

von Kriki (Gast)


Lesenswert?

kommt drauf an, wie viel du noch drinnen hast.

geht leicht zum ausrechnen. Pro Register ein Wert (=1Byte). 2 Register
speicherst du ab. 300 Frequenzen. Ergibt nach Adam Riese 600 Byte.

Wenn sonst noch genügend Platz drinnen ist, kannst ja ruhig
Speicherplatz für Geschwindigkeit opfern.

Andererseits dürfte er eigentlich gar nicht so lange brauchen zum
berechen.
Schon mal probiert, wenn du den Timer abschaltetst, dann die Werte ins
Register schreibst, und dann wieder einschaltest ???

von Hagen (Gast)


Lesenswert?

Korrekt, dies steht auch im Datenblatt beschrieben. Am "sichersten"
dürfte es sein erstmal die PWM zu deaktiveren, ICP als neuen Top setzen
und TCNT auf Null zu setzen. Dies produziert aber Jitter.

Besser noch ist es eine Compare Match ISR zu schreiben und nur in
dieser Routine setzt du TOP neu. Du benutzt ja Fast PWM und das heist
das TCNT auf 0 gesetzt wird beim Compare Match. Du musst nur
sicherstellen das der minimalste Wert vom ICP=TOP größer ist als du an
Takte benötigst für die Compare Match ISR + dem Neusetzen vom ICP.
Ansonsten rennt TCNT=OCR1A wider über TOP hinaus. Mit dieser Methode
hast du dann keinerlei Jitter mehr.

Gruß Hagen

von Hagen (Gast)


Lesenswert?

Da du Floatngpoints benutzt solltest du die Berechnung des neuen ICP
Wertes ausserhalb, in deiner Main Routine, durchführen. Dessen Resultat
spricherst du in einer globalen Variable zwischen. Diese wird in der
Compare Matcvh ISR dann in ICP kopiert.

Gruß Hagen

von dili (Gast)


Lesenswert?

"Du musst nur
sicherstellen das der minimalste Wert vom ICP=TOP größer ist als du an
Takte benötigst für die Compare Match ISR + dem Neusetzen vom ICP.
Ansonsten rennt TCNT=OCR1A wider über TOP hinaus. Mit dieser Methode
hast du dann keinerlei Jitter mehr.

Gruß Hagen"

&

"Da du Floatngpoints benutzt solltest du die Berechnung des neuen ICP
Wertes ausserhalb, in deiner Main Routine, durchführen. Dessen
Resultat
spricherst du in einer globalen Variable zwischen. Diese wird in der
Compare Matcvh ISR dann in ICP kopiert.

Gruß Hagen"

Versteh ich nicht, sorry. Bin Neuling in diesem Gebiet, kann mir jemand
das einfacher erklären evtl. ein Bsp. aufzeigen wie man so was machen
kann.

Gruss dili

von Hagen (Gast)


Lesenswert?

ICR1 = (F_CPU/N/f)-1;    //  Endwert für Frequenz
OCR1A =ICR1*(100-T)/100;

schriebst du oben. Die Divisionen die du da machst benötigen einiges an
Rechenzeit. Führst du diese direkt in der ISR aus so kann es bei hohen
Frequenzen dazu führen das das Timing immer kritischer wird. Deshalb
lagerst du diese Berechnungen aus, in deine Main Loop.

volatile uint8_t nOCR1A;
volatile uint8_t nICP1;

SIGNAL(SIG_OUTPUT_COMPARE1A) {
  ICP1 = nICP1;
  OCR1A = nOCR1A;
}

Das ist die ISR bei jedem Output Compare Match, und diese soll nach
Möglichkeit sehr schnell in wenigen Taktzyklen die wichtigen Register
updaten.

void setPWM(uint16_t Frequency; uint8_t Duty) {

  int i = (F_CPU/N/Frequency)-1;
  int o = i*(256-Duty)/256;
  if ((i != nICP1) | (o != nOCR1A)) {
    cli();
    nICP1 = i;
    nOCR1A = o;
    sei();
  }
}

void main(void) {

  TCCR1A = (1 << COM1A1) | (1 << COM1A0) | (1 << WGM11);
  TCCR1B = (1 << WGM12) | (1 << WGM13) | (1 << CS12) | (1 << CS10);
  TIMSK = (1 << OCIE1A);
  sei();

  while (1) {
    setPWM(123456, 128);
  }

}

Es passiert nun folgendes. Der TCNT1 zählt hoch bis auf OCR1A und setzt
je nach TCCR1A deinen Output pin. Gelichzeitig wird ein Flag gesetzt das
die Ausführung der CPU veranlasst in die obige ISR zu springen. Dies
kostet Zeit, minimal 4 Takte, aber in einem C Source einiges mehr.
Falls nun deine PWM mit hoher Frequenz laufen sollte dann könnte diese
Zeit zu lange dauern und deshalb sollte man sie wie in jeder ISR so
kurz wie möglich halten. Deshalb berechnen wir die neuen Werte für
OCR1A und ICP1 schon im Vorhinein und speichern sie in globale
Variablen.

Obiger Source ist nicht getestet sondern hier schnell reingehämmert.
Also keine Garantie auf Funktionstüchtigkeit. Ich weis nicht auf
welchem AVR du das laufen lassen möchtest, berücksichtige das Timer1
auch ein 16Bit Timer sein könnte, dann musst du oben die
variablendeklaration entsprechend anpassen.

Gruß Hagen

von Hagen (Gast)


Lesenswert?

So nun zu den Vorteilen. Die neuen Werte für die Frequenz und der Duty
werden durch die ISR immer nur zu einem exaktem Zeitpunkt verändert. Je
nach Wert in TCCR1A also zb. immer nur bei steigender Flanke deines
Ausgangssignales. Es kann somit zu keinem Jitter in deiner PWM mehr
kommen.

Statt dem Output Compare Match Interrupt kannst du auch den Timer
Overflow Interrupt benutzen. Die Entscheidung was du benutzt hängt vom
PWM Mode ab und welche Register im AVR gebuffert sind. Beim OCR1A
Register ist es so das es gebuffert ist. Schreibt man da ws rein so
ändert sich dieser Wert erst beim Eintreffen eines Compare Matches.

Beim ICP1 Register ist dies glaube ich nicht der Fall. Deswegen würde
ich den Compare Match Interrupt benutzen. Das steht aber alles im
Datenblatt drinnen und kann von AVR zu AVR unterschiedlich sein.

Gruß Hagen

von dili (Gast)


Lesenswert?

Hallo Hagen

Danke für deine Hilfe, da wäre noch was. Du sprichst von ISR , was ist
das, habe das noch nie gehört. Bin ja Anfänger :)

Gruss dili

von Hagen (Gast)


Lesenswert?

Shit, das du soooo Anfänger Anfänger bist wusste ich ja nicht.

ISR = Interrupt Service Routine. Ist ein Stückchen Programmcode der
quasi asynchron zum normalen Programmcode ausgeführt wird. Auf grund
eines Ereignisses -> IRQ führt die CPU ein Programcode aus, die ISR.

Solche ISRs können also sehr schnell auf ein bestimmtes Ereignis
reagieren und unterbrechen zeitweise den normalen Programfluß. Ohne
solche Interrupts und deren Service Routinen würde man im normalen
Program immer auf solche Ereignisse pollen müssen, sprich möglichst
periodisch den Status des Events abfragen.

Als Ereignisse können ganz verschiedene Sachen herhalten. Grundsätzlich
unterscheidet man 3 solcher Ereignisse
1.) Hardware basierte, sind die meisten im AVR, zb. UART, Pin Change,
Timer, Output Compare, Watchdog, Reset etc. pp.
2.) Software basierte, zb. BIOS oder DOS Interrupts auf PCs
3.) Exceptions, werden hauptsächlich von der CPU ausgelösst als
Ergebnis einer Ausnahmebedinung beim Ausführen eines Machinencodes, sie
sind aber nicht nur darauf beschränkt

Tja. Nun solltest du meine Kurzfassung eines Kurz-überblicks durch
Recherchen im WEB ausbauen.

Gruß Hagen

von dili (Gast)


Lesenswert?

Merci vielmals.

In deinem Bsp. Code habe ich gesehen, dass du

SIGNAL(SIG_OUTPUT_COMPARE1A) {
  ICP1 = nICP1;
  OCR1A = nOCR1A;
}
schreibst, ich habe vorher anstatt ICP1, ICR1 geschrieben, was ist da
der Unterschied?

von dili (Gast)


Lesenswert?

Nun habe ich deine Idee miteinbezogen.

Es ist keine Verbesserung vorzufinden, wenn f oder Abtastver. = 0 ist,
dann geht es lange bis der Ausgang gesetzt ist.
Beim Verändern der Frequenz oder des tastver. kommt es wieder manchmal
vor, dass es lange dauert, bis der Ausgang gesetzt wird, aber nur
manchmal.

keine Ahnung! :(

von Hagen (Gast)


Lesenswert?

Ok erstmal zur Frequenz.

Du hast doch bestimmt schon mal "Emergency Room" im Fernsehen
geschaut ? Da gibt es öfters Menschen die an einem Gerät angeschlossen
werden das auf einem Bildschirm eine regelmäßige Kurve und einen
Piepton von sich geben, ich meine Herzryhtmusmonitore.
Wenn nun auf dem Monitor eine Gerade Line ist und ein Dauer Piepton zu
hören war, mit welcher Frequenz schlug dann das Herz dieses Toten ?

Nun zum Dutycycle:

Du gehst in den Flur und schaltest deine Lampe abwechseln ein und aus.
Die Lampe wird jenach Dauer wie lange du sie einschaltest leuchten und
wie lange der Schalter aus ist eben dunkel sein.
Wenn du dir nun Vorstellst das das Dutycyle Verhältnis 0 ist, also zb.
die Leuchtphase der Lampe 100% ist und die Dunkelphase 0%, wie lange
leuchtet dann die Lampe ?

Ergo: mit ein bischen Nachdenken wirst du sehen das eine Frequenz von 0
Hz, wie auch ein Dutycycle von 0% immer einen Dauerpuls auf dem Ausgang
erbegen muß. So gesehen macht deine Software und Hardware exakt das was
DU ihr gesagt hast.

Gruß Hagen

von Hagen (Gast)


Lesenswert?

Es ist schwer dir jetzt weiterzuhelfen. Ich möchte mal einen Vergleich
machen, bitte nicht persönlich nehmen.

Wenn du deinem Sohn erklären möchtest was eine Transzendente Zahl ist,
meinst du das er das kapiert wenn er noch in der Vorschule ist ?

Exakt so wird es aber uns ergehen um dir was erklären zu können. Ich
weis nun nicht was ich dir empfehlen soll, ich kenne keine einfache
Simulationssoftware die dir die allgemeinen Grundlagen beibiegen kann.
Das beste wäre wohl mit einem guten Einsteigerbuch das die Grundbegrife
erklärt anzufangen, aber auch davon habe ich keine Ahnung und kann dir
keines empfehlen.

Gruß Hagen

von dili (Gast)


Lesenswert?

Danke!

Ich habe es endlich geschaft, es funktioniert!!!!!!!!!!!!  :) :)

Ich habe nach jeder Änderung den aktuellen Zähler TCNT1 gelöscht.

if (TCNT1>ICR1)
    TCNT1=0;

Wenn der akt. Zaler höher als der Endwert ist, dann wird er geresetet.

Toll ah. Das Problem war ganz anders als gedacht.

von Captayne (Gast)


Lesenswert?

Diese sporadischen Wartezeiten, bis wieder etwas passiert liegt am 
Schreiben des nicht doppelt gepufferten ICF1.
Kommt der TCNT1 genau während das ICF1 geschrieben wird dort vorbei, 
kann es sein dass der Compare-Match gar nicht ausgelöst sondern verpasst 
wird (Nur manchmal halt) und TCNT1 zählt dann lustig bis 0xffff durch 
und wrapped dann erst ...
Daher kommen deine nicht ganz determinisitischen Effekte.

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.