Forum: Mikrocontroller und Digitale Elektronik MSP430 aus Interrupt springen Hilfe!


von Markus (Gast)


Lesenswert?

Servus,

Ich sitze gerade an einem größeren Projekt für den MSP430F169 und bin 
auf ein Problem gestoßen:

Ich muss (Und ich hab mir das echt lange überlegt; Polling in main ist 
nicht drin!) aus einer Port-Interrupt-Funktion an eine bestimmte Stelle 
in meinem Main-Programm springen.

Ich habe das jetzt über einen Inline-Asembler-Code gelöst, welcher den 
PC manipuliert, um so gezielt die entsprechende Zeile erreichen zu 
können.

Das Verfahren klappt auch perfekt. Aber leider nur ein einziges mal!!!
Danach wird der Interrupt gar nicht mehr ausgelöst, auch wenn der IE 
noch aktiv ist und ich alle Flags vorher gelöscht habe.

Da ich Assembler nie gelernt habe: Weiß jemand, woher das kommen könnte? 
Hängt das mit meiner frechen Manipulation der Core-Register zusammen?

Für eine Antwort wäre ich echt dankbar!!!

Markus

von (prx) A. K. (prx)


Lesenswert?

Markus schrieb:

> aus einer Port-Interrupt-Funktion an eine bestimmte Stelle
> in meinem Main-Programm springen.

Böser Fauxpas. Auch dann, wenn man von Assembler Ahnung hat. So 
programmiert man nicht.

von Markus (Gast)


Lesenswert?

Das habe ich mir fast schon gedacht, danke!!!

Aber gibt es auch eine andere Lösung für dieses Problem?

Gruß,

Markus

von (prx) A. K. (prx)


Lesenswert?

In Interrupt-Handlern wird irgendein Status gesetzt, der in der Mainloop 
abgefragt wird.

von Markus (Gast)


Lesenswert?

Polling/Abfragen von Status-Bits ist leider nicht möglich :(

Deshalb bin ich ja auf diese verquere Lösung gekommen!

Der Interrupt soll zu einem "kontrollierten Reset" führen und eine 
Stelle in main anfahren ohne einen wirklichen Hardware-Rest zu benutzen, 
das Variablen erhalten bleiben müssen!

von (prx) A. K. (prx)


Lesenswert?

Das was du vorhast, setzt beinahe voraus, dass du Compiler und Library 
selbst geschrieben hast. Weil du ganz genau wissen musst, was beide in 
allen Lebenslagen tun.

Da dies offensichlich nicht der Fall ist, kann ich nur die dringende 
Empfehlung abgeben, dieses Konzept komplett in die Tonne zu kippen.

Wenn bestimmte Variablen einen Reset überleben sollen, dann gibt es 
evtl. Möglichkeiten, sie entsprechend zu deklarieren oder den sie 
initialisierenden Startup-Code entsprechend zu modifizieren/ersetzen.

Es gibt zwar mit setjmp/longjmp antike Mechanismen für non-local gotos, 
aber wenn man das in einem Interrupt-Handler macht, dann drohen trotzdem 
einige Überrschungen.

von Christian R. (supachris)


Lesenswert?

Stellt sich eher die Frage, wieso Polling nicht möglich ist? Musst halt 
entsprechend oft pollen, quasi nach jeder Funktion in der Main-Schleife 
das Bit, was die ISR setzt, überprüfen.

von Markus (Gast)


Lesenswert?

Polling ist extrem schwierig bis unmöglich, da der Sprung zurück an eine 
bestimmt Main-zeile aus mehreren Funktionen in mehreren C-Modulen 
(Dateien) kommen können muss! Deshalb ja der gescheiterte Versuch mit 
dem Inline-Assembler. Ich wollte mir tausende von if(polling) Abfragen 
ersparen, zumal die Applikation sehr Timing-sensitiv ist!

Das ist alles sehr unbefriediegend!

Aber trotzdem schonmal allen danke für die Antworten!

Und falls es noch Ideen gibt: Her damit!!!

von mal_so (Gast)


Lesenswert?

Kannst Du deine Reset-Routine 'diese Stelle in Main' nicht als Funktion 
/ Unterprogramm ausführen welches von Main und auch deiner 
Interruptroutine angesprungen wird ?

von Markus (Gast)


Lesenswert?

Gute Idee!

Aber das Problem ist nicht, dass ich eine spezifische Operation, welche 
schon mal in main vorgekommen ist, nochmal ausführen will, sondern dass 
das ganze Programm nochmal von vorne starten soll (Halt nur ohne das 
Setup). Und dieser Sprung muss halt IMMER und von ÜBERALL möglich sein, 
weil in meiner Applikation ein Reset IMMER und ÜBERALL kommen kann und 
das Programm SOFORT reagieren können muss und nicht erst beim nächsten 
Polling.

Dumme Sache, was?

Ich bin weiterhin für alles offen! Super Hilfsbereitschaft!!!!!!

von mal_so (Gast)


Lesenswert?

Dann setz doch in Main alle Register (ausser PC) so wie sie beim 
Hard-Reset /Power-on gesetzt werden (Sprungmarke main_2 für die 
Interruptroutine). Davor main_1 welche beim Power_on angesprungen wird - 
zur Initialisierung...

von Markus (Gast)


Lesenswert?

Klingt gut!
Hab ich aber nicht verstanden!

Wäre ein Anschauungsbeispeil möglich???

(Fehlerhaftes) Prinzip im Moment:

Variablen.....

main()
{
  Initialisierung........
  Assembler-Code (PC speichern)
  Programm.......
}

Interrupt-Funktion
{
   Assembler-Code (Ersetzte PC durch gespeicherten Wert)
}

Ergebniss: Murks!

von spess53 (Gast)


Lesenswert?

Hi

Ergebniss: Murks!

Du hast eigentlich die ganze zeit um den heißen Brei herumgeredet z.B. 
Warum muss man aus jeder Programmstelle an eine bestimmte andere 
springen?

Ausser deiner Meinung, das es nicht anders geht, hast du noch keinen 
triftigen Grund genannt.

>Ergebniss: Murks!

eher: Konzept Murks.

MfG Spess

von Markus (Gast)


Lesenswert?

Tschuldigung, ich wollte nicht gleich mit allen Details nerven!
Habs ja nur einfach halten wollen!

Mein Prozessor soll zum 1-Wire-Slave werden und eine Kommunikation mit 
anderen 1-Wire Devices ermöglichen. Da diese Schnittstelle rein auf 
Timing basiert und jede Flanke damit entscheidend ist, muss ich 
Interrupt-Funktion für jeden Befehl nutzen können. Dies bedeutet auch, 
dass mal auf einen Interrupt gewartet werden muss, zb, wenn mehr als ein 
bit erwartet wird!
Nun wollte ich vermeiden, dass sich das Programm beim Verlust eines Bits 
oder einer Flanke (Aus welchem grund auch immer) aufhängt. Also: Es muss 
immer ein Reset möglich sein, ganz egal, wo das Programm gerade ist, ob 
in main oder sonst wo!

Zufrieden?

Wenn ja, ich bin weiterhin am Suchen für eine Lösung!

MfG,

Markus

von Christian R. (supachris)


Lesenswert?

Naja, du müsstest den StackPointer auch wieder zurück stellen, denn beim 
Srpung in die ISR wird der PC und das SR auf den Stack gerettet. Dann 
wird sicherlich der C-Compiler auch noch einige Pushs eingebaut haben, 
um seine Register zu retten. Das alles musst du wieder per pop 
zurückholen. Außerdem musst du alles das abhandeln, was ein RETI 
eigentlich macht. Flags löschen, GIE wieder einschalten usw. damit es 
wirklich so ist, als wärest du aus der ISR wieder heraus gesprungen. 
Dann könnte es klappen, obwohl es eine wirkliche Schweinerei ist die 
ganze Sache. Du wolltest das Konzept nochmal überdenken, es geht 
garantiert auch ohne diese Kankheit.

von Markus (Gast)


Lesenswert?

OK, mir ist klar geworden, dass es so nicht gehen kann!
Aber anscheinend gibt es auch keine bessere Lösung in diesem einfachen 
Sinne.
Vielleicht mache ich doch einen Totalen Hardware-Reset und speichere mir 
vorher die wichtigen Variablen im Flash!

Schade aber auch! Eine schnelle und korrekte Lösung für mein Problem 
scheint es echt nicht zu geben!

Trotzdem allen Vielen Dank! Falls jemandem doch noch was einfällt, wäre 
ich überaus dankbar, wenn er/sie es mir noch mitteilen könnte!

Gruß,

Markus

von Jörg S. (joerg-s)


Lesenswert?

Hab's mir jetzt nicht im detail durchgelesen, aber wenn es darum geht 
nach dem Reset einen Code-Teil zu überspringen, müsste das doch mit dem 
Watchdog möglich sein. Wenn ich mich recht erinnere kann man doch 
feststellen ob der Reset durch Power on oder Watchdog ausgelöst wurde.
Also muss man beim booten nur schauen ob's ein Watchdog war und wenn ja 
den Code Teil überspringen den er nicht ausführen soll.

von Christian R. (supachris)


Lesenswert?

Naja, ihm gehts ja aber drum, alle Variablen-Inhalte zu belassen. Dazu 
müsste er die StartUp Codes ändern, die ja alle Variablen 
initialisieren.

von Flo (Gast)


Lesenswert?

1-wire-Slave würd ich eher mit Timerinterrupts programmieren als über 
irgendwelche Flankenerkennungen, da du ja 0 und 1 nur über 
unterschiedlich lange Low-Phasen codierst, kann man ja im Timerinterrupt 
den Pinstatus mitzählen, von mir aus 4 mal low = 0 und 10 mal low = 1.
dann die Zeitlich außeinanderliegenden Abtastwerte in ein Datenwort 
zusammenschieben und auswerten.

von Nobody (Gast)


Lesenswert?

Markus schrieb:
> Nun wollte ich vermeiden, dass sich das Programm beim Verlust eines Bits
> oder einer Flanke (Aus welchem grund auch immer) aufhängt.

Sowas geschieht ja nicht grundlos und falls man den Grund nicht als 
irrelevant betrachtet, hat das den Vorteil das man ihn kennt und 
vermeiden kann.
Gefühlsmäßig beruht die Fragestellung des TO auf der Unsicherheit 
darüber welche Gründe es für das übersehen von Flanken gibt. Das sollte 
man zuerst angehen und erst wenn man die Gründe kennt eine Abhilfe 
überlegen.
Es ist IMHO besser die Ursachen anzugehen und nicht die Symptome.

von Peter D. (peda)


Lesenswert?

A. K. schrieb:
> Es gibt zwar mit setjmp/longjmp antike Mechanismen für non-local gotos,
> aber wenn man das in einem Interrupt-Handler macht, dann drohen trotzdem
> einige Überrschungen.

Schau in der Compiler-Doku oder im "setjmp.h" nach.
Z.B. beim AVR-GCC geht es.


Eine andere Möglichkeit ist, den Main-Code als Statemachine 
(Switch/Case) aufzubauen, statt als ellenlangen Spaghetticode.
Dann brauchst Du immer nur zu Beginn des Switch das Flag abtesten.
Oder der Interrupt setzt Deine Statevariable ganz brutal auf 0 und schon 
geht alles automatisch von vorne los.
Es wird maximal noch der gerade laufende State beendet.


Peter

von Peter D. (peda)


Lesenswert?

Hier mal ein Beispiel für eine Statemachine für 1-Wire Master im 
Hintergrund per Timerinterrupt auf nem 8051:

State 20, 19: Reset
State 18 .. 11: sende Byte
State 8 .. 1: lese Byte
State 0: fertig

onwi_write: Anzahl der zu sendenden Bytes
onwi_read: Anzahl der zu lesenden Bytes
1
void t0_handler( void ) interrupt INT_T0
2
{
3
  ONWI_IO = 1;                          // pin = 1
4
5
  if( --onwi_state >= ONWI_START )
6
    return;                             // used as start delay counter
7
8
  TR0 = 0;
9
  TL0 = LOAD60us;
10
  TH0 = LOAD60us >> 8;                  // 60µs = 200 cycle
11
12
  switch( onwi_state ){
13
14
    case 20:                            // 480us low
15
      ONWI_IO = 0;                      // pin = 0
16
17
    case 19:                            // 480us high
18
      TL0 = LOAD480us;
19
      TH0 = LOAD480us >> 8;
20
      break;
21
22
    case 10:                            // 1. command byte finished
23
    case 18:
24
      if( onwi_write ){
25
        onwi_write--;
26
        onwi_state = 18;                // write bit 0
27
28
    case 17:                            // write bit 1
29
    case 16:
30
    case 15:
31
    case 14:
32
    case 13:
33
    case 12:
34
    case 11:                            // write bit 7
35
        ONWI_IO = 0;                    // pin = 0
36
        delay();
37
        if( onwi_buff[onwi_write] & 1 )
38
          ONWI_IO = 1;                  // pin = 1
39
        onwi_buff[onwi_write] >>= 1;
40
        break;
41
      }
42
    case 0:
43
      if( onwi_read == 0 ){             // all reading done
44
45
    default:
46
        return;                         // leave TR0 = 0: one wire finished
47
      }
48
      onwi_read--;
49
      onwi_state = 8;                   // read bit 0
50
51
    case 7:                             // read bit 1
52
    case 6:
53
    case 5:
54
    case 4:
55
    case 3:
56
    case 2:
57
    case 1:                             // read bit 7
58
      ONWI_IO = 0;
59
      delay();
60
      ONWI_IO = 1;                      // 6 cycle = 1.8µs
61
      onwi_buff[onwi_read] >>= 1;
62
      if( ONWI_IO )                     // 18 cyle = 5.7µs
63
        onwi_buff[onwi_read] |= 0x80;
64
  }
65
  TR0 = 1;
66
}

Peter

von Markus (Gast)


Lesenswert?

Flo schrieb:
> 1-wire-Slave würd ich eher mit Timerinterrupts programmieren als über
> irgendwelche Flankenerkennungen, da du ja 0 und 1 nur über
> unterschiedlich lange Low-Phasen codierst, kann man ja im Timerinterrupt
> den Pinstatus mitzählen, von mir aus 4 mal low = 0 und 10 mal low = 1.

Schon klar, ich zähle ja auch Zeiten und keine Flanken (Nur nicht über 
Timer, sondern über Delay-Cycles, die sind genauer). Nur für den Start, 
also das loslassen der Timer, brauche ich die Flankendetektierung. (Und 
das bei jedem Bit auf neue!)

von Markus (Gast)


Lesenswert?

Nobody schrieb:
> Gefühlsmäßig beruht die Fragestellung des TO auf der Unsicherheit
> darüber welche Gründe es für das übersehen von Flanken gibt. Das sollte
> man zuerst angehen und erst wenn man die Gründe kennt eine Abhilfe
> überlegen.

Es gibt dafür bei mir nur einen Grund: Hardware zu langsam.
Alles andere habe ich beseitigt!

Den Fehler kann ich im Moment nicht beheben, da Übertakten und Austausch 
im Moment nicht in Frage kommen.

Aber grundsätzlich stimme ich dem Statement voll und ganz zu!!!!!

von Markus (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Hier mal ein Beispiel für eine Statemachine für 1-Wire Master im
> Hintergrund per Timerinterrupt auf nem 8051:
>
> State 20, 19: Reset
> State 18 .. 11: sende Byte
> State 8 .. 1: lese Byte
> State 0: fertig

Guuuuuut! Vielen Dank für den Hinweis!

Allerdings ist die sache nicht ganz so einfach:
Weiterhin gilt: Reset muss immer möglich sein und der Slave-Code 
beschränkt sich nicht nur aufs andauernde lesen und schreiben, sodass 
durchaus mal Flanken verloren gehen können (Siehe: Hardware zu langsam) 
Das ist bei mir aber echt nur die Ausnahme. Die Sicherheit in Sachen 
aufhängen muss halt nur immer gegeben sein. Und falls mal eine erste (!) 
Flanke verloren geht, ist halt ohne möglichen Reset Ende!

Aber ich werde die Sache mit der Satemachine weiter verfolgen! 
vielleicht lässt sich das verwerten! Danke!

von ralf (Gast)


Lesenswert?

Eigentlich brauchst du nur im IRQ den den SP neu zu initialisieren, GIE 
setzen und ohne reti direkt zu der Main-zeile springen.

von Markus (Gast)


Lesenswert?

Und wie in die entsprechende Main-Zeile springen?
Da liegt ja mein Problem!

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.