Eigenartiges Verhalten des MSP430 ( Absturz / Neustart )

OP #3214187
Lesenswert?

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
int main(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
6
    
7
    P1DIR |= BIT0;
8
    P1OUT &= ~BIT0;
9
  
10
    // Initialize Hardware UART
11
    uart_init();
12
    
13
    __enable_interrupt();
14
    
15
    uart_write("\n\r-------------------------- START ------------------------------\n\r");
16
    
17
    ms5611_init();
18
    busy_waiting( 65000 );
19
    
20
    ms5611_read_prom();
21
    busy_waiting( 65000 );
22
    
23
    timer_counter = 0;
24
    
25
    TACCTL0 = CCIE;               // Set Capture/Compare interrupt enable
26
    TACCR0 = 4800;                // Timer a Capture/Compare register
27
    TACTL = TASSEL_2 + MC_2;      // Source = SMCLK (Sub-System master clock)
28
                                  // Mode control = MC_2 (Continuous mode)
29
    
30
    _BIS_SR(LPM0_bits + GIE);     // Enter LPM0 w/ interrupt
31
    
32
    return 0;
33
}
34

35
// Timer A0 interrupt service routine
36
#pragma vector=TIMER0_A0_VECTOR
37
__interrupt void Timer_A (void)
38
{
39
    if( (timer_counter%40) == 0 ) {
40
        sprintf( debug, "pres = %d\r\n", (int)ms5611_get_pressure() );
41
        uart_write(debug);
42
        
43
        P1OUT ^= BIT0;        // Hardbeat signal
44
        TACCR0 += 4800;       // Add Offset to CCR0
45
    }
46
    
47
    timer_counter++;
48
}

lG,
Max
#3214268
Lesenswert?

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
#3214283
Lesenswert?

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
OP #3214344
Lesenswert?

Das Timer wird continuierlich ausgefuehrt. Die ISR wird also immer 
wieder aufgerufen, waehrend die main angehalten ist.

Sowas funktioniert ja auch problemlos:
1
int main(void)
2
{
3
        WDTCTL = WDTPW + WDTHOLD;                       // Stop WDT
4

5
        BCSCTL1 = CALBC1_1MHZ;
6
        DCOCTL  = CALDCO_1MHZ;
7

8
        P1DIR |= BIT0;                                  // set P1.0 (LED1) as output
9
        P1OUT &= ~BIT0;                                 // P1.0 low
10

11
        // Configure Timer A
12
        TACCTL0 = CCIE;                                 // CCR0 interrupt enabled
13
        TACCR0 = 1000;                                  // 
14
        TACTL = TASSEL_2 + ID_2 + MC_3;                 // SMCLK, /4, upmode/down
15

16
        _BIS_SR(LPM0_bits + GIE);                       // Enter LPM0 w/ interrupt
17
        return 0;
18
}
19

20
// Timer A0 interrupt service routine
21
#pragma vector=TIMER0_A0_VECTOR
22
__interrupt void Timer_A (void)
23
{
24
  
25
        P1OUT ^= BIT0;                                  // Toggle LED
26
        TACCR0 += 1000;
27
}
#3214375
Lesenswert?

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
OP #3214461
Lesenswert?

Baue ich das Auslesen vom Temperatur und Druck des Sensors in die main() 
ein, also ganz ohne Timer Interrupt, funktioniert alles:
1
int main() {
2
 ...
3
    while( 1 ) {
4
        P1OUT ^= BIT0;
5
        
6
        uart_printf( "temp = %l\r\n", ms5611_get_temperature() );
7
        delay_ms( 29 );
8
        uart_printf( "pres = %l\r\n", ms5611_get_pressure() );
9
        
10
        delay_ms( 1000 );
11
    }

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
#3214503
Lesenswert?

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?
Gast #3214509
Lesenswert?

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...)
OP #3214587
Lesenswert?

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?
OP #3216816
Lesenswert?

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
int main(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
6
    
7
    P1DIR |= BIT0;
8
    P1OUT &= ~BIT0;
9

10
    // Configure TimerA0
11
    TACTL = TASSEL_2 | ID_3 | MC_3;   // Source = SMCLK (Sub-System master clock)
12
                                      // Mode control = MC_1 (Up mode)
13
    
14
    while( 1 ) {
15
        TACCR0  = 62500;          // Add Offset to CCR0
16
        TACCTL0 = CCIE;           // Start timer interrupt
17
        _BIS_SR(LPM0_bits + GIE); // Enter LPM0 w/ interrupt
18
        
19
        P1OUT ^= BIT0;
20
    }
21

22
    return 0;
23
}
24

25
// Timer A0 interrupt service routine
26
#pragma vector=TIMER0_A0_VECTOR
27
__interrupt void Timer_A (void)
28
{
29
    _BIC_SR_IRQ(LPM0_bits);
30
}

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?
Gast #3216850
Lesenswert?

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.
OP #3217327
Lesenswert?

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.
Gast #3218423
Lesenswert?

So, mal auf die schnelle aufs launchpad gepackt:
1
#include "io430g2553.h"
2

3
typedef unsigned char     uint8_t;
4
typedef unsigned int      uint16_t;
5

6
volatile uint16_t ticks = 0;
7

8
void main( void )
9
{
10
    // stop watchdog timer to prevent time out reset
11
  WDTCTL = WDTPW + WDTHOLD;
12
   // clock source DCO, set to 1MHz
13
  BCSCTL1 = CALBC1_1MHZ;
14
  DCOCTL = CALDCO_1MHZ;
15
   // init port
16
  P1DIR |= BIT0;                        // Set P1.0 to output direction
17
   // WDT Timer 8ms
18
  WDTCTL = WDT_MDLY_8;
19
   // enable interrupts
20
  IE1 |= WDTIE;
21
  __enable_interrupt();
22
  while(1)
23
  {
24
     // do something
25
    if(ticks % 2) P1OUT |= BIT0;
26
    else P1OUT &= ~BIT0;
27
  }
28
}
29

30
 //Watchdog Timer interrupt service routine
31
#pragma vector=WDT_VECTOR
32
__interrupt void WDT_ISR (void)
33
{
34
  static uint8_t count = 0;
35
  if(count++ >= 125)
36
  {
37
    count = 0;
38
    ticks++;
39
  }
40
}
Anstatt Timer A habe ich den WDT genommen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren