Hallo,
Ich hab immer wieder mal ein eigenartiges Verhalten meines Programmes,
das auf einem MSP430G2553 laeuft, kompiliert mit msp-gcc. Da ich es
schwer erklaeren kann, hab ich mal ein Video von der Ausgabe via UART
auf der Konsole gemacht.
Das video (739KB) gibts hier:
http://www.airmax.at/uploads/msp430_hangup.avi
So wies ausschaut wird da mein Stackpointer "zerschossen" (willkuerliche
Vermuting), auf jeden Fall startet die Mainroutine immer wieder neu,
oder so. Ich denke mal es hat was mit sprintf und senden via UART zu
tun, bin mir aber nicht sicher ... hat von euch jemand eine Idee?
Was fuehrt generell zu so einem Verhalten?
Kann ich das mit debuggen herausfinden?
So schaut mein main.c aus:
1
intmain(void){
2
WDTCTL=WDTPW+WDTHOLD;// Stop WDT
3
4
BCSCTL1=CALBC1_1MHZ;// Set "Basic clock system control 1" to 1MHz
5
DCOCTL=CALDCO_1MHZ;// Set "DCO clock frequency control " to 1MHz
Ich hab jetzt sprintf rausgeschmissen und mir nach Vorlage von
http://www.msp430launchpad.com/2012/06/using-printf.html
ein uart_printf gebaut. Kann man das in einer ISR verwenden oder sollte
man darin gar nichts via UART senden?
Markus Manninger schrieb:> So wies ausschaut wird da mein Stackpointer "zerschossen" (willkuerliche> Vermuting), auf jeden Fall startet die Mainroutine immer wieder neu,> oder so.
Ja, denn wo ist die Endlosschleife in "main"? Du verlässt ja "main" mit
"return 0;" und landest im Nirvana. Oder ist da noch was versteckt in
einer Funktion, was man so nicht sieht?
Gruß Dietrich
Mit "_BIS_SR(LPM0_bits + GIE);" leg ich den MC schlafen ... der Rest
wird zyklisch in der Timer_A Routine erledigt. Das sollte so schon
passen.
lG,
Max
Markus Manninger schrieb:> Mit "_BIS_SR(LPM0_bits + GIE);" leg ich den MC schlafen ... der Rest> wird zyklisch in der Timer_A Routine erledigt. Das sollte so schon> passen.
Aber er muss ja irgendwann wieder aufwachen, sonst tut er ja nie was.
Und wenn er aufwacht und die Timer_A Routine ausführt, dann ist er beim
Verlassen der Routine immer noch wach und macht bei "main" weiter. Oder
ist die Endlosschleife in "_BIS_SR(LPM0_bits + GIE);" und wird dort
erneut schlafen gelegt?
Gruß Dietrich
Markus Manninger schrieb:> Das sollte so schon passen.
Offensichtlich nicht...
Du legst ihn zwar schlafen, aber was passiert, wenn er durch den
Interrupt wieder aufgeweckt wird?
Pack doch einfach mal ein while(1) um Dein "_BIS_SR(LPM0_bits + GIE);".
Das Timer wird continuierlich ausgefuehrt. Die ISR wird also immer
wieder aufgerufen, waehrend die main angehalten ist.
Sowas funktioniert ja auch problemlos:
Markus Manninger schrieb:> Das Timer wird continuierlich ausgefuehrt. Die ISR wird also immer> wieder aufgerufen, waehrend die main angehalten ist.
Hast Du denn wenigstens mal ausprobiert, eine Endlosschleife vor das
"return 0;" zu schreiben? Das kostet nichts.
Außerdem: es wird nicht "main" angehalten, sondern der Prozessor. Und
wenn er durch den Interrupt wieder startet, läuft auch "main" weiter,
wenn nach Ende der ISR und erneutem Aufruf doch eine Lücke sein sollte.
Was für ein Interrupt ist das denn überhaupt?
> Sowas funktioniert ja auch problemlos:
Auch wenn ein Programm anscheinend funktioniert, muss es nicht
fehlerfrei sein. Ob der Fehler sichtbar wird hängt u.U. noch von vielen
anderen Bedingungen ab.
Gruß Dietrich
Dietrich L. schrieb:> Außerdem: es wird nicht "main" angehalten, sondern der Prozessor. Und> wenn er durch den Interrupt wieder startet, läuft auch "main" weiter,
Nö, solange kein Exit LPM in der ISR steht, schläft er weiter.
Ich dachte mir aber dass es ja kein Problem sein sollte das in der ISR
zu machen, solange die Abarbeitung nicht laenger dauert wie der naechste
Interrupt auftritt. In der ISR funktionierts auch mit der uart_pintf
nicht.
Da die endgueltige Schaltung mit Solar betrieben werden soll, will/muss
ich Strom sparen. Wie kann ich denn am besten jede Sekunde eine Messung
durchfuehren ohne in einer while schleife zu loopen?
Danke,
Max
Was aber funktioniert, ist den Timer via "TACCTL0 &= ~CCIE;" in der ISR
zu deaktivieren, Sensor auslesen und wenn alles erledigt ist, den Timer
via "TACCTL0 = CCIE;" wieder scharf zu schalten.
Sollte man das so machen?
@Marc N.:
Es funktioniert auch mit einem while(1) in der main nicht!
Ich bin sicher kein Besserwisser, ganz im Gegenteil. Aber ich will Dinge
verstehen und haette gerne eine Erklaerung weshalb was funktioniert bzw.
nicht funktioniert.
Markus Manninger schrieb:> Ich dachte mir aber dass es ja kein Problem sein sollte das in der ISR> zu machen, solange die Abarbeitung nicht laenger dauert wie der naechste> Interrupt auftritt.
Selbst wenn es länger dauert, sollte der µC trotzdem nicht abstürzen.
>Was aber funktioniert, ist den Timer via "TACCTL0 &= ~CCIE;" in der ISR>zu deaktivieren, Sensor auslesen und wenn alles erledigt ist, den Timer>via "TACCTL0 = CCIE;" wieder scharf zu schalten.
Das deutet natürlich auf ein Timing Problem hin. Timer kommt bevor
letzte ISR abgeschlossen ist. Allerdings sollte das die bestehende ISR
nicht kümmern .
Was ich mir vorstellen könnte: Die UART Routine soll das letzte Byte
ausgeben, das wird in den Buffer übertragen, die UART Routine wartet
aber nicht bis es fertig übertragen worden ist und kehrt in die ISR
zurück die gleich wieder in die main und somit in den LPM springt und
die Ausgabe des letzten Bytes abwürgt.
Hast du eigentlich kein Debugger? Dann kannst du dir doch wunderschön
ansehen was passiert.
Was sollen die Zeilen:
TACCR0 += 4800;
und
((timer_counter%40) == 0 )
bewirken?
Markus Manninger schrieb:> Was aber funktioniert, ist den Timer via "TACCTL0 &= ~CCIE;" in der ISR> zu deaktivieren, Sensor auslesen und wenn alles erledigt ist, den Timer> via "TACCTL0 = CCIE;" wieder scharf zu schalten.Markus Manninger schrieb:> Ich dachte mir aber dass es ja kein Problem sein sollte das in der ISR> zu machen, solange die Abarbeitung nicht laenger dauert wie der naechste> Interrupt auftritt.
Bist Du Dir sicher, dass dem so ist?
Bist Du Dir wirkich sicher, dass Dein Prozessor nicht doch aufwacht, an
das Ende der Main kommt und seltsame Sachen macht (z.B. einen Reset oder
den PC komplett durchzählen, irgendwelchen Müll dabei machen und dann
wieder von vorne anfangen...)
Jörg S. schrieb:> Was ich mir vorstellen könnte: Die UART Routine soll das letzte Byte> ausgeben, das wird in den Buffer übertragen, die UART Routine wartet> aber nicht bis es fertig übertragen worden ist und kehrt in die ISR> zurück die gleich wieder in die main und somit in den LPM springt und> die Ausgabe des letzten Bytes abwürgt.>> Hast du eigentlich kein Debugger? Dann kannst du dir doch wunderschön> ansehen was passiert.
Davor hab ich mich bis jetzt gedrueckt ... werd mich aber mit dem
mspdebug wohl beschaeftigen muessen.
> Was sollen die Zeilen:> TACCR0 += 4800;
Ich befuerchte das ist eh um sonst, da der Timer immer bis FFFFh zaehlt
und dann feuert, da ich den MC_2 mode benutze.
> ((timer_counter%40) == 0 )
Naja, damit fuehre ich den Code innerhalb der if Abfrage nur jeden 40ten
ISR Aufruf aus. Auch nicht sehr schoen, ich weiss. Aber ich bekomm mit
1MHz Clock keine vernueftige Sekunde zusammen. Werd aber in Zukunft eh
mit 32kHz takten.
Gibts eigentlich eine Moeglichkeit zu zaehlen wieviele CPU cycles
zwischen zwei Iterationen (Codestellen) vergangen sind?
Ich hab das printf via UART von
http://www.msp430launchpad.com/2012/06/using-printf.html
übernommen. Denke dass sprintf auf einem MC nicht so gut ist, mit dem
uart_printf funktionierts recht gut ... aber das Hauptproblem war, dass
ich innerhalb der Timer ISR den Timer nicht angehalten hab. Seitdem ich
das mache, funktioniert auch alles soweit.
So, jetzt hab ichs glaub ich recht sauber umgesetzt. In der while() leg
ich den MC schlafen und setz vorher den Timer, der den MC wieder
aufweckt. Simple und wie von TI empfohlen
1
intmain(void){
2
WDTCTL=WDTPW+WDTHOLD;// Stop WDT
3
4
BCSCTL1=CALBC1_1MHZ;// Set "Basic clock system control 1" to 1MHz
5
DCOCTL=CALDCO_1MHZ;// Set "DCO clock frequency control " to 1MHz
Mich wunderst nur dass mein LED nicht "genau" im 1 Sekunden Takt blinkt.
Bei 1MHz Tankt, 1/8 Teiler (ID_3), Up- Down Mode (MC_3) und TACCR0 =
62500 muesste ich da doch einigermassen hinkommen. 62500 x 8 x 2 =
1000000, oder was versteh ich da falsch?
Und gibts keine Moeglichkeit zwischen zwei Durchlauefen in der while
Schleife die Anzahl der CPU Ticks auszulesen?
Die Aktualisierung des Timers muss in der ISR erfolgen.
Vergiss vorerst die Sache mit dem Low Power Mode. Das kannst du später
noch nachholen.
Dietrich L. schrieb:> Und wenn er aufwacht und die Timer_A Routine ausführt, dann ist er beim> Verlassen der Routine immer noch wach und macht bei "main" weiter.
Nein, der Power Mode steht im PSW und wird mit dem RETI restauriert. Es
wird kein weiterer Befehl im Hauptprogramm ausgeführt. Man findet viele
Programmbeispiele für den MSP ohne Endlosschleife.
Markus Manninger schrieb:> Mich wunderst nur dass mein LED nicht "genau" im 1 Sekunden Takt blinkt.
Wie blinkt er denn?
> Und gibts keine Moeglichkeit zwischen zwei Durchlauefen in der while> Schleife die Anzahl der CPU Ticks auszulesen?
Nein. Dafür kannst du Timer nutzen ;)
Jörg S. schrieb:> Markus Manninger schrieb:>> Mich wunderst nur dass mein LED nicht "genau" im 1 Sekunden Takt blinkt.> Wie blinkt er denn?
Naja, nicht ganz genau eine Sekunde. Das passt aber schon.
>> Und gibts keine Moeglichkeit zwischen zwei Durchlauefen in der while>> Schleife die Anzahl der CPU Ticks auszulesen?> Nein. Dafür kannst du Timer nutzen ;)
Schade, das waere sehr praktisch.