Hallo
bei der Suche, weshalb mein Programm nicht das tut, was es soll, bin ich
auf ein eigenartiges Verhaltens eines 16-bit Timmers gestoßen:
Ich starte den Timer, er soll so lange zählen, bis ca 400 ms vergangen
sind und dann einen Overflow-Interrupt auslösen.
Ich hatte erwartet, daß es nach Ablauf der Zeit einen Overflow-Interrupt
gibt, egal, wie das Programm die Zwischenzeit verbracht hat.
Das scheint aber nicht der Fall zu sein. Schreibt man
timerstart(); // enthält Timer-, TIMSK-, Interrupteinstellungen usw.
_delay_ms(800); // e i n e Art, auf den Interrupt zu warten
if (flag == 1) // in der ISR wird flag=1 gesetzt
{ .......};
dann kann man das flag abfragen, das man in der ISR gesetzt hat und
alles ist gut.
Schreibt man hingegen
timerstart();
while(1); // eine andere Art, auf den Interrupt zu warten
if (flag == 1)
{ .......};
dann wird entweder die ISR nicht angesprungen oder sonst etwas
Unerklärliches getan. Jedenfalls bleibt das Programm hängen.
Geht das nicht so, wie mir das gedacht habe?
Gruß
Gast-01
> timerstart();> while(1); // eine andere Art, auf den Interrupt zu warten> if (flag == 1)> { .......};>> dann wird entweder die ISR nicht angesprungen oder sonst etwas> Unerklärliches getan. Jedenfalls bleibt das Programm hängen.
Das verwundert nicht. Du hast schließlich eine Endlosschleife in deinem
Programm. Was läßt dich glauben, daß nach dem Interrupt die
while-Schleife auf einmal aufhört, endlos zu sein?
Das ist eine Endlosschleife. Das "if (flag == 1)" wird nie erreicht. Der
Interrupt unterbricht die Schleife nur für kurze Zeit, er bricht sie
nicht ab.
Was du wahrscheinlich haben möchtest:
while(1);// eine andere Art, auf den Interrupt zu warten
Das ist eher eine Art auf den jüngsten Tag zu warten, wenn man denn
daran glaubt.
Was genau soll denn den uC veranlassen die nachfolgende Zeile
1
if(flag==1)
auszuführen?
Der uC ist ja munter dabei while(1) zu bearbeiten, wenn der Interrupt
auftritt. Dann wird zwar die Interruptroutine abgearbeitet, aber danach
macht er wieder mit while(1) weiter. Was anderes steht ja auch nicht da.
1
_delay_ms(800);// e i n e Art, auf den Interrupt zu warten
wartet auch nicht auf den Interrupt, sonderen darauf, das 800ms
vergangen sind. Der Interrupt wird zwar abgearbeitet, wenn er
zwischenzeitlich auftritt (und verlängert auch das delay) aber er
beendet das delay nicht vorzeitig.
Interrupts an sich ändern überhaupt nichts an der Abarbeitung des
Programmteils, der gerade läuft, wenn der Int auftritt. Danach geht es
wie gehabt weiter. Deswegen bemutzt man ja die volatile flag variable,
damit das Hauptprogramm mitbekommt, das ein Int aufgetreten ist.
Deswegen also, z.B. sowas wie:
>Das verwundert nicht. Du hast schließlich eine Endlosschleife in deinem>Programm. Was läßt dich glauben, daß nach dem Interrupt die>while-Schleife auf einmal aufhört, endlos zu sein?
Nun ja, ich hatte mir schon gedacht, daß ich nicht so ohne weiteres aus
der Schleife herauskomme, um den flag abzufragen und daß ich es
innerhalb der Schleife tun sollte. Also werde ich, wie ihr schreibt,
while (flag == 0) in die Schleife integrieren.
Gäbe es denn überhaupt eine Möglichkeit, durch einen Sprungbefehl in der
ISR aus der Schleife herauszukommen? Return würde ja wohl nicht helfen,
da es auch nur zurück in die Schleife führt. (Ist nur eine Frage
neugierdehalber, es ginge bei mir ohnehin nicht, weil ich die ISR nicht
nur an einer Stelle benutzen möchte).
mfg
Gast-01
Gast-01 schrieb:
> Gäbe es denn überhaupt eine Möglichkeit, durch einen Sprungbefehl in der> ISR aus der Schleife herauszukommen? Return würde ja wohl nicht helfen,> da es auch nur zurück in die Schleife führt. (Ist nur eine Frage> neugierdehalber, es ginge bei mir ohnehin nicht, weil ich die ISR nicht> nur an einer Stelle benutzen möchte).
Im Prinzip gäbe es eine Möglichkeit (setjmp/longjmp)
Aber das willst nicht wirklich benutzen. Vergiss es gleich wieder.
Das Wesen einer ISR ist es, dass das Hauptprogramm exakt dort weiter
macht, wo es unterbrochen wurde. Wenn die ISR dem Hauptprogramm etwas
mitzuteilen hat, dann tut sie das, in dem globale Variablen manipuliert
werden, die vom Hauptprogramm an günstiger Stelle abgefragt werden.
Alles andere führt über kurz oder lang nur zu Ärger. Wenn man aus diesem
Korsett ausbricht, muss man schon sehr genau wissen was man tut und auch
die Internals des Compilers gut kennen, denn schliesslich mischt man
sich damit in einige Bereiche ein, die eigentlich unter alleiniger
Kontrolle des Compilers stehen sollten (Returnstack, Stack Frames, etc)
Hallo Gast-01,
warum willst Du überhaupt mit einer while() Schleife Rechenzeit
verschwenden, wie wäre es denn mit
void main(void)
{
timerstart();
while(1) // Endlosschleife in Main
{
if (flag == 1)
{
.......
flag = 0;
};
// tue andere wichtige Sachen
}
};
So muß man auf den Interrupt nicht warten und kann nebenbei das
Hauptprogramm weiter abarbeiten.
Viele Grüße,
Steffen
Ich finde die Frage interessant. Wir hatten schon so oft hier die Frage
wie man die Rücksprung-Adresse von einem Interrupt ändern kann uswusf.
Leider war mir nie ganz klar, wie die Fragesteller darauf kamen.
Nimmt man aber diese Frage, so scheint es, zumindest bei einem Teil der
Frager, das Problem zu geben, das Interrupts nicht verstanden werden.
Insbesondere die Tatsache, das ein Interrupt, den Ablauf des
unterbrochenen Teils nicht ändert. D.h. also, ein solcher Frager würde
wahrscheinlich gerne irgendwas wie:
1
ISR(){
2
mach_was();
3
gotonachher;// eigentlich eher manipulation des stackinhalts
4
}
5
6
main(){
7
timerinit();
8
while(1);
9
nachher:
10
mach_was_anderes();
11
}
schreiben.
Das ist natürlich nur ein pseudocode.
>Nun ja, ich hatte mir schon gedacht, daß ich nicht so ohne weiteres aus>der Schleife herauskomme, um den flag abzufragen und daß ich es>innerhalb der Schleife tun sollte. Also werde ich, wie ihr schreibt,>while (flag == 0) in die Schleife integrieren.>Gäbe es denn überhaupt eine Möglichkeit, durch einen Sprungbefehl in der>ISR aus der Schleife herauszukommen? Return würde ja wohl nicht helfen,>da es auch nur zurück in die Schleife führt. (Ist nur eine Frage>neugierdehalber, es ginge bei mir ohnehin nicht, weil ich die ISR nicht>nur an einer Stelle benutzen möchte)
Das ist kein workaround, was wir da vorschlagen. Das ist das normale
, korrekte Verfahren.
Im Gegenteil: das mit dem Sprungbefehl wäre furchtbar ungeschickt,
auch wenn es geht. Auch die Manipulation der Adresse auf dem Stack wäre
möglich, ist gilt aber als unsauber.
Du kommst auf diese Problem (und die Sprunglösung), weil Du nicht
verstanden hast, wie Interrupt-Routine und Hauptprogramm in Beziehung
stehen.
Das mit der Variablen IST die korrekte Lösung um die das auftreten des
Interrupts an das Hauptprogramm zu melden.
Die Gründe dafür sind nicht ganz leicht zu erklären:
1. Nach der Verwendung: Beim Interrupt wird die momentan abgearbeitete
Adresse (eigentlich die folgende) auf dem Stack abgelegt. Das würde
nicht der Fall sein, wenn die Manipulation der Rücksprungadresse normal
wäre.
2. Wegen der möglichen Nebenwirkungen: Wenn die Rücksprungadresse
manipuliert würde, so wären auch Maßnahmen nötig um die Effekte von noch
nicht oder nur teilweise ausgeführten Schritten im Hauptprogramm zu
kompensieren.
3. Übersicht: So wie gotos sind auch longjmps bzw. geänderte
Rücksprungadressen verpönt, weil sie den Ablauf unübersichtlich, den
Test und die Codeanalyse schwieriger wenn nicht unmöglich machen.
Da könnte man glatt mal einen Wiki-Artikel drüber schreiben.
Wenn man das Problem der Fragesteller noch etwas genauer betrachtet, so
haben sie zwar erkannt, das der Kontrollfluss des Hauptprogrammes
geändert werden soll, aber sie sind in dem Moment zu sehr auf das
Ausführungsmodell fixiert.
Also etwas so:
Da ist der Prozessor, setzt den Instruction Pointer, führt den befehl
aus, setzt den IP, nächster Befehl.... Ooops, ein Interrupt. Also IP auf
den Stack, interrupt routine ausführen (IP, Befehl....). Und nun? Wieder
zurück zum Hauptprogramm. Jaaaa, aber ich will ja jetzt was ganz anderes
machen, wo der Interrupt aufgetreten ist! Also der IP ist jetzt
"falsch".
Was hier glaube ich nützlich wäre, wäre die Sichtweise, das man auch
über Daten den Kontrollfluss ändern kann. Das "IF", "SWITCH" usw. den
Kontrollfluss ändern. Das das Ergebnis eines Interrupts auch geänderte
Daten sein können bzw. in gewissen Fällen müssen.