Schönen guten Morgen, meine Frage geht in Richtung Interrupts, ich möchte das Ereignis eines Interrupts abwarten, und dann wenn der Interrupt eintrifft Befehle abarbeiten. Also solange warten, bis Interrupt gekommen ist. Ohne ISR. Mein erster Ansatz ist, zu warten ob das entsprechende I-Flag gesetzt wurde. Beispielsweise warten bis ext. Int1 eintrifft. ... ... GICR |= (1 << INT1); // External INT1 Interrupt Enable sei(); // global Ints Enable while (!(GIFR & (1 << INTF1))) // Solange warten bis Int1 eintrifft { } andere Befehle dann abarbeiten... ... ... Erklärung der While Schleife: Solange warten bis das INTF1 Flag im GIFR Register gesetzt wird (laut Datenblatt wird das gesetzt wenn ein Int1 eintrifft). Wie ist das mit dem Bit Rücksetzen? Sind meine Überlegungen korrekt?
Die Überlegungen sind prinzipiell korrekt. "Ereignis abfragen ohne Interrupt" nennt sich übrigens in der Fachsterminologie "Polling". Das Löschen eines Interrupt-Flags per Software funktioniert bei den AVRs, indem man eine "1" hineinschreibt, also z.B.
1 | GIFR = 1 << INTF1; |
Schau(t) Dir (Euch) aber dazu den Thread Beitrag "Interrupt ein/aus schalten" an, da steht (u.a. in meinem Posting vom 6.12.2006, 08:15) drin, warum man das mit "=" und nicht mit "|=" macht.
Bernie und Ert wrote: > GICR |= (1 << INT1); // External INT1 Interrupt Enable > sei(); // global Ints Enable Ich denke, du willst keine Interrupts haben, warum gestattest du sie denn dann?
Hmmm, also wenn die Flags unabhängig von den Interrupt Einstellungen gesetzt werden, dann brauch ich das wirklich nicht... Aber an der Funktion wie die Abfrage in der while Schleife ändert das ja nix oder?
Die Abfrage in der while-Schleife ist, wie oben schon gesagt, korrekt. Nur das sei() muss natürlich weg...
Wobei sich natürlich die Frage erhebt: Warum um alles in der Welt willst anstatt bequem die Hardware den Interrupt auslösen zu lassen, selbst pollen ob ein Ereignis eingetreten ist? Das ist so, als ob du zuhause die Klingen abklemmst und dafür lieber alle 10 Sekunden nachsiehst ob jemand zu Besuch kommt.
Ich wollte eine Frequenzmessung realisiern. Mit Hardware Interrupts hab ich das schon realisiert, nur ist das Ergebnis zu ungenau. Jetzt wollte ich das per SW probieren ob das genauer wird. Schließlich dauert ein Sprung in die ISR mal >10 CPU Zyklen. Beim Pollen denke ich, dass wenn der Interrupt eingetroffen ist, der nächste Befehl dann im nächsten Takt gleich abgearbeitet wird. Oder ist dem nicht so?
Nimm doch input capture dafür, wenn die Frequenz gering ist, oder takte den Zähler extern, wenn sie höher ist.
Die zu messende Frequenz ist ca. 0,5 MHz - 1Mhz Das müsste doch ohne Zusatzhardware zu schaffen sein. Wenn ich z.B. über 20 Perioden messe, krieg ich ein ziemlich ungenaues Ergebnis. Wenn ich z.B. die gleiche Messung paar mal wiederhole, krieg ich immer ein anderes Ergebnis, da die Sache ja auf meine Interrupt Flags triggert, müsste ich doch immer das gleiche Ergebnis bekommen. Mein Timer läuft dabei mit max. CPU Takt 8Mhz.
Warum triggerst du nicht einen Zähler damit? Ein zweiter dient dann als Zeitnormal zum Vergleich. Hab' ich mal für den Prototypen eines LC-Meters gemacht, war so ungenau nicht.
Mit was für einer Samplingrate soll denn gemessen werden? Oder ändert sich die Frequenz des Signals so schnell, dass sie nur über 20 Perioden konstant ist? Frequenzen, die in der selben Größenordnung liegen wie der CPU-Takt misst man nicht über die Periodenlänge sondern mit einem Periodenzähler mit fester Torzeit. Bei einer Torzeit von 100 ms gibts bei 0,5 MHz 50000 Impulse, bei 1 MHz 100000. Das sollte doch von der Auflösung her reichen. Und selbst 100 ms Torzeit ist noch wenig. 10 Samples / Sekunde kann man bei rein optischer Anzeige eh nicht mehr gebrauchen. Also eher 200 ms oder mehr nehmen.
Mir gehts darum das auch als Übung zu verstehen. Wenn ich z.B. 10 Perioden Messe eines 0,8 Mhz Signals, dann habe ich pro Signalperiode 10 Taktzyklen die vergehen. Ich zähle sowohl die Perioden des Messsignals (schreibt man das jetzt mit 3s jetzt?) mit Timer0, extern getaktet, sowie die Zeit mit Timer1 welcher mit CPU speed rennt. Bsp: Ext. Signal 0,8 Mhz. Messe 10 Perioden Timer0 : 10 Timer1 : 100 +x x sollte dabei immer konstant sein, ist es aber nicht, es schwankt so zwischen 10 und 30 in etwa. Ich verstehe aber nicht warum.
> Ich verstehe aber nicht warum.
Ich auch nicht, solange ich nicht weiß, wie die Messung implementiert
ist. Ohne den Code gesehen zu haben, kann ich nur raten...
OK paar Worte zur Messung: Das Messsignal liegt an INT1, der MEssvorgang wird über den ext. Int1 Interrupt Flag gestartet. Gestoppt wird über den Timer0 Überlauf. Der Timer 0 wrid vorher mit einem Wert vorgeladen. Die Funktion funktioiniert aber noch nicht richtig. Da bin ich noch am dran arbeiten. unsigned int messung(void) // Zählt 25 Perioden des Messsignals und berechnet die Vergangene Zeit anhand Timer1 stand { TCNT0 = 230; // Timer0 "reset" TCNT1 = 0x0000; // Timer1 reset while (!(GIFR & (1 << INTF1))) // Auf Interrupt warten { } TCCR0 = 0x07; // Starte Timer0, ext Takt TCCR1B = 0x01; // Starte Timer1 while (!(TIFR & (1 << TOV0))) // Warte bis Überlauf, also 25 Perioden { } TCCR0 = 0x00; // Stoppe Timer0 TCCR1B = 0x00; // Stoppe Timer1 unsigned int frequenz; frequenz = 8000000 * 25; frequenz /= TCNT1; return frequenz; }
> unsigned int frequenz; > frequenz = 8000000 * 25; Wie soll das gehen? "unsigned int" hat einen Wertebereich von 0...65535! Dass da Murks rauskommt ist ziemlich sonnenklar. Ist das das Programm, mit dem die o.g. Abweichungen auftreten? Bei dem Timing wirst Du das ohne Interrupts nie hinbekommen. Die Bearbeitungszeiten der Warteschleifen sind zu undefiniert und der Abstand zwischen dem Prozessortakt und dem zu messenden Signal ist zu gering.
BTW: Wenn Du die Interrupt-Flags vor der Freigabe der Interrupts bzw. vor dem Start der Timer nicht löschst, dann kann alles Mögliche passieren, nur vermutlich nicht das, was Du willst...
Ok Danke, das mit dem int ist ein Tippfehler, muss klar unsigned long heißen. Aber zur Ausgabe: Bei einem Signal von 0,4 Mhz vom Signalgenerator liefert er mir, wenn ich mir die Timer1 stände ausgeben tue, immer entweder 1003, 1013, 1023. Korrekter Wert wäre ja 1000, wenn die Abweichung konstant wäre, könnte ich eine Korrektur machen, aber da es immer zwischen den 3 Werten schwankt kann ich mir nicht erklären.
bei 0,8 MHz liefert er mir Zählerstände von entweder 503 oder 513, aber nix dazwischen.
Dein Timing ist viel zu knapp. Wenn du nur 25 Timerzyklen misst, kommt es in der Tat auf jeden Timerclock an. Miss länger! Bei hohen Frequenzen dreht man den Messvorgang um. Anstatt die Zeit zwischen 2 (oder 20) Flanken zu messen, zählt man wieviele Flanken in einer definierten Zeit eingelangt sind. Man lässt also den Eingang für, sagen wir mal, 0.1 Sekunden offen und läst den Timer/Counter zählen wieviele Ereignisse in dieser Zeit eingetroffen sind.
Hm was heitß zu knapp? Ich würde davon ausgehen dass der Timerstand immer gleich ist. Weil ja beim INT1 der Interrupt und der Timer Overflow Interrupt ja IMMER zeitlich gleich weit außeinander liegen... Jetzt schwankt das aber um 20 Timerzyklen. Was mich auch stutzig macht ist, dass immer die gleichen Timerwerte auftauchen, und nix dazwischen.
> Weil ja beim INT1 der Interrupt und der Timer Overflow Interrupt ja IMMER > zeitlich gleich weit außeinander liegen... Im Prinzip schon. Da Du aber die Flags in while-Schleifen abfragst, ist das ganze nicht definiert. Je nachdem, an welcher Stelle der while-Schleife das Programm grad ist, wenn das Flag gesetzt wird, kann es schon mal ein paar Zyklen mehr oder weniger dauern, bis das Programm "merkt", dass es die Schleife verlassen soll. Schau Dir mal das Assembler-Listing an, das der Compiler auf Anforderung gerne ohne Aufpreis anfertigt. Da kannst Du (Assembler-Kenntnisse vorausgesetzt) schön nachvollziehen, was der Controller wirklich macht.
Bernie und Ert wrote: > > Jetzt schwankt das aber um 20 Timerzyklen. Das ist vollkommen normal. In C sind schnell mal 20 Zyklen um. Wenn Du es zyklengenau haben willst, mußt Du die Capture-Funktion nehmen, anders geht es nicht. Oder man mißt über soviele Perioden, daß ein Fehler von 20 Zyklen vernachlässigbar ist. Peter
Danke für die Hilfe! Ich hab das noch nie gemacht mir das Assembler Listing ansehen. Kann mir das AVR Studio das asm File ausgeben? Wenn ich die Frequenzmessung in Assembler realisiere, wäre die Messung dann genauer?
@Peter, wie meinst Du wie es mit der Capture Funktion gehen soll? Wie kann ich da über 25 Zyklen messen? Beim Input Capture müsste ich bei jedem Capture den Zählerstand in eine Variable sichern, das kostet auch wieder Rechenzeit.
> Wenn ich die Frequenzmessung in Assembler realisiere, wäre die Messung > dann genauer? Warum streubst du dich so dagegen, dein C Programm auf eine andere Grundlage zu stellen. Dein Messprinzip ist Scheisse! Das ganze kann man auch in C so schreiben, dass der von dir beobachtete Fehler nicht auftritt. Verbuch deinen Versuch unter 'so gehts nicht' und * setzt entweder die Anzahl der Messzyklen höher (dadurch wird dann der prozentuelle Fehler geringer). * oder schau dir an, was man zb. mit dem Capture Interrupt machen kann.
> Beim Input Capture müsste ich bei jedem > Capture den Zählerstand in eine Variable sichern, das kostet auch wieder > Rechenzeit. Nein, eben nicht. Das macht die Hardware. Das ist ja gerade der Witz am Capture. Das geht aber nur bei relativ kleinen Frequenzen vernünftig. Ansonsten überläuft dich der Capture und ausserdem kommt der Tier nicht sehr weit von einem Capture zum nächsten. Es gibt 2 Messprinzipien: * ein Timer läuft frei durch Das zu messende Signal lösst jeweils einen Capture aus so dass der aktuelle Timerstand wegespeichert wird. In der zugehörigen Interrupt Funktion wird dann der jetztige ge-capture-te Zählerstand mit dem vorhergehenden verglichen. Die Differenz ist ein Mass für die Zeit die in der Zwischenzeit vergangen ist. Da die Hardware den Capturevorgang macht, verlierst du keinen einzigen Taktzyklus. Dieses Messprinzip eignet sich für kleine Frequenzen. Ist unmittelbar klar, denn das zu messende Signal muss dir von einem Capture zum nächsten genügend Zeit lassen um zumindest die Buchhaltung AlterWert = NeuerWert NeuerWert = CaptureRegister machen zu können. Alles weitere kann dann relativ langsam in der Hauptschleife erfolgen. * Ist dein Signal zu schnell, dann wird das Messprinzip umgedreht. Es ist nicht mehr das Signal, dass irgendeine Aktivität auslöst, sondern ein Timer. Der Timer realisiert eine Zeitbasis und liefert alle zb. 0.1 Sekunden ein Signal. Ein 2ter Timer wird als Couter eingesetzt und zählt die Flanken im Eingabesignal. Bei jedem Timertick des ertsen Timers wird der Zählerstand des 2ten Timers ausgelesen und wieder mit der unmittelbar vorhergehenden Messung verglichen. Die Differenz gibt an wieviele Flanken in zb. 0.1 Sekunden eingetroffen sind und daraus kann man dann wieder die Frequenz berechnen. Es ist nicht ungewöhnlich, wenn in einem Messgerät beide Messverfahren implementiert werden und je nach Messbereich zwischen den beiden Verfahren umgeschaltet wird. Wichtig ist nur, dass ich bei beiden Verfahren den eigentlichen Messvorgang komplett mit Hardware erschlagen kann und das Programm alle Zeit der Welt hat, die Messwerte auszuwerten.
Bernie und Ert wrote: > @Peter, > wie meinst Du wie es mit der Capture Funktion gehen soll? Wie kann ich > da über 25 Zyklen messen? Beim Input Capture müsste ich bei jedem > Capture den Zählerstand in eine Variable sichern, das kostet auch wieder > Rechenzeit. Nö, Du mußt nur einen Periodenzähler hochzählen. Oder Du führst die Frequenz auch an den T0-Eingang und liest beim T0-Interrupt den Capture-wert aus. Peter
Ja ganz per Hardware gehts ja auch nicht, wenn ich z.B. die Zeit zwischen 2 Captur Vorgängen messen will, muss ich zumindest während dem 1. Captur Interrupt, das Capture Register in eine Variable sichern. Nach dem 2. Capture Interrupt kann ich dann die Differenz des Capture Registers(Capture Wert 2) mit der Variable(Capture Wert 1) und bekomme die Zeitdifferenz von einer Periode Messsignal. Bei meiner Messung sehr ungenau, da ich ca. Faktor 10 von Messsignal zu CPU (Timer) Frequenz habe. >Da die Hardware den Capturevorgang macht, verlierst du keinen >einzigen Taktzyklus. Verlier ich schon, und zwar beim Speichern der Capture Variable!? Zumindest muss ich im Interrupt entweder Register sichern, oder Differenzwert berechnen. Beim Messverfahren welches Du (Karl Heinz) als 2. Vorgeschlagen hast, würde ich das dann so machen? Timer1 konfiguerieren auf Torzeit z.B. 100ms. Nach 100ms kommt IntT1. Timer1 loslaufen lassen, und Timer0 mit externem Messignal takten. Bei IntT1 dann die gezählten Signalimpulse Anhand der 100ms in Frequenz umrechnen. Geht es auch, dass ich per Hardware den "Zeitstempel" von jeder einkommenden Signalflanke irgendwohin sichern kann? Also nicht nur von einem Capture Event, sondern von den darauffolgenden auch noch. Oder führt kein weg dran vorbei die Register in der ISR auszulesen?
>>Da die Hardware den Capturevorgang macht, verlierst du keinen >>einzigen Taktzyklus. > > Verlier ich schon, und zwar beim Speichern der Capture Variable!? Der springende Punkt ist, dass dir das Speichern des Timers in der Capture Variablen die Hardware macht: Signal kommt rein -> Hardware reagiert darauf -> Hardware speichert den momentanen Zählerstand in einem anderen Register. Von diesem Register kannst du es dann in aller Ruhe auslesen während der Timer im Hintergrund weitertickt und auf den nächsten Capture wartet. Der Wert den du aus dem Captureregister ausliest ist exakt der Zählerstand wie er beim Auftreten des Capture Ereignisses anlag. Selbst dann wenn du das Capture Register erst eine halbe Stunde später ausliest :-) > Zumindest muss ich im Interrupt entweder Register sichern, oder > Differenzwert berechnen. Logisch, ja. Aber die Capture Hardware hat dir ja den Zählerstand beim Auftreten der Flanke am Captue Eingang in einem anderen Register eingefroren. Du hast in der Interruptfunktion alle Zeit der Welt (na ja, bis zum Auftreten des nächsten Captures) um diesen Wert zu verarbeiten.
> Geht es auch, dass ich per Hardware den "Zeitstempel" von jeder > einkommenden Signalflanke irgendwohin sichern kann? Nicht von jeder. Nur von der letzten. > Also nicht nur von einem Capture Event, sondern von den > darauffolgenden auch noch. Oder führt kein weg dran vorbei > die Register in der ISR auszulesen? Da führt kein Weg dran vorbei. Deshalb ist mit diesem Verfahren irgendwann Schluss. Zum einen zählt der freilaufende Timer nicht mehr weit genug um daraus noch was vernünftiges ausrechnen zu können. Zum anderen läufst du irgendwann in das Problem hinein, dass die Captures zu schnell erfolgen und du mit dem wegsichern der Daten nicht mehr nachkommst. Es soll ja auch noch ein wenig Rechenzeit für die Hauptschleife übrig bleiben, damit die Differenzen in eine Frequenz umgerechnet und angezeigt werden können :-) Daher auch: Man macht eine Grenze. Darunter wird mit dem Capture Verfahren gemessen (also die Periodendauer festgestellt), darüber wird mit einer Torzeit die Frequenz direkt gemessen.
> Timer1 konfiguerieren auf Torzeit z.B. 100ms. Nach 100ms kommt IntT1. > Timer1 loslaufen lassen, und Timer0 mit externem Messignal takten. > Bei IntT1 dann die gezählten Signalimpulse Anhand der 100ms in Frequenz > umrechnen. Ganz genau. Das hat denn Vorteil, dass bei steigender Frequenz dein Messwert zunimmt. Nicht wie beim Capture Verfahren, bei dem nimmt der Messwert mit steigender Frequenz ab. Wenn du dort einen Messwert von 2 hast und einen Messwert von 4. War die Frequenz dann wirklich doppelt so hoch? Oder hast du nur die Flanken ungünstig erwischt, so dass die 4 auch eine 3 oder eine 5 sein könnten? Ob 3 oder 4 oder 5 macht aber einen nicht unerheblichen Unterschied bei den Timerfrequenzen die du da hast.
Ok ich habs kapiert, versuch das gleich mal umzusetzen... @ Karl Heinz: Du bist der König!
Mal eine kleine Fehlerbetrachtung: Wenn ich so messe wie erwähnt, beträgt mein Fehler also ca. +/- ein Zählschritt. Wenn ich also 100 Impulse einfange wäre mein Messfehler ca. 1%. Bei 1000 dann 0,1% usw. Vorausgesetzt im dem IntT1 wird ohne Zeitverzug der Zählerstand von den Signalperioden gesichert.
> Vorausgesetzt im dem IntT1 wird ohne Zeitverzug der Zählerstand von den > Signalperioden gesichert. Es genügt, wenn der Zeitverzug zwischen Beginn und Ende der Messung gleich ist. Also: Beginn der Messung: Zählerstand speichern. Torzeit abwarten => Ende der Messung: Zählerstand speichern. Dann subtrahieren sich systematische Fehler wenigstens. Du solltest natürlich während dieser Zeit möglichst keine weiteren Interrupts haben, die dir das Ergebnis vermasseln.
Bernie und Ert wrote: > Mal eine kleine Fehlerbetrachtung: > > Wenn ich so messe wie erwähnt, beträgt mein Fehler also ca. +/- ein > Zählschritt. Wenn ich also 100 Impulse einfange wäre mein Messfehler ca. > 1%. > Bei 1000 dann 0,1% usw. > > Vorausgesetzt im dem IntT1 wird ohne Zeitverzug der Zählerstand von den > Signalperioden gesichert. Das was Jörg schon gesagt hat Plus: Du hast aber nach eigenen Aussagen Frequenzen um die 800 KHz. D.h. bei 0.1 Sekunde Messzeit, kriegst du einen Zählerstand von 80000. Ein Digit daneben, also 79999 oder 80001, wären dann nur noch 0.00125 % Das wird ja doch reichen. Wenn nicht: Messzeit verdoppeln
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.