Ich habe eine Atmel AT90PWM3 vor mir. Ich habe vor, ein Rechtecksignal dass von meinem Lüfter erzeugt wird auszulesen um so seine Umdrehungszahl zu ermitteln. Ich will also die Periodendauer von dem Rechtecksignal messen. Dabei hatte ich gedacht, während einer bestimmten Zeitspanne, zB 1s oder 1/2s alle steigenden Flanke des Signals mit einem Zähler zu zählen. Dafür würde mir der 8 Bit Counter ausreichen. Da die max Anzahl von Flanken pro sec bei 40 liegt. Dass heißt ich hätte noch den 16 Bit Timer, mit dem ich das Zeitfenster generieren kann. Mein Problem ist nun, in welcher Reihenfolge ich vorgehen muss und wie es letztendlich zu realisieren ist. Wie kann ich den Counter anhalten wenn die Zeit verstrichen ist? Macht mein Controller überhaupt noch was wenn ich den Counter starte, läuft dass dann im Hintergrund?
>...Da die max Anzahl von Flanken pro sec bei 40 liegt...
Oben hast Du doch noch gesagt, Du wolltest eine Periodendauer messen.
Das was Du hier beschreibst ist aber eine Periodenzählung. Bei max.
40Hz und einem µC, der eine Input Capture-Einheit besitzt, solltest Du
wirklich eine Periodendauermessung machen. Das geht mit Input Capture
ganz hervorragend und ist bei derart niedrigen Frequenzen eigentlich
das einzig sinnvolle. Überleg doch mal, wie gering die Auflösung wäre,
wenn Du tatsächlich nur max. 40 Flanken pro Sekunde erhältst und nur
über eine halbe oder ganze Sekunde misst...
Da hast du Recht. Habe mit gerade das mit dem Input Capturing durchgelesen. Heißt das, das dieser Mode, wenn sich mein Signal ändert, den 16 Bit Timer startet und dieser dann läft bis sich das Signal erneut ändert? Dann muss ich praktisch nur den entsprechenden Timerwert aus dem TCNTn Register auslesen und ensprechend der CPU Frequenz hochrechnen und schon habe ich die Periodendauer?
Nein, der Timer läuft im Prinzip durch. Es wird lediglich bei Auftreten einer Flanke der aktuelle Zählerstand in das Capture-Register geschrieben (und zwar verzögerungsfrei). In der entsprechenden Interrupt-Routine sicherst Du dann das Capture-Register in einer Variablen und beim nächsten Capture-Ereignis musst Du im Prinzip nur noch die Differenz zwischen den beiden Werten bilden. Die Timer-Überläufe musst Du natürlich auch zählen und dann zur Differenz jeweils den kompletten Wert addieren. Sinnvollerweise betreibst Du den Timer im CTC-Modus mit einem Compare-Register als Reset-Wert und stellst das ganze so ein, dass Du z.B. alle 10 ms einen Reset bekommst. Dann über den entsprechenden Compare-Interrupt die Millisekunden zählen und die Differenz der beiden Capture-Werte dazuaddieren (wenn die Differenz negativ ist, muss natürlich ein 10 ms-Wert abgezogen werden). Das ist vom Rechenaufwand minimal und sehr präzise. In Deinem Fall reicht es wahrscheinlich sogar dicke aus, in 100 ms-Blöcken zu zählen, zumal der 16-Bit Timer an sich schon eine sehr gute Auflösung zur Verfügung stellt (wenn man ihn sinnvoll konfiguriert). Hängt alles ein bisschen davon ab, wie präzise das ganze sein soll. Gruß Johnny
Also erst mal danke für deine Hilfe. Hab das Ganze jetzt mal programmiert, sieht so aus: #include <stdint.h> #include <avr/io.h> #include <avr/interrupt.h> volatile uint32_t Zaehler[2]; int main(void) { Zaehler[0]=0; for(;;) { /*CTC Timer (Mode 12) mit Prescaler auf CPU/256, fallende Flanke als Trigger, eine Timerzyklus = 100ms*/ TCCR1A |= 0x00; TCCR1B |= (1<<WGM13)|(1<<WGM12)|(1<<CS12); ICR1 = 3200; //Obergrenze Timer } } ISR(TIMER1_CAPT_vect) //wenn fallende Flanke auftritt { Zaehler[1]=ICR1; //Zählerwert in Variable kopieren } int Drehzahl(void) { /*Nicht berücksichtigt, dass bei Umdrehungen unter 10 rpm, ein falsches Ergebnis entsteht,da dann 2 Zählerüberläufe entstehen.*/ uint32_t abs_Zaehler=0; uint32_t Drehzahl=0; if(Zaehler[0] < Zaehler[1]) { abs_Zaehler = Zaehler[1]-Zaehler[0]; } else /*Neuer Wert kleiner alter Wert ->Überlauf dh volle Periode dazu*/ { abs_Zaehler = ((Zaehler[1]-Zaehler[0])+3200); } Zaehler[0]=Zaehler[1]; Drehzahl = (1/(abs_Zaehler * 0.001875)); //Drehzahl in rpm return Drehzahl; } Ablauf: -CTC Timer mit 256 Prescale Obergrenze 3200 starten, ein Zyklus = 100mS -Wenn Capture auftritt in Variable umspeichern -Prüfen ob Überlauf stattfand -Umrechnen auf Umdrehungen pro Minute Hab ich das so richtig verstanden? Bzw. Warum muss ich den CTC Timer wählen der ICR1 als TOP Value hat? Der Wert im ICR1 Register wird doch immer wieder überschrieben! Vielen Dank noch mal für deine super Hilfe Gruß Matze
Nimm den CTC-Modus 4 (mit OCR1A als TOP). Das ICR1 brauchste ja für die Capture. Und im CTC-Modus darfst Du natürlich nicht auf nen Überlauf warten (der tritt ja nie auf, wenn OCR1A kleiner als MAX ist), sondern musst den Compare-Interrupt benutzen, um die 100ms-Blöcke zu zählen.
>Und im CTC-Modus darfst Du natürlich nicht auf nen Überlauf
warten (der tritt ja nie auf, wenn OCR1A kleiner als MAX ist)
Das versteh ich jetzt nicht so ganz. Wenn ich CTC auf den Modus 4
umstelle und dann natürlich auch anstatt ICR1, OCR1A schreibe. Dann
zählt mein Counter bis zum Wert in OCR1A, also bis 3200, was bei 8MHz
Clock und 256 Prescaling 100ms entspricht.
Die Blöcke zu zählen hab ich weggelassen, wenn du das meinst, da sich
das Problem von 2 Überläufen, erst unterhalb von 10 rpm bemerkbar
macht. Anstatt hab ich den Vergleich eingeführt. Der fällt natürlich
ins Wasser wenn ein Signal mit niedrigerer Frequenz wie ein Zählzyklus
auftaucht.
Obwohl, wenn ichs mir recht überleg, ist ja auch blöd wenn ich meinen
Lüfter blockiere und auf einmal gibt die Software wieder grünes Licht,
obwohl der Lüfter fast steht.
Oh ich glaube jetzt weiß ich was du meinst: ISR(TIMER1_CAPT_vect) { Zaehler[1]=ICR1; //Zählerwert in Variable kopieren } Das heißt der Interrupt tritt erst beim Zählerüberlauf auf?
Nein. Du musst ja die 100 ms-Blöcke zählen. Dazu musst Du den Compare-Interrupt benutzen. Dieser ersetzt im CTC-Modus den Overflow-Vektor. Im Prinzip kannst Du im Programm eine globale Zählvariable setzen, die zwischen zwei Flanken die Timer-Resets (die ja nach jeweils 100 ms auftreten) zählt. Zusätzlich hast Du dann zwei Capture-Werte, deren Differenz Du bildest. Daraus errechnest Du die Gesamtzeit, die zwischen zwei Flanken verstrichen ist. Bsp.: -Globale Zählvariable für 100 ms-Blöcke auf Null setzen -Flanke 1 kommt -> Zählerstand von TCNT1 wird in ICR1 gespeichert (z.B. 2500) -In der Capture-Routine wird ICR1 in einer globalen Variable gesichert -Bei erreichen von 3200 (Wert in OCR1) im TCNT1 wird TCNT1 zurückgesetzt und der Compare-Interrupt ausgelöst. -In der Compare-Interrupt-Routine wird der 100 ms-Zähler um 3200 erhöht (3200 entspricht ja 100 ms, wird für die Endberechnung gebraucht). -Wenn in den nächsten 100 ms noch keine weitere Flanke am ICP auftritt, gibts wieder ein Compare-Ereignis, wieder muss der 100 ms-Zähler um 3200 erhöht werden usw.... -Flanke 2 am ICP tritt auf -> TCNT1 wandert in ICR1 -In der Capture-ISR wird der oben gesicherte erste Capture-Wert vom aktuellen abgezogen. Die Differenz zu dem Wert im 100 ms-Register addieren und das ganze mit einer Division durch 32 in Millisekunden umrechnen. Es ist darauf zu achten, dass für die Differenzbildung eine vorzeichenbehaftete Variable verwendet wird, da die Differenz ja auch negativ sein kann. -Fertig, Periodendauer ist da! (Alternativ zur Erhöhung des 100ms-Zählers um 3200 kann man ihn auch jeweils um 1 erhöhen und am Ende für die Berechnung mit 3200 multiplizieren. Dann kann der Zähler i.A. auch eine 8-Bit-Variable sein) Wichtig ist nur, dass CTC-Mode 4 zum Einsatz kommt und nicht CTC-Mode 12, weil der tatsächlich das ICR benötigt. So würde ich es machen, wenn ich es jetzt mal eben hinbiegen müsste. Man kann es sicher noch anpassen und optimieren. Habe jetzt nur dummerweise keine Zeit, mir den ganzen Code noch mal anzuschauen. Ich hoffe aber, dass ich Dir helfen konnte. Gruß Johnny
Ja, auf jeden Fall. Ich werd es ausprobieren. Meld mich dann wenn es klappt. Vielen Dank Matze
Also, dass mit der Drehzahlmessung funktioniert jetzt mal. Allerdings habe ich immer noch ein Problem. Ich kann die Zählerüberläufe , wie von Johnny vorgeschlagen nicht zählen, da der Overflow Interupt eine niedrigere Priorität hat wie der des Input Capturing. Als Folge tritt der Overflow Interupt nie auf, da er vom Input Capturing Interupt praktisch blockiert wird. Gibt es eine Möglichkeit dieses Problem zu beheben? Denn sonst kann ich nur Frequenzen > 5 Hz erkennen. Grüße Matze
Es gibt beim AVR keine Interrupt-Prioritäten! Es gibt lediglich eine Abarbeitungsreihenfolge gleichzeitig auftretender Interrupts. Der Overflow-Interrupt tritt nicht auf, da der Timer im CTC-Modus nie überläuft!
> da er vom Input Capturing Interupt praktisch > blockiert wird. Wieso blockiert??? Ich hoffe, Du bleibst nicht die ganze Zeit während der Messung in der Input-Capture-ISR?!?!?!? Du musst natürlich nach dem Sichern des Capture-Wertes und Initialisierung der Zählervariablen wieder aus der ISR raus. Sonst blockierst Du alles andere!
ja, aber im CTC Modus wird doch, wenn der Counter von MAX nach 0 zählt, das TOV Flag gesetzt. Daher dachte ich, dass dann auch ein Overflow Interrupt ausgelöst wird. Oder muss ich auf ein Counter Compare Match A warten? Und wenn ja, muss ich dann ein Compare Wert definieren, oder mit welchem Wert wird dann verglichen?
MAX ist der maximale Zählerstand (beim Timer 1 0xFFFF), der im CTC-Modus nie erreicht wird, es sei denn, in OCR1A steht 0xFFFF... Also nochmal zum Ablauf (um sämtliche Klarheiten zu beseitigen): 1. Timer 1 läuft im CTC-Modus 4 und Input Capture ist aktiviert 2. Es tritt eine Flanke am Input Capture Pin auf -> TCNT1 wird automatisch in ICR1 gesichert 3. Die Input Capture ISR wird aufgerufen, ICR1 gesichert, der Zähler für die 100ms-Blöcke auf null gesetzt. Anschließend WIRD DIE ISR WIEDER VERLASSEN!!! 4. [optional, evtl. auch mehrmals] Es tritt ein Compare-Ereignis auf -> die Compare-ISR wird aufgerufen. Darin wird der Zähler für die 100ms-Blöcke inkrementiert. Anschließend wird auch diese ISR wieder verlassen! 5. Es tritt eine zweite Flanke am Input Capture Pin auf -> es wird erneut TCNT1 in ICR1 übernommen. 6. Die Input Capture ISR wird wieder aufgerufen. Die Berechnungen werden durchgeführt und das Ergebnis gesichert. Dann: RAUS AUS DER ISR! Natürlich musst Du in der ISR abfragen (z.B. eine Flag-Variable), ob die jeweils aufgetretene Flanke die erste oder die zweite ist...
ICh glaub ich bin grad selber auf die Antwort gestoßen. Der Output Compare Match vergleicht meinen Wert im OCR1A Register(das ja acuh meine Timer Obergrenze ist) und löst wenn beides gleich ist einen Interrupt aus, wenn ich das entsprechende Bit in der TIMSK vorher aktiviert habe.
Vielen Dank für deine Zusammenfassung. Ich hab die einzelnen Schritte auch so realisiert, jedoch habe ich die Drehzahlberechnung in ein eigenes Programm gepackt, dass von der der Input Capturing ISR aufgerufen wird. Gibt das ein Problem, oder kann ich das so lassen? Vielen, vielen Dank noch mal Matze
Ich hoff ich darf gleich noch ne Frage hinterher schicken. Ist auch ne ganz einfache kurze. :-) Wass passiert denn wenn zB ein Zähler für eine Variable zu groß wird. Also zB wenn: uint8_t i=255; i++; Läuft die Variable dann einfach über und ist danach 0, oder wächst die dann "irgendwo" unbestimmtes hin.
Zu der einfachen, kurzen Frage: ja, die Variable läuft über, genau wie die Hardware-Zähler (Timer). Weiter wachsen (irgendwohin) kann sie ja nicht, weil nix mehr reinpasst... Zu der anderen Angelegenheit: Der Code einer ISR sollte 1. Grundsätzlich so kurz wie möglich sein 2. Keine Warteschleifen o.ä. enthalten 3. Keine Funktionsaufrufe enthalten Im Allgemeinen ist es am sinnvollsten, in der ISR nur das absolut nötigste zu machen (Beispiel Input Capture: ICR sichern, Zähler auf Null setzen oder Ergebnis berechnen) und alles andere, was mit dem Interrupt zu tun hat im Hauptprogramm abarbeiten. Meist setzt man dazu in der ISR eine Variable (bzw. ein Bit in einer Variablen, sog. Job-Flag) und fragt dieses im Hauptprogramm ab. Die Berechnung des Ergebnisses in Deinem Beispiel ist eigentlich auch ein Fall für ein Jobflag und eine Bearbeitung im HP, aber da sie in diesem Fall noch überschaubar ist, kann man die auch in der ISR lassen. Mit Deiner Methode machst Du im Prinzip alles falsch, was man falsch machen kann (Punkt 1. bis 3. von oben). Die ISR blockiert das ganze System, so dass der Compare-Interrupt (der ja die 100ms-Abschnitte zählen soll) gar nicht ausgeführt wird. Du musst auf jeden Fall aus der ISR wieder raus, wenn das Capture gesichert ist, sonst läuft gar nix.
Vielen Dank noch mal für deine ausdauernde Hilfe. Jetzt funktioniert es wunderbar. Habe die Drehzahlberechnung jetzt mit in die ISR gepackt und dafür alles was auch ins main Programm kann ausquartiert. Dann noch den entsrechenden Compare Match Interrupt freigeschaltet. Habe soeben auch das Ganze mit einem Papst Lüfter getestet, mein Controller erkennt jetzt auch ernn der Lüfter vollständig blockiert ist. Genau so habe ich mir das vorgestellt. Viele Grüße und Besten Dank Matze
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.