Forum: Mikrocontroller und Digitale Elektronik Schleife mit interrupt beenden.


von Martin #. (martin-)


Lesenswert?

Hallo.

Ich habe folgendes Problem:

es gibt eine simple Schleife:
1
......
2
......
3
loop:
4
     sbis PINB,0x00
5
     rjmp loop
6
......
7
......
Die schleife wird durchgegangen bis ein Signal eintrifft.
Diese schleife muss auch sehr schnell sein, deshalb möchte ich nichts
mehr in die Schleife reinpacken.
MCU soll aber nur eine bestimmte Zeit auf das Signal warten, und wenn
es nicht ankommt, die schleife verlassen und mit ausführung des Code
fortfahren.
Hier bietet sich also an, einen Timer und Interrupt zu verwenden.

Wenn der Timer überläuft springt MCU zu der entsprechenden Routine und
kehrt genau da wieder zurück wo der Interrupt kamm.
Die schleife läuft also weiter.

Ich weiss jetzt nicht was ich in der Interrupt-Routine anstellen kann
damit nach dem die Interrupt-Routine beendet ist auch die Schleife
bendet wird, sprich rjmp loop übersprungen.
PIN am PORTB kann ich nicht setzen weil es als Eingang konfiguriert
wurde.

Ich hoffe, ich habe das Problem einigermassen verständlich
beschrieben.
Wäre dankbar für eine Lösung dieses Problems.

von Bolle (Gast)


Lesenswert?

Eine Möglichkeit, dieses Problem zu lösen, sieht so aus:
1
...... 
2
...... 
3
     clr t
4
loop: 
5
     tst  t 
6
     sbis PINB,0x00 
7
     breq loop 
8
...... 
9
......
Im Timer-Overflow-Interrupt-Handler mußt Du das t-Register auf einen
Wert ungleich Null setzten (z. B. 1).  Dann wird die loop-Schleife
(auch) verlassen (Wirkungsweise klar?).

Die Frage, ob diese Lösung mit Deinen Geschwindigkeitsanforderungen
noch vereinbar ist, kann ich natürlich nicht beantworten.

von Marco K. (Gast)


Lesenswert?

Servus Martin,

ich hätt dir da auch noch ne Möglichkeit, ist zwar etwas umständlich
aber effektiv:
1
ISR:      ; Interruptroutine
2
  ...
3
  ...
4
  pop temp  ; Rücksprungadresse vom Stack löschen
5
  pop temp
6
  ldi temp, low(ende)  ; Neue Rücksprungadresse in Stack laden
7
  push temp
8
  ldi   temp, high(ende)
9
  push temp
10
  reti
11
12
13
loop:
14
  sbis PINB,0x00
15
  rjmp loop
16
17
ende:
18
  rjmp ende

Du hast hier auch die Möglichkeit durch ändern der Adresse in der ISR
jeden Punkt im Programm anzuspringen.
Gruß Marco

von Peter D. (peda)


Lesenswert?

Eine Möglichkeit ist, den Timerinterrupt mit RET zu beenden:
1
        sei              ; I-Bit setzen
2
loop:
3
        sbis    PINB, 0  ; Abbruch mit PINB0 = 1
4
        brie    loop     ; loop mit I = 1
5
        brid    timeout  ; Entscheidung ob Abbruch durch Timer
6
; ...
7
timeout:
8
; ...

Stackmanipulation sollte man nur im äußersten Notfall machen.
Und hier bringt es ja auch nichts, die Schleife wird nie kürzer als 3
Zyklen.


Peter

von Marco K. (Gast)


Lesenswert?

Die Idee ist ja mal Super!
Die Befehle BRIE und BRID hab ich noch gar nicht gekannt, obwohl ich
die Datenblätter schon zichmal gelesen hab, man lernt ja nie aus.

von Thomas K. (thkais)


Lesenswert?

Und was wäre, wenn Du einen externen Interrupt nimmst? Dann bräuchtest
Du den Controller überhaupt nicht in einer Schleife warten zu lassen.

von Peter D. (peda)


Lesenswert?

"Und was wäre, wenn Du einen externen Interrupt nimmst?"

Du wirst lachen, Polling ist schneller als ein Interrupt (7..10 Zyklen
bis zum Eintritt).

Ich habe auch mal in einem ATTiny26 pollen müssen, weil ich nur so
schnell genug auf ein Signal reagieren konnte. Ich mußte einen CPLD
ersetzen.


Peter

von Martin #. (martin-)


Lesenswert?

Vielen Dank für Eure Antworten.
Sicher sind alle hier vorgeschlagen Lösungen wert sie zu testen um die
optimale Lösung für meine Anwendung zu finden.
Wir gesagt je länger die Schleife desto schlechter, stack würde ich
auch ungern maniplieren.
Die Lösung von Peter scheint beides zu erfüllen (kurz, keine Stack
manipulation),ich verstehe nur nicht warum da beide Befehle vorhanden
sein müssen, eins springt doch wenn Interrupt-Flag gesetzt ist, das
andere wenn nicht, oder sind das Beispiele
Ich werde aber alle Lösungen testen, der ATMega8 wird hier halt an
seine grenzen gebracht, da muss die optimale Lösung her.

Zu besseren verständnis erkläre ich einfach wozu die Schleife ist und
warum man hier ein externer Interrupt keinen Sinn macht.
Ich versuche hier so was wie ein Osilloskop zu bauen.
Das Signal das am PORTB ankommt, ist von der Trigger-Einhheit die aus
dem Eingangsignal Triggerimpulse generiert. Sie funktioniert recht
zuverlässig, aber wenn der MCU mit unterschidlicher geschwindikkeit auf
diese Impulse reagiert erhällt man kein stabieles Bild. Deshalb muss
diese Schleife möglichst kurz sein.
Wenn jedoch kein Signal am Eingang gibt, hängt MCU bei dieser Schleife,
das ist jedoch in sofern schlecht dass dann keine kommunikation mit dem
MCU möglich ist (UART-Interrupt ist hier auch problematisch weil Daten
gesendet und empfangen werden).
Es soll also nur eine bestimmte Zeit auf den Trigger-Impuls gewartet
werden. Einen externen Interupt zu verwenden macht keinen Sinn, weil
dazu bräuche man sowie so zusätzlichen externen Timer der ja eh schon
im MCU vorhanden ist, und aus sicht des Programms sich wie ein externes
Gerät verhält, läuft nebenbei und keine Rechenkapazität braucht.
Ausserdem es gibt mehrere Sampling-Frequenzen von 5kHz bis 2MHz und für
die höchsten brauch man getrennte Routinen(mindestens 3), sonst sind sie
zu langsam.

Ich bedanke mich noch mal für die Lösungsvorschläge.

von Thomas K. (thkais)


Lesenswert?

@Peter: Wieder etwas dazugelernt, das wußte ich auch noch nicht.

@Martin: Ich meinte eigentlich, daß man einen der Ext-In Pins zum
Triggern nimmt. Ein externer Timer ist natürlich Unsinn, aber auch
nicht notwendig. Abgesehen davon, ist die Lösung von Peter in diesem
Fall die bessere.

von Siegfried (Gast)


Lesenswert?

Hallo,

ich suche eine Lösung für ein ähnliches Problem. Allerdings nutze ich C 
als Programmiersprache.

Ich möchte über einen exdternen Interrupt (Taster) eine Schleife im 
Main() beenden. Gibt es eine Möglichkeit z.B. im ISR(INT0_vect){} 
anzugeben, das dann im Main() eine Schleife beendet wird? Ein einfaches 
break; reicht da ja nicht aus.

Bin für jede Hilfe dankbar.

Gruss Siegfried

von Wolfram Q. (quehl)


Lesenswert?

@ Peter Danegger

das Programmteil ist sicher ganz gut, wenn das eine Standalone Lösung 
ist. Wenn noch andere Interrupts auftreten, ist das nicht mehr zu 
gebrauchen. Der Threaderöffner sprach aber auch von Kommunikation und da 
sind Interrupts erforderlich.
Die universellere Lösung finde ich daher mit der Manipulation des 
Stacks.

Auch wenn das Thema schon alt ist, so finde ich die vorgeschlagenen 
Lösungen schon beachtenswert.

mfg

von Peter D. (peda)


Lesenswert?

Wolfram Quehl wrote:
> @ Peter Danegger
>
> das Programmteil ist sicher ganz gut, wenn das eine Standalone Lösung
> ist. Wenn noch andere Interrupts auftreten, ist das nicht mehr zu
> gebrauchen.

Warum denn nicht?

Der Interrupt wird mit RET verlassen, die Schleife wird dadurch sofort 
beendet und schon kannst Du die Interupts wieder freigeben (den Timer 
dann sperren oder anhalten).
Sind vielleicht 15 Zyklen unter Interrutsperre, jeder Interrupt dauert 
länger.

Man könnte aber auch jedes andere Bit im SREG nehmen.


> Die universellere Lösung finde ich daher mit der Manipulation des
> Stacks.

Nö.


Peter

von Wolfram Q. (quehl)


Lesenswert?

der Timer war ja dafür da, daß die Wartezeit bis zum Ausstieg definiert 
ist. Wenn vor Ablauf des Timers z.B. ein Uart Interrupt auftritt, dann 
wird die Schleife vorzeitig verlassen. Das T-bit wäre sicherlich besser 
geeignet, aber auch nur, wenn es nicht woanders verwendet wird.
Da ich noch Anfänger bin, muß ich mir das wohl noch mal überlegen.

mfg

von Hannes L. (hannes)


Lesenswert?

Mit dem T-Flag hat es den Haken, dass es dank SREG-Sicherung nicht 
ankommt. Wenn also die ISR das T-Flag verändert, merkt das Hauptprogramm 
nix davon.

...

von Peter D. (peda)


Lesenswert?

Hannes Lux wrote:
> Mit dem T-Flag hat es den Haken, dass es dank SREG-Sicherung nicht
> ankommt. Wenn also die ISR das T-Flag verändert, merkt das Hauptprogramm
> nix davon.

Wenn die ISR das T ändern soll, gibts nen ganzen einfachen Trick: das 
SREG nicht sichern. Niemand zwingt Dich, das SREG zu sichern.

Man sichert das SREG nur, wenn die ISR irgendwo zuschlagen kann, der 
Kontext also unbekannt ist.

Hier weiß aber die ISR ganz genau, in welchem Kontext sie zuschlägt und 
damit darf sie auch Informationen im SREG übergeben.


Peter

von Hannes L. (hannes)


Lesenswert?

Ja sicher kommt es immer auf den Kontext an, was in einem Fall 
zweckmäßig ist, kann im anderen Fall unzweckmäßig sein. Ein überall 
gültiges Universalrezept wird es nicht geben.

Ich bezog mich allerdings auf Wolframs Aussage, dass für diesen Zweck 
das T-Flag besser geeignet sei als das I-Flag.

Wolfram: Die Nutzung des I-Flags als Timeout-Merker ist genial. Deine 
Bedenken, dass andere Interrupts stören, sind unbegründet, denn alle 
anderen ISRs werden ja weiterhin mit RETI beendet. Nur der 
Timeout-Interrupt endet mit RET und wirft damit das Programm aus der 
Warteschleife. Sorry, das ist mir beim ersten Lesen noch gar nicht 
aufgefallen, erst jetzt, wo ich drüber nachdenke.

...

von Wolfram Q. (quehl)


Lesenswert?

ja, das war mir auch nicht gleich aufgefallen, aber dann müßte das 
I-Flag wieder zurückgesetzt werden, wenn die Schleife verlassen wurde. 
Sonst könnten keine anderen Interrupts mehr auslösen. Wenn also hinter 
Timeout: noch SEI stehen würde, dann wäre es vielleicht klarer gewesen.

mfg

von Thomas J. (Gast)


Lesenswert?

Hallo,

Wozu eigentlich muss man für so etwas eine ISR benutzen? Wenn man im
Hauptprogramm nur ein Flag pollt kann man doch auch gleich das
entsprechende Ereignissflag im Interrupt Flag Register benutzen.
Dann spart man die Verzögerung durch die ISR.

Thomas

von AVRFan (Gast)


Lesenswert?

>Wozu eigentlich muss man für so etwas eine ISR benutzen? Wenn man im
>Hauptprogramm nur ein Flag pollt kann man doch auch gleich das
>entsprechende Ereignissflag im Interrupt Flag Register benutzen.
>Dann spart man die Verzögerung durch die ISR.

Die Verzögerung durch die ISR ist Nebensache, weil sie ja nur im 
Timeout-Fall wirksam wird.  Wichtig ist, dass das Pin-Abtastintervall 
möglichst kurz sein soll, und das beträgt hier lediglich 3 (!) 
Taktzyklen.  Denkt man ein wenig nach, erkennt man, dass es keinen Code 
gibt, mit dem sich 3 Taktzyklen (oder gar noch weniger) wesentlich 
anders erreichen ließen.

von Wolfram Q. (quehl)


Lesenswert?

@ Thomas J

wenn das Interrupt Flag gesetzt wird, muß ein Interrupt ausgelöst werden 
und dann geht es in die ISR. Ich wüßte nicht, wie man das verhindern 
kann.

mfg

von Wolfram Q. (quehl)


Lesenswert?

das habe ich jetzt verstanden.
zur prakt. Anwendung:

es wird ein Timer benötigt, der vor Aufruf des Programms initialisiert 
bzw. auf einen definierten Zustand gesetzt werden muß, um eine bestimmte 
Timeout Zeit zu bekommen. Zusätzlich besteht die Einschränkung, daß die 
Timeout Zeit nicht länger sein kann als die Timerzeit. Mehrmaliges 
Durchlaufen des Timers ist nicht möglich. Das kann bei längeren Pulsen 
am Eingang zum Problem werden.
Nun habe ich als weiteren Gedankengang gehabt, daß statt brie ein 
Registerbit abgefragt wird, das z.B. von der sowieso vorhandenen Uhr in 
der Uhr-ISR gesetzt wird. Dieses einfache Setzen reicht jedoch nicht 
aus, weil beim Aufruf des Pin-abfrageprogramms gerade das Registerbit 
gesetzt worden sein kann. Dann wäre die Zeit zu kurz.
Abhilfe durch Differenzbildung: Sek. oder Überlaufzähler lesen, Zeit 
addieren und in der Uhr als Abfrage zum Setzen des Bits verwenden.

Ob mir das jetzt was nutzt, weiß ich noch nicht. Schließlich soll der MC 
nicht warten, sondern in der Timeout Zeit andere Aufgaben ausführen. Und 
dann wären wir wieder in der Nähe der Jobflags.
Aber als Idee auf jeden Fall anerkennenswert.

mfg

von Michael W. (wiebel42)


Lesenswert?

Kann mir mal bitte einer erklären was schlecht daran sein soll den Stack 
zu manipulieren. Sicher es ist keine Standardvorgehen, aber beim 
Assembler programmieren muss man sowieso auf alles und jeden aufpassen, 
da kommt es darauf auch nicht mehr an, es ist eben noch eine Stufe 
Tiefer als die normalen Assembler Mnemonics, aber ich kann da nichts 
schlechtes drin entdecken, klar muss man aufpassen das nacher kein Müll 
im Stack landet, aber  wie gesagt aufpassen muss man sowieso, und auf 
die weise lernt und versteht man auch gleich was bei einem Interrupt und 
einem RET wirklich im hintergrund abläuft, ich finde eigentlich die 
Lösung mit dem Stack sogar sehr elegant. -wiebel

von Peter D. (peda)


Lesenswert?

Michael Waiblinger wrote:

> aber ich kann da nichts
> schlechtes drin entdecken

Du hast nen bedingten Sprung ohne Sprungziel (Label) !!!

Und wenn Du dann nach 2 Jahren mal wieder auf den Code schaust, beginnt 
das große Kopfkratzen. Du änderst was am Code und wunderst Dich, warum 
was ganz anderes ausgeführt wird.

Auch Assemblerprogramme darf man strukturiert und lesbar schreiben.


Außerdem brauchst Du einen zusätzlichen Call, um dahinter zu springen, 
also 2 Bytes mehr Stackverbrauch.


Peter

von Wolfram Q. (quehl)


Lesenswert?

das mit dem Stack halte ich fast für genauso gut wie Peters Code. Nur 
das Bessere schlägt das Gute. Bei der Stackmanipulation wird etwas mehr 
Code und Zeit verbraucht. Ist nicht erheblich, aber es bringt keinen 
Vorteil gegenüber Peters Code.
Und die Nachteile im prakt. Teil sind da auch nicht besser. Entweder 
extra Timer oder man ist auf bestimmte Zeiten eingegrenzt.

mfg

von Michael W. (wiebel42)


Lesenswert?

Peter Dannegger wrote:
> Michael Waiblinger wrote:
>
>> aber ich kann da nichts
>> schlechtes drin entdecken
>
> Du hast nen bedingten Sprung ohne Sprungziel (Label) !!!
Wenn ich das Beispiel von Marco ganz oben nehme hab ich sehr wohl ein 
Label.
>
> Und wenn Du dann nach 2 Jahren mal wieder auf den Code schaust, beginnt
> das große Kopfkratzen. Du änderst was am Code und wunderst Dich, warum
> was ganz anderes ausgeführt wird.
>
> Auch Assemblerprogramme darf man strukturiert und lesbar schreiben.
Ich finde nicht das die Lesbarkeit darunter leidet, selbstverständlich 
muss man sich darüber bewusst sein, dass man sowas verwendet hat, aber 
ich finde das kein bisschen unlesbarer als viele andere Sachen in 
Assembler.
>
> Außerdem brauchst Du einen zusätzlichen Call, um dahinter zu springen,
> also 2 Bytes mehr Stackverbrauch.
Das verstehe ich nicht, ich hole die 2 Byte raus und stopfe zwei neue 
rein, klar wenn ich die einfach dazustopfe wird das nix, aber das 
erfüllt ja auch dann nichtmehr den Zweck.

Ich finde wenn es funktioniert isses gut, wenn nicht nicht. Assembler 
nach zwei Jahren zu verstehen ist so oder so knifflig wenn man das gut 
kommentiert geht das doch.

Wenn ich einen echten Grund höre warum das schlecht ist, seh ich das ja 
gerne ein, aber so klingt das ähnlich wie das "goto ist böse" Dogma, 
dass heute auch keiner mehr richtig begründen kann.

von Peter D. (peda)


Lesenswert?

Ja ja, is schon ne Weile her, ich hab den Code nicht nochmal angesehen.

Ich dachte, er popt einfach nur die Returnadresse weg und landet dann 
beim Caller (wäre ja auch ne Möglichkeit).


Peter

von Michael W. (wiebel42)


Lesenswert?

Peter Dannegger wrote:
> Ich dachte, er popt einfach nur die Returnadresse weg und landet dann
> beim Caller (wäre ja auch ne Möglichkeit).
Ahh, ok verstehe, das wäre auch sehr flott, da muss ich dann aber auch 
gestehen, dass sowas dann in der tat dringend kommentierungspflichtig 
ist. Find ich aber eigentlich auch wieder elegant, wenn auch tatsächlich 
sehr schwer zu beherrschen. -wiebel

von Horst Gschwandtner (Gast)


Lesenswert?

> Hallo,

> Wozu eigentlich muss man für so etwas eine ISR benutzen? Wenn man im
> Hauptprogramm nur ein Flag pollt kann man doch auch gleich das
> entsprechende Ereignissflag im Interrupt Flag Register benutzen.
> Dann spart man die Verzögerung durch die ISR.

> Thomas

Bis zu diesem Post musste ich lesen bis einer genau meiner Meinung war, 
wieso nicht:

Timer = Wert;
TF = 0;

while(!TF);        // warten bis Timer ueberlaueft


ist die schnellst Variante. Warum zum Teufel soll man in die Isr 
springen und dort eine Variable verändern, dann zurückt, dann erst aus 
der Schleife springen - ist völliger Unsinn. Den Timer Interrupt 
deaktivieren, dann wird auch die Isr nicht aufgerufen, das Timer Flag 
wird aber trotzdem gesetzt.

bcf   TimerFlag
loop: btfss TimerFlag
      goto loop

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.