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;
}
}
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
volatileintBeispiel;
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)!
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 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...???
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?
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)?
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)
>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 *