Hallo Zusammen, ich möchte eine Wartezeit programmieren, damit sich die Hardware erstmal beruhigen kann (nicht der MSP430 selber aber was daran hängt). Hierfür wollte ich den Timer B nutzen, dieser hängt der über XT2 mit 8 Mhz Quartz und Divider 8 auf SMCLK und Divder 8 auf den Timer B initialisiert wurde. In wait() will ich den TimerB starten und im Interrupt von TimerB wird das Flag Ready auf 1 gesetzt und dies beendet die while Schleife in wait(). Zumindest war dies mein Plan. Das ganze bleibt aber in der while Schleife hängen, und der Interrupt von TimerB wird nie aufgerufen (mit Breakpoint überwacht). Ich habe mir in der while Schleife den Counter TBR anzeigen lassen, der wird brav immer wieder hochgezählt, aber der Interrupt wird nie ausgelöst. Kann mir einer sagen was ich falsch mache? Wenn ich keine while Schleife nutze wird irgendwann der Interrupt ausgelöst. Bin für jede Hilfe dankbar. Grüße Jens Auszug aus main: WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer BCSCTL1 = 0; _BIC_SR(OSCOFF); // Schaltet LFXT1 aus im Statusregister do // do Schleife wartet bis XT2 stabil ist { IFG1 &=~OFIFG; for(i=0xFF;i>0;i--); } while (IFG1&OFIFG); // diese while Schleife ist nicht gemeint BCSCTL2 = SELM1 + SELS + DIVS0 + DIVS1; // MCLK-Source ist XT2, //SMCLK-Source ist XT2, //Divider MCLK = 1, //Divider SMCLK = 8 ... wait(); ... Subroutinen: // ************************************** // Init Timer B // ************************************** void init_timer_B(void) { TBCCTL0 = CCIE; // CCR0 interrupt enabled stopTimerB(); TBCTL = TBSSEL1 + ID0 + ID1 + MC0; // SMCLK, divide by 8 if (Master == 0) { StatusTimerB = 0; CounterTimerB = TIMERBTAKT; runTimerB(); } else { stopTimerB(); } _BIS_SR(GIE); // General interrupt enable _EINT(); } // ***************************** // Stopt Timer B // ***************************** void stopTimerB(void) { TBCCR0 = 0; TBCTL &= ~MC0; } // ***************************** // Startet Timer B // ***************************** void runTimerB(void) { TBCTL |= MC0; TBCCR0 = 0; TBCCTL0 = CCIE; // CCR0 interrupt enabled TBCCR0 = CounterTimerB; } //******************************* // Wartet bis Timertaktweg //******************************* void wait(void) { Ready = 0; stopTimerB(); StatusTimerB = 3; // Flag für TimerB setzten, signalisiert // Warten bis wieder dran CounterTimerB = TIMERBTAKTWEG; runTimerB(); // TimerB starten while (Ready==0) { for(;;); // bleibt hier hängen // TBR wir aber hochgezählt } } // *************************** // Timer B interrupt // *************************** #pragma vector=TIMERB0_VECTOR __interrupt void Timer_B(void) { ... switch(StatusTimerB) // hier Breakpoint gestezt kommt nie an :( { ... case 3: stopTimerB(); ... Ready = 1; // hier soll Flag für while Schleife geändert werden break; } }
TIMSK wo wird das bei dir gesetzt ? MCUCR ? zeig mal!
sorry ist ja msp. hast du vielleicht vergessen den Interrupt einzuschalten`??
Hi, danke für die Antwort. Eigentlich sollte der Interrupt eingeschaltet sein. Zum einen im Statusregister in main _BIS_SR(GIE); und für den TimerB in der Subroutine in init_Timer_B TBCCTL0 = CCIE; // CCR0 interrupt enabled Wenn ich die while Schleife rausnehme gehts ja auch. Vielleicht verstehe ich etwas falsch. Danke jedenfalls Gruß Jens
Beide werden global deklariert (oberhalb von main) P.S. Mit den genauen Begriffen hakts etwas bei mir. Ich denke volatil bedeut innerhalb der Subroutine, sind sich nicht. Falls es was anderes bedeutet bräuchte ich eine kurze Erläuterung. Danke für die Antwort. Gruss Jens
Globale Variablen, die auch in Interrupt-Service-Routinen (ISR) geändert werden können, musst Du explizit als volatile deklarieren:
1 | volatile int Beispiel; |
Ansonsten kann es Dir passieren, dass Dein Compiler den Zugriff auf Deine Variablen derart optimiert, dass nicht mehr das gewünschte Ergebnis rauskommt (Siehe Dein Problem)!
Danke Stefan, ich werde es gleich probieren. Grüße Jens
Hi nochmal, leider löst das mein Problem nicht. Das wird zwar richtig sein was Stefan sagt, aber das Problem ist das der Interrupt von TimerB gar nicht aufgerufen wird, obwohl TBR hochzählt. Er bleibt einfach in der while Schleife hängen. Ist irgendwas generelles falsch? Grüße Jens
Hi ich habs probiert leider bleibt er immer noch hängen. Grüße Jens
OK Was glaubst Du denn, was hier passiert?
1 | while (Ready==0) |
2 | {
|
3 | for(;;); // bleibt hier hängen |
4 | // TBR wir aber hochgezählt
|
5 | }
|
Aus der for-Schleife wirst Du nimmer mehr herauskommen, die hat kein Abbruchkriterium! Du brauchst lediglich das hier:
1 | while(!Ready); |
Hi Stefan, hab's gerade ausprobiert. Gleiches Ergebnis. Der msp bleibt in der while Schleife hängen. Es stimmt zwar das er aus der for Schleife nicht rauskommt, aber vor dem Aufruf der for Schleife habe ich eigentlich den TimerB gestartet und wenn der Interrupt von TimerB aufgerufen wird, wird in der Interrupt Routine Ready auf 1 gesetzt. Das sollte dann die Schleife abbrechen. Nur wird dieser verflixte Interrupt nicht aufgerufen obwohl TBR hochzählt und der IR freigeschaltet ist. Ich glaube ich mache irgendwas grundsätzliches falsch. Mit while(CounterTimerB != TBR) { for(;;); } klappt es jetzt, aber eigentlich ist das unsauber programmiert. Falls Jemand eine Lösung mit Interrupt hat wäre ich nachwievor dankbar. Danke Stefan auf jeden Fall. Grüße Jens
>Es stimmt zwar das er aus der for Schleife nicht rauskommt, aber vor dem >Aufruf der for Schleife habe ich eigentlich den TimerB gestartet und >wenn der Interrupt von TimerB aufgerufen wird, wird in der Interrupt >Routine Ready auf 1 gesetzt. Das sollte dann die Schleife abbrechen. Dann hast Du wohl Glück, dass Dein Compiler die Nutzlosigkeit der for-Schleife erkannt und diese weg-optimiert hat. Denn wenn Du einmal in der for-Schleife sitzt, dann kommst Du nie mehr zur Abfrage des Abbruchkriteriums in der while-Schleife!!! So aus purer Verzweiflung... mal die Errata für Deinen MSP angeguckt... TimerB Bug...???
Wo finde ich im IAR Compiler die Errata? Beim Compilieren heißt es null Errors und null Warnings.
Kann es sein, dass du immer wieder ein Interrupt kriegst? Und zwar hast du den Interruptvektor TIMERB0_VECTOR definiert, der ist aber für die Capture and Compare Register des Timermoduls zuständig. Versuche anstatt dessen mal den TIMERB1_VECTOR zu verwenden. Michael
Hi Supa Micha, jetzt habe ich alles auf de TimerB1_Vector umgeschrieben, inklusive TBCCR1 = 0; beim stoppen des Timers Und TBCCR1 = CounterTimerB; und TBCCTL1 = CCIE; beim starten. Gleiches Ergebnis. Initialisiere ich vielleicht falsch?
Sind nicht alle 7 Interrupt gleichermaßen für Capture und Compare zuständig?
Das sind 27 Seiten, was genau brauchst du den davon? Die meisten Subroutinen dürften von keinem besonderen Interesse sein. Grüße Jens
Ok. Dann anders. Lass das Programm laufen und halte es nach geraumer Zeit (z.B. 5 Sekunden) mal an. Dann schau Dir im Debugger mal die SFR's an. Ist das CCIE wirklich gesetzt? Wird der Timer geclocked ? Ist das GIE wirklich gesetzt (SR)?
Hi szimmi , jetzt habe ich noch etwas ausführlicher gelistet. Wie gesagt Ready und StutstimerB sind mittlerweile als volatile global definiert. Der Punkt ist, das der Interrupt für TimerB nicht aufgerufen wird, TBR aber immer wieder hochgezählt wird.??? Nee Idee? //******************************* // Hauptroutine //******************************* void main(void) { float Alpha, Beta; // Argumente von Cosinus und Sinus Werten int wert; // Buffer int NN; WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer shortBreak(10); BCSCTL1 = 0; _BIC_SR(OSCOFF); // Schaltet LFXT1 aus im Statusregister do // do Schleife wartet bis XT2 stabil ist { IFG1 &=~OFIFG; for(i=0xFF;i>0;i--); } while (IFG1&OFIFG); BCSCTL2 = SELM1 + SELS + DIVS0 + DIVS1; ... // ***************************************** // Timer initialisieren // ***************************************** init_timer_A(); init_timer_B(); ... } // Ende Main Loop // ************************************** // Init Timer A // ************************************** void init_timer_A(void) { TACCR0 = TIMERAVALUE; // Load timer compare register TACTL = TASSEL1 + ID0 + ID1 + MC0 + MC1;// SMCLK, divide by 8, up a. down if (Master == 1) { runTimerA(); // Timer A läuft permanet "up and down" // run Timer A enabled nur den Interrupt } _BIS_SR(GIE); // General interrupt enable } // ************************************** // Init Timer B // ************************************** void init_timer_B(void) { TBCCTL0 = CCIE; // CCR0 interrupt enabled stopTimerB(); // Nur Startwert wird später geändert TBCTL = TBSSEL1 + ID0 + ID1 + MC0; // SMCLK, divide by 8, up if (Master == 0) { StatusTimerB = 0; CounterTimerB = TIMERBTAKT; runTimerB(); } else { stopTimerB(); } _BIS_SR(GIE); // General interrupt enable _EINT(); } // ***************************** // Stopt Timer B // ***************************** void stopTimerB(void) { TBCCR0 = 0; TBCTL &= ~MC0; } // ***************************** // Startet Timer B // ***************************** void runTimerB(void) { TBCTL |= MC0; TBCCR0 = 0; TBCCTL0 = CCIE; // CCR0 interrupt enabled TBCCR0 = CounterTimerB; } //****************************************** // Wechselt zwischen Master und Slave //****************************************** void toggle_master_slave(void) { ... wait(); ... } //*************************** // Timer A interrupt //*************************** #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A(void) { ... toggle_master_slave(); ... } //******************************* // Wartet bis Timertaktweg //******************************* void wait(void) { Ready = 0; stopTimerB(); StatusTimerB = 3; // Flag für TimerB setzten CounterTimerB = TIMERBTAKTWEG; runTimerB(); // TimerB starten while (Ready==0) { for(;;); } }
So, was SFR sind weiß ich ehrlich nicht, da ich nicht so firm in der Materie bin. Ich habe aber das Programm mal laufen lassen und mir nach 5 Sekunden den Wert für TBCCTL0 anzeigen lassen. Der ist 17, das interpretiere ich so, dass Bit und Bit 0 und Bit 4 gesetzt sind, das wäre CCIFG und CCIE. CCIE habe ich auch gesetzt. Das müßte bedeuten Interrupt enabled (CCIE) und sogar das er ausgelöst (CCIFG) wurde, aber warum springt er nicht rein. Ohne wihle loop tut er es, leider irgendwo aus dem Program raus. :(
Alles sehr mystisch. Was steht nach den 5s im Register SR? Ist GIE gesetzt? Bitte mal für Testzwecke die ISR wie folgt ändern: // Timer B0 interrupt service routine #pragma vector=TIMERB0_VECTOR __interrupt void Timer_B (void) { P1OUT ^= 0x01; // Toggle P1.0 TBCCR0 += 50000; // Add Offset to CCR0 } Und einen Breakpoint auf die P1OUT-Zeile setzen. Da muss er hin, da kann der Compiler nix wegoptimieren.
Danke sizimi, im Statusregister SR war tatsächlich er GIE zurückgesetzt worden. Beim ersten Durchlauf hat es noch nicht geklappt, da ich die Einstellungen noch auf den #pragma vector=TIMERB1_VECTOR umgeschrieben hatte (s.o.). Wieso im SR aber der GIE zurückgestzt wurde ist mir ein Rätsel. Da ich das SR mit _BIS_SR(GIE) gesetzt hatte, dachte ich alles wäre geritzt. Wodurch wird den der GIE zurückgesetzt? Macht das der msp automatisch nach einem up Durchlauf? Jetzt klappt es jedenfalls, vor den Aufruf runTimerB() habe ich nochmal ein _BIS_SR(GIE) gesetzt jetzt läuft es. Nochmals danke, Grüße Jens
@ Jens (Gast) >was SFR sind weiß ich ehrlich nicht, da ich nicht so firm in der Materie >bin. http://www.mikrocontroller.net/articles/Speicher#Register MFG Falk
Danke Falk, von alleine setzten die sich aber doch nicht zurück, speziell jetzt der SR vom MSP430 oder? Grüße Jens
@ Jens (Gast) >von alleine setzten die sich aber doch nicht zurück, speziell jetzt der >SR vom MSP430 oder? Was heisst zurücksetzten? Zürücksetzen bzw. löschen muss man ggf. nur Interruptflags. Das macht enweder der MSP selber beim ausführen der ISR oder man muss es manuell per Befehl machen. Das SR ist ein zentrales SFR, nämlich das Statur Register der CPU. Das ist quasi heilig. Dort sollte man tunlichst nicht wild rummurksen. Um deinem Probelm auf die Sprünge zu helfen. Poste mal VOLLSTÄNDIGEN Quelltext als ANHANG. MFG Falk
Danke Falk, mein Problem hat sich geklärt. Das mit dem Interrupt klappt jetzt. Wenn ich vorher das GIE setzte verläßt er die while Schleife. Morgen versuche ich noch rauszufinden warum das GIE zurückgesetzt wurde. Jetztt ist erstmal Bett angesagt. Danke allen die mir geholfen haben. Grüße Jens
Das GIE wird vom Prozessor beim Eintritt in die ISR gelöscht. Nach Beendigung der ISR wird es wiederhergestellt. Man kann es in der ISR auch wieder setzen, ist aber tricky, dann werden pending interrupts bedient ohne Rücksicht auf die Priorität. Also besser Finger weg. Ansonsten liegt das GIE in Deiner Hand. Ich nehme also an, dass Du irgendwo das GIE explizit löscht. Wenn Du es durch ein Codereview nicht findest, kannst Du Dich auch schrittweise durch Deine Initialisierung debuggen und schauen, an welcher Stelle das GIE noch gesetzt ist bzw. zurückgenommen wurde. Am besten ist, Du setzt zuerst den Brechpunkt ans Ende Deiner Initialisierung und halbierst dann immer die Zeilenanzahl. So findet man es in der Regel am schnellsten (so ne Art binäre Suche).
Kann mich meinen Vorrednern nur anschließen! Irgendwo in Deinem Programm setzt Du (ungewollt) das GIE zurück. Such mal nach _BIC_SR(GIE) oder nach _DINT() Was mir an Deinem Code nicht gefällt, dass Du sowohl in init_Timer_A() als auch in init_Timer_B() das GIE setzt! Ausserdem setzt du es im letzen Fall gleich zweimal, denn _BIS_SR(GIE) und _EINT() machen das gleiche! Die globale Interruptfreigabe setzt man (im Normalfall) ein einziges Mal im Programm! Deshalb vermute ich, dass sich bei Dir irgendwo ein _DINT() eingeschlichen hat, ohne dass Du's gemerkt hast ?!
Ahhh, jetzt hat es klick gemacht. Ich glaube, ich weiss, was passiert. Das wait rufst Du aus der TimerA-ISR auf. Dort ist natürlich das GEI gelöscht (siehe oben). In der TimerA-ISR wartest Du also darauf, das in der TimerB-ISR das Ready-Flag gesetzt wird. Die TimerB-ISR wird aber solange nicht ausgeführt, solange Du in der TimerA-ISR rumwerkelst. Klassischer Deadlock. Ich würde Dir empfehlen, die Struktur Deines Programms zu überdenken. Nimm Dir einen Timer her, der z.B. die Systemzeit hochzählt und werte diesen im Main für Deine Wartezeit auf. Oder erzeuge eine Timervariable, die Du im Timer dekrementierts und wenn die Variable zu Null wird, dann ist auch Deine Wartezeit zu Ende. Aber mit der aktuellen Struktur wirst Du noch andere Probleme bekommen. Also, am bestene nochmal zurück auf Los (Deine Subroutinen musst Du deswegen nicht neu schreiben).
Noch ein Hinweis (muss mal den Schlaumeier raushängen lassen). While oder Endlos-For-Schleifen habe in einer ISR höchst selten etwas zu suchen. Kurz und knackig ist das Stichwort.
> Noch ein Hinweis (muss mal den Schlaumeier raushängen lassen). While > oder Endlos-For-Schleifen habe in einer ISR höchst selten etwas zu > suchen. Kurz und knackig ist das Stichwort. Vor allem wenn man Energie sparen könnte in dem man einfach schläft. Hier mal meine Version eines schlafenden waits für Timer A (mit 32768 Hz Uhrquarz für ACLK) für einen msp430f169:
1 | #include <msp430x16x.h> |
2 | #include <msp430/common.h> |
3 | #include <signal.h> |
4 | |
5 | |
6 | /* Mit mspgcc sorgt "wakeup" für Aufwachen nach der ISR *
|
7 | * bei anderem Compiler eben von Hand */
|
8 | interrupt (TIMERA0_VECTOR) wakeup timerA0_irq (void) { |
9 | return; |
10 | }
|
11 | |
12 | /* wartet d/32768 Sekunden */
|
13 | int wait(unsigned int d) { |
14 | TACTL = TASSEL0 + TACLR; /* ACLK */ |
15 | CCR0 = d; |
16 | CCTL0 = CCIE; |
17 | CCTL1 = 0; |
18 | CCTL2 = 0; |
19 | TACTL |= MC1; |
20 | LPM0; /* Hier wird geschlafen */ |
21 | TACTL &= ~MC1; |
22 | return 1; |
23 | }
|
24 | |
25 | int main(void) { |
26 | WDTCTL = WDTPW + WDTHOLD; /* watchdog aus */ |
27 | BCSCTL1 |= RSEL0 + RSEL1 + RSEL2; |
28 | DCOCTL |= DCO0 + DCO1 + DCO2; |
29 | |
30 | P1DIR = 0xFF; |
31 | P1OUT = 0x00; |
32 | eint(); |
33 | while(wait32768(33)) { |
34 | P1OUT ^= 0x01; |
35 | }
|
36 | }
|
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.