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?
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?
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.
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.
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