Forum: Mikrocontroller und Digitale Elektronik ISR sprung in "normalen" Code


von Stefan (Gast)


Lesenswert?

Hallo,

ich möchte, dass ein Interrupt keine eigentliche ISR ausführt, sondern 
an eine gewisse Stelle im Hauptprogram springt (so eine Art Teilreset).
Wenn ich jedoch direkt aus der ISR mit RJMP raus springen würde, dann 
wäre ja noch die Rücksprungadresse im Stack und Globale Interrupts 
deaktiviert.
Reicht folgender Code aus, um das Problem zu beheben?

1
.equ F_CPU = 4800000
2
3
.def temp = R16
4
5
.org 0x000
6
  RJMP RESET
7
  RJMP ISR_SMALL_RESET
8
9
RESET:
10
  ;Hauptinitialisierung
11
SMALL_RESET:
12
  ;Reset der Module
13
  SEI  ;Globale Inerrupts nach Reset und Small Reset erlauben
14
15
MAIN:
16
  ;Hauptcode
17
  RJMP MAIN
18
19
ISR_SMALL_RESET:
20
  POP  ;Stack Pointer "säubern" 
21
  POP
22
  RJMP SMALL_RESET

von holger (Gast)


Lesenswert?

>Reicht folgender Code aus, um das Problem zu beheben?

Wieso probierst du es nicht aus?

von Alexander F. (alexf91)


Lesenswert?

Löse den Reset über den Watchdog aus.
Beim Start kannst du dann den Grund für den Reset überprüfen und 
entsprechend darauf reagieren.

https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_Watchdog

von Magic S. (magic_smoke)


Lesenswert?

Natürlich reicht das aus. Wenn Du die Änderungen durch den 
Interrupt-Einsprung rückgängig machst, woher soll der µC dann noch 
wissen, daß da einer stattgefunden hat? Erst Stack reinigen, danach 
Interrupts wieder aktivieren.

Ähnlich haben x86-DOS-Viren ermittelt, an welche Adresse im RAM sie 
geladen wurden.

call 0
pop <adresse>

von Rolf M. (rmagnus)


Lesenswert?

Stefan schrieb:
> Wenn ich jedoch direkt aus der ISR mit RJMP raus springen würde, dann
> wäre ja noch die Rücksprungadresse im Stack und Globale Interrupts
> deaktiviert.

Außerdem liegt auf dem Stack noch wer weiß was alles vom Programm, das 
durch den Interrupt ja irgendwo mittendrin unterbrochen wurde.
Aus einer ISR heraus direkt ins Hauptprogramm springen klingt sehr 
abenteuerlich.

von Stefan (Gast)


Lesenswert?

Ich bin mir eben nicht sicher was so ein Interrupt alles macht. Ich 
weiß, dass er quasi CLI macht und natürlich in den Stack die 
Rücksprungadresse schreibt.
Ob es da mehr gibt wollte ich eben erfragen.

Ein voller Reset soll nicht stattfinden, da ich werte aus dem RAM 
erhalten will. Es sollen nur ein paar Module in einen definierten 
Zustand übergehen und dann wieder normal die Arbeit aufnehmen.

von Rolf M. (rmagnus)


Lesenswert?

Stefan schrieb:
> Ich bin mir eben nicht sicher was so ein Interrupt alles macht. Ich
> weiß, dass er quasi CLI macht und natürlich in den Stack die
> Rücksprungadresse schreibt.
> Ob es da mehr gibt wollte ich eben erfragen.

Der Interrupt unterbricht dein Programm irgendwo mitten während seiner 
Ausführung. Kommt dein Programm wirklich in allen Fällen damit klar, 
wenn an einer beliebigen Stelle mittendrin plötzlich wo anders hin 
gesprungen wird?

> Ein voller Reset soll nicht stattfinden, da ich werte aus dem RAM
> erhalten will. Es sollen nur ein paar Module in einen definierten
> Zustand übergehen und dann wieder normal die Arbeit aufnehmen.

Setze in der ISR ein Flag und rufe vom Hauptprogramm aus die 
Initialisierungs-Funktionen auf.
Um es noch etwas klarer zu machen: Mir würde kein einzige Szenario 
einfallen, bei dem ein Jump aus einer ISR heraus ins Hauptprogramm 
hinein eine gute Idee wäre.

von Sascha W. (sascha-w)


Lesenswert?

Hallo,

die 2x pop kannst du dir sparen, lade lieber an deiner small_reset 
stelle wieder RAMEND in den Stackpointer und gib im Anschluss die 
Interrupts frei.
Wenn du nur die gesichtere Rückprungadresse der ISR vom Stack löschst 
könnten dort immer noch gesicherte Register oder Rücksprungadresen aus 
anderen Subroutinen bleiben. Das stört zwar erst mal nicht deine 
gewünschte Funktion, aber wenn du die Reset-ISR paar mal auslöst dann 
verbleibt mit der Zeit immer mehr auf dem Stack und irgendwann wenn du 
nicht mehr dran denkst tritt ein ganz komischer Fehler auf weil der 
Stack überläuft.

Sascha

von c-hater (Gast)


Lesenswert?

Stefan schrieb:

> ich möchte, dass ein Interrupt keine eigentliche ISR ausführt, sondern
> an eine gewisse Stelle im Hauptprogram springt (so eine Art Teilreset).

Das ist Mist. Du weißt ja nicht, an welcher Stelle dein Hauptprogramm 
unterbrochen wurde, dementsprechend kann da beliebig viel Müll auf dem 
Stack liegen, nicht nur die Rücksprungadresse der aktuellen ISR.

Sprich: Stackunterlauf ist der vorprogrammierte End-GAU dieser überaus 
schwachsinnigen Idee...

Wennschon, mußt du also mindestens den kompletten Stack abräumen und 
nicht nur die Rückspungadresse der aktuellen ISR. Dann ist die Sache 
wenigstens erstmal prinzipiell sauber.

Allerdings muß dann alles (und wirklich absolut alles), was in main() 
passiert, darauf vorbereitet sein. D.h.: nix darf davon abhängig sein, 
dass eine Aufrufstruktur von Unterprogrammen immer vollständig 
abgearbeitet werden kann. Das ist durchaus machbar, aber oft garnicht so 
einfach zu realisieren, wie man im ersten Moment denken mag und es führt 
i.A. zu extrem ineffizienten Code.

Insgesamt: Ein technisch mögliches, aber absolut nicht empfehlenswertes 
Design-Pattern. Nur die Verwender von längeren Software-Zeitscheifen 
kommen normalerweise auf solche unsinnigen Ideen. Und diese sollten sich 
statt mit schrägen Hacks lieber damit beschäftigen, wie sie diese 
schwachsinnigen Warteschleifen loswerden können...

von Stefan (Gast)


Lesenswert?

Klar, einfach den Stack neu initialisieren. **headslap**

Es geht im Endeffekt darum, dass ein Programm alle paar Sekunden genau 
die gleiche Tätigkeit machen soll. Nur soll es vom vorherigen mal ein 
paar Werte im RAM behalten und vergleichen.
Das die ISR mitten im Program aufgerufen wird, ist nahezu unmöglich, da 
es nach paar 100ms abgearbeitet sein soll und alles in den Tiefschlaf 
geht, die ISR aber nur alle 10sec kommt. Selbst wenn sie mal ausversehen 
rein haut, stört es nicht, da wie gesagt alles neu initialisiert 
wird/werden muss (bis auf die RAM Adresse) und somit nix passieren kann.

von c-hater (Gast)


Lesenswert?

Stefan schrieb:

> Es geht im Endeffekt darum, dass ein Programm alle paar Sekunden genau
> die gleiche Tätigkeit machen soll. Nur soll es vom vorherigen mal ein
> paar Werte im RAM behalten und vergleichen.
> Das die ISR mitten im Program aufgerufen wird, ist nahezu unmöglich, da
> es nach paar 100ms abgearbeitet sein soll und alles in den Tiefschlaf
> geht, die ISR aber nur alle 10sec kommt. Selbst wenn sie mal ausversehen
> rein haut, stört es nicht, da wie gesagt alles neu initialisiert
> wird/werden muss (bis auf die RAM Adresse) und somit nix passieren kann.

Wenn das alles so ist, wie du schreibst, kannst du auch einfach einen 
ganz normalen Reset auslösen. Der faßt den RAM-Inhalt nicht an...

Sprich: Du hast da wohl gerade den Watchdog neu erfunden. Den gibt's 
aber schon lange...

von Magic S. (magic_smoke)


Lesenswert?

> Klar, einfach den Stack neu initialisieren. **headslap**
Dann hast Du einen ganz normalen Reset. :D

von Rolf M. (rmagnus)


Lesenswert?

Stefan schrieb:
> Es geht im Endeffekt darum, dass ein Programm alle paar Sekunden genau
> die gleiche Tätigkeit machen soll. Nur soll es vom vorherigen mal ein
> paar Werte im RAM behalten und vergleichen.
> Das die ISR mitten im Program aufgerufen wird, ist nahezu unmöglich, da
> es nach paar 100ms abgearbeitet sein soll und alles in den Tiefschlaf
> geht, die ISR aber nur alle 10sec kommt. Selbst wenn sie mal ausversehen
> rein haut, stört es nicht, da wie gesagt alles neu initialisiert
> wird/werden muss (bis auf die RAM Adresse) und somit nix passieren kann.

Und warum kann dein Programm das nicht einfach in einer Schleife tun, 
ganz ohne fiese Sprünge "quer" aus der ISR raus?

von Georg (Gast)


Lesenswert?

Hallo,

die einzig sichere Methode eine ISR zu benden ist mit dem jeweiligen 
Return from Interrupt-Befehl. Neben dem bereits gesagten, dass der 
Zustand beim Auftreten des Interrupts dem Programmierer völlig unbekannt 
ist, bestehen je nach Prozessor auch möglicherweise 
Hardware-Abhängigkeiten: manche Prozessoren setzen z.B. mit dem 
RETI-Befehl u.a. die IRQ-Prioritäts-Chain zurück, ohne RETI passiert das 
nicht. Ob ein einfaches SEI den ordentlichen Rückkehrbefehl ersetzen 
kann ist fraglich, schon deshalb, weil bei vielen Systemen nicht nur der 
allgemeine Interrupt wieder eingeschaltet werden muss, sondern auch die 
auslösende Peripherie muss erfahren, dass die Bearbeitung IHRES IRQs 
fertig ist. Man kann sich um vieles herumtricksen, aber so einfach mit 
SEI wie behauptet ist es nicht.

Man kann den Stack aufräumen, wenn man weiss wie, aber nur wenn das 
Hauptprogramm wie vorgesehen weiter ausgeführt wird. Springt man 
irgendwo anders hin, besteht keine Möglichkeit den Stack korrekt 
zurückzusetzen - ausser man startet komplett von vorn mit allen 
Initialisierungen. Dann kann man sich aber den ganzen Aufwand sparen und 
gleich einen Reset oder JMP 0 ausführen.

Georg

von Peter D. (peda)


Lesenswert?

Stefan schrieb:
> Es geht im Endeffekt darum, dass ein Programm alle paar Sekunden genau
> die gleiche Tätigkeit machen soll.

Dann setzt der Timerinterrupt einfach ein Bit und das Main testet in 
einer Schleife das Bit.
Falls es wirklich nur diesen einen Interrupt gibt, wäre auch ein Sleep 
an der Stelle möglich, d.h. das Main geht in Idle.

von Peter D. (peda)


Lesenswert?

Daß vom Sprung aus Interrupts abgeraten wird, liegt nicht daran, daß es 
grundsätzlich nicht geht, sondern daß man sich damit eine Zeitbombe ins 
Programm einbaut, die bei größer werdendem Programm irgendwann 
explodiert.
Und dann heißt es, alles wegschmeißen und nochmal von vorne 
programmieren.

von Michael U. (amiga)


Lesenswert?

man kann sowas in ASM schon machen, nur kaschiert man damit meist andere 
Fehler.
Wer löst denn den zugehörigen IRQ aus?
Es muß ja schon Fehler im Programm geben, sonst können Module ja nicht 
in einen undefinierten Zustand gelangen.

Natürlich kann z.B. ein externes Modul abstürzen, nur dann initialisiere 
ich genau dieses neu und weiß zu dieser Zeit auch genau, welche Daten im 
Ram ich da behalten will und auch kann.

Gruß aus Berlin
Michael

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Stefan schrieb:
> ich möchte, dass ein Interrupt keine eigentliche ISR ausführt, sondern
> an eine gewisse Stelle im Hauptprogram springt (so eine Art Teilreset).

 Es ist mir zwar nicht ganz klar was du damit überhaupt bezwecken
 willst, aber anstatt von einer ISR zur irgendeiner Routine zu
 springen, könntest du alles in der zuerst aufgerufenen ISR machen.

Stefan schrieb:
> ISR_SMALL_RESET:
>   POP  ;Stack Pointer "säubern"
>   POP
>   RJMP SMALL_RESET

 wenn schon, dann so:
1
ISR_SMALL_RESET:
2
 PUSH <SMALL_RESET Adress>
3
 RETI
4
5
SMALL_RESET:
6
  ;Reset der Module
7
  SEI  ;Globale Inerrupts nach Reset und Small Reset erlauben
8
  RET    ;* Und jetzt zuruck

 Aber ich wurde wirklich gerne wissen, wozu das Ganze ?

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.