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
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.
Das habe ich mir fast schon gedacht, danke!!! Aber gibt es auch eine andere Lösung für dieses Problem? Gruß, Markus
In Interrupt-Handlern wird irgendein Status gesetzt, der in der Mainloop abgefragt wird.
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!
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.
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.
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!!!
Kannst Du deine Reset-Routine 'diese Stelle in Main' nicht als Funktion / Unterprogramm ausführen welches von Main und auch deiner Interruptroutine angesprungen wird ?
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!!!!!!
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...
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!
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
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
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.
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
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.
Naja, ihm gehts ja aber drum, alle Variablen-Inhalte zu belassen. Dazu müsste er die StartUp Codes ändern, die ja alle Variablen initialisieren.
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.
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.
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
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
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!)
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!!!!!
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!
Eigentlich brauchst du nur im IRQ den den SP neu zu initialisieren, GIE setzen und ohne reti direkt zu der Main-zeile springen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.