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!
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)
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!
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
Merci! Ich werde es mal probieren, wenn es funzt, dann gut, ansosnten frage ich halt nochmals! Tschau!
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
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
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
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
@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
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!
"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
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!
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.
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!
"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
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 ???
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
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
"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
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
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
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
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
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?
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! :(
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
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
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.