Forum: Compiler & IDEs Ohne ISR Interrupt handeln


von Bernie und Ert (Gast)


Lesenswert?

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?

von johnny.m (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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?

von Bernie und Ert (Gast)


Lesenswert?

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?

von johnny.m (Gast)


Lesenswert?

Die Abfrage in der while-Schleife ist, wie oben schon gesagt, korrekt. 
Nur das sei() muss natürlich weg...

von Karl heinz B. (kbucheg)


Lesenswert?

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.

von Bernie und Ert (Gast)


Lesenswert?

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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Nimm doch input capture dafür, wenn die Frequenz gering ist, oder
takte den Zähler extern, wenn sie höher ist.

von Bernie und Ert (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von johnny.m (Gast)


Lesenswert?

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.

von Bernie und Ert (Gast)


Lesenswert?

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.

von johnny.m (Gast)


Lesenswert?

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

von Bernie und Ert (Gast)


Lesenswert?

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;
}

von johnny.m (Gast)


Lesenswert?

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

von johnny.m (Gast)


Lesenswert?

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

von Bernie und Ert (Gast)


Lesenswert?

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.

von Bernie und Ert (Gast)


Lesenswert?

bei 0,8 MHz liefert er mir Zählerstände von entweder 503 oder 513, aber 
nix dazwischen.

von Karl heinz B. (kbucheg)


Lesenswert?

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.

von Bernie und Ert (Gast)


Lesenswert?

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.

von johnny.m (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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


von Bernie und Ert (Gast)


Lesenswert?

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?

von Bernie und Ert (Gast)


Lesenswert?

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

von Karl heinz B. (kbucheg)


Lesenswert?

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

von Karl heinz B. (kbucheg)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Bernie und Ert (Gast)


Lesenswert?

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?


von Karl heinz B. (kbucheg)


Lesenswert?

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




von Karl heinz B. (kbucheg)


Lesenswert?

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

von Karl heinz B. (kbucheg)


Lesenswert?

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


von Bernie und Ert (Gast)


Lesenswert?

Ok ich habs kapiert, versuch das gleich mal umzusetzen...

@ Karl Heinz: Du bist der König!

von Bernie und Ert (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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

von Karl heinz B. (kbucheg)


Lesenswert?

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
Noch kein Account? Hier anmelden.