Forum: Mikrocontroller und Digitale Elektronik MSP430 Timer im Up-Mode für Sleep-Funktion


von Klaus R. (klara)


Lesenswert?

Hallo,
ich habe eine Sleep-Funktion mit Timer im Up-Mode erstellt. Zur Zeit 
arbeite ich noch mit 1 MHz, Source SMCLK. Ohne Teiler komme ich so auf 
max. 65,535 ms. Deshalb arbeite ich mit Overflow um den Sleep erweitern 
zu können. Die Dauer wird in Millisekunden eingegeben und dann mit 1024 
multipliziert um auf Takte zu kommen. Die Ungenauigkeit von 2,4% ist 
unerheblich, zudem auch Laufzeiten eine Rolle spielen.

Im Sekunden-Takt funktioniert die Funktion relativ genau. Probleme gibt 
es wenn TA0CCR0 = 1 gesetzt wird. Dann bleibt das Programm beim Return 
der Sleep-Funktion stehen.

Aber es gibt schon zuvor eine Unstimmigkeit, aus meiner Sicht. In der 
Interrupt-Funktion habe ich Breakpoints gesetzt um den Ablauf zu prüfen. 
TA0CCR0 = 2 ist jetzt auf gesetzt. Es wird zunächst erwartungsgemäss 
0x0A, der Overflow, ausgeführt. overflow_loops und overflow_rest stehen 
auf 0. Der low power mode wird beendet.

Aber, Interrupt-Funktion wird nochmals aufgerufen, TA0IV ist 0 (No 
interrupt pending). Wie kann dies sein????

Bei TA0CCR0 = 2 geht es dann munter weiter, overflow .... No interrupt 
pending .... overflow ....

Bei TA0CCR0 = 1 sieht es so aus, overflow .... No interrupt pending .... 
overflow .... hängen beim Return der Sleep-Funktion


Fragen: muss das Clear - Flag gesetzt werden? Fehlt irgendwo noch eine 
Pause wie __no_operation()???

mfg klaus

1
    
2
while(1)
3
{
4
  sleep(1)
5
  P3OUT ^= N_RESET1_P33;      /*Port toggle*/
6
}

1
#include <msp430.h>
2
3
unsigned int overflow_loops;
4
unsigned int overflow_rest;
5
6
/*****************************************************************
7
 * sleep(unsigned int time)
8
 * Return: -
9
 * Param:  (unsigned int) time
10
 *
11
 * sends the MSP to sleep mode (LPM0)
12
 * Sleep max. 65535 ms * 64
13
 *****************************************************************/
14
void sleep(unsigned int time_ms)
15
// sends MSP to Low Power Mode 0, wake up after 'time' milliseconds
16
// Interrupt handling remains enabled
17
18
/*
19
 * SMCLK = 1 MHz
20
 * Divider to 1 -> 1 MHz
21
 * kürzeste Zeitspanne = 1 Takt / 1 MHz  = 1 µs
22
 * längste Zeitspanne  = 65535 Takte / 1 MHz  = 65535 µs = 65,535 ms
23
 *
24
 * Für grössere Verzögerungen -> Overflow nutzen
25
 *
26
 * 1000 ms -> 1000000/65536 = 15 mal overflow, Rest = 16960
27
 *
28
 * Für kleinere (genauere) Verzögerungen -> Div kleiner, Frequenz höher
29
 */
30
31
32
{   TA0CTL = MC_0;              // Stop timer, um sicher zu sein
33
  TA0CCTL0 = CCIE;             // enable CCR0 interrupt
34
  TA0CTL |= TASSEL_2 | ID_0 | TAIE;     // SMCLK, Input divider /1, Interrupt
35
36
  /*
37
   * Der Basis-Takt hat 1 µs Länge
38
   * Die Dauer wird in ms angegeben.
39
   * Zur Vereinfachung wird um Faktor 1024 multipliziert (Shift 10)
40
   */
41
42
//  overflow_loops = time_ms >> 6;      // Shift 16 - 10
43
//  overflow_rest = 0x0000FFFF & (time_ms << 10);
44
//
45
  overflow_loops = 0;
46
  overflow_rest = 1;
47
48
  if(overflow_loops == 0)
49
  {
50
    TA0CCR0 = overflow_rest;       // set CCR0
51
    overflow_rest = 0;
52
  }
53
  else
54
  {
55
    TA0CCR0 = 0xFFFF;           // set CCR0
56
  }
57
58
  TA0CTL |= MC_1 ;             // TIMER START; up mode (counts up to TACCR0)
59
  __bis_SR_register(LPM0_bits + GIE);     // LPM0, Interrupts enable
60
}
61
62
// ---------------------------------------------------------------
63
// ------------------ I N T E R R U P T S ------------------------
64
// ---------------------------------------------------------------
65
66
#pragma vector = TIMER0_A0_VECTOR                   // Timer A overflow (TAIFG) interrupt
67
__interrupt void TIMER0_A0_ISR(void)                //
68
{
69
    switch(TA0IV) {                                 // TA0IV must be read to reset interrupt
70
71
    case 0x00:                                  // No interrupt pending
72
      __no_operation();      // SET BREAKPOINT HERE
73
      break;
74
      case 0x02:                                  // CCR1
75
      __no_operation();      // SET BREAKPOINT HERE
76
        break;
77
      case 0x04:                                  // CCR2
78
      __no_operation();      // SET BREAKPOINT HERE
79
        break;
80
        case 0x0A:                                  // Timer overflow, 0x0A == TAIFG (see SLAU144 12.3.5)
81
          if (overflow_loops > 0)
82
          {
83
            overflow_loops--;
84
            break;
85
          }
86
          else
87
          {
88
            if (overflow_rest > 0)
89
            {
90
              TA0CCR0 = overflow_rest;
91
              overflow_rest = 0;
92
              break;
93
            }
94
            else
95
            {
96
                  TA0CTL = MC_0;                // Stop timer
97
                  TA0CTL |= TACLR;              // Clear - Flag setzen ??????
98
                  __bic_SR_register_on_exit(LPM0_bits);     // Exit low power mode
99
                  break;
100
            }
101
          }
102
        default:
103
      __no_operation();      // SET BREAKPOINT HERE
104
        break;
105
106
    }
107
}

von Clemens L. (c_l)


Lesenswert?

TA0IV gilt nur für Interrupts, die am TIMER0_A1_VECTOR ankommen.

Du solltest dich entscheiden, ob du TA0CTL.TAIE oder TA0CCTL0.CCIE haben 
willst.

Und ID/IDEX wäre einfacher, als Überläufe manuell zu behandeln.

von Klaus R. (klara)


Lesenswert?

Vielen Dank für Deine Antwort.

Clemens L. schrieb:
> TA0IV gilt nur für Interrupts, die am TIMER0_A1_VECTOR ankommen.
>
Ist doch in Ordnung, oder?

> Du solltest dich entscheiden, ob du TA0CTL.TAIE oder TA0CCTL0.CCIE haben
> willst.
>
Wie mache ich das?

> Und ID/IDEX wäre einfacher, als Überläufe manuell zu behandeln.
Kannst Du das mal ausführlicher erläutern? ID/IDEX sagt mir im Moment 
nichts.

mfg klaus

von Clemens L. (c_l)


Lesenswert?

Klaus R. schrieb:
> Clemens L. schrieb:
>> TA0IV gilt nur für Interrupts, die am TIMER0_A1_VECTOR ankommen.
>
> Ist doch in Ordnung, oder?

Nein, weil du nicht TIMER0_A1_VECTOR sondern TIMER0_A0_VECTOR benutzt. 
Siehe Abschnitt 12.2.6 des User's Guide.

>> Du solltest dich entscheiden, ob du TA0CTL.TAIE oder TA0CCTL0.CCIE haben
>> willst.
>>
> Wie mache ich das?

Indem du eines dieser Bits setzt, und das andere nicht. Und dann den 
richtigen Interrupt behandelst.

> ID/IDEX sagt mir im Moment nichts.

Siehe Abschnitt 12.2.1.1 des User's Guide. Allerdings hat dein 
unbekannter Chip wahrscheinlich kein IDEX.

von Klaus R. (klara)


Lesenswert?

Hallo Clemens,
ich habe den MSP430G2553 und das User-Guide SLAU144J. Im CCS habe ich 
über den Resource Explorer gesehen, dass ich damit aktuell bin.

Clemens L. schrieb:
> Klaus R. schrieb:
>> Clemens L. schrieb:
>>> TA0IV gilt nur für Interrupts, die am TIMER0_A1_VECTOR ankommen.
>>
>> Ist doch in Ordnung, oder?
>
> Nein, weil du nicht TIMER0_A1_VECTOR sondern TIMER0_A0_VECTOR benutzt.
> Siehe Abschnitt 12.2.6 des User's Guide.
>
Mit TIMER0_A1_VECTOR lande ich in ISR_TRAP.ASM. Das Sample 
msp430g2xx3_ta_02.c den TIMER0_A0_VECTOR.

Im Abschnitt 12.2.6 des User's Guide finde ich leider Angabe über die 
Zuordnung von Variablen und Vektoren. Es ist ziemlich frustrierend sich 
die Infos aus den Samples herauszuholen. Die geben ja auch nicht alles 
wieder.

>>> Du solltest dich entscheiden, ob du TA0CTL.TAIE oder TA0CCTL0.CCIE haben
>>> willst.
>>>
>> Wie mache ich das?
>
> Indem du eines dieser Bits setzt, und das andere nicht. Und dann den
> richtigen Interrupt behandelst.
>
Ich möchte eigentlich den TA0CTL.TAIE nehmen und dann über TA0IV den 
overflow behandeln. Wenn ich den TA0CCTL0.CCIE nicht setze komme ich in 
TIMER0_A0_VECTOR aber nicht hinein.


>> ID/IDEX sagt mir im Moment nichts.
>
> Siehe Abschnitt 12.2.1.1 des User's Guide. Allerdings hat dein
> unbekannter Chip wahrscheinlich kein IDEX.
Da steht bei mir nur:
1
12.2.1.1 Clock Source Select and Divider
2
The timer clock can be sourced from ACLK, SMCLK, or externally via TACLK or INCLK. The clock source
3
is selected with the TASSELx bits. The selected clock source may be passed directly to the timer or
4
divided by 2, 4, or 8, using the IDx bits. The timer clock divider is reset when TACLR is set.

von Clemens L. (c_l)


Lesenswert?

Klaus R. schrieb:
> Im Abschnitt 12.2.6 des User's Guide finde ich leider Angabe über die
> Zuordnung von Variablen und Vektoren.

Siehe auch den Abschnitt "Interrupt Vector Addresses" des Datenblatts 
und die xxx_VECTOR-Definitionen am Ende von msp430g2553.h.

> Ich möchte eigentlich den TA0CTL.TAIE nehmen und dann über TA0IV den
> overflow behandeln.

Dann brauchst du TIMER0_A1_VECTOR.

> Mit TIMER0_A1_VECTOR lande ich in ISR_TRAP.ASM.

Weil du TA0CCTL0.CCIE auch gesetzt hast; dann springt die CPU beim 
Erreichn des CCR0-Wertes auch zu TIMER0_A0_VECTOR.

> Das Sample msp430g2xx3_ta_02.c den TIMER0_A0_VECTOR.

Weil es (nur) TA0CCTL0.CCIE benutzt.

von Klaus R. (klara)


Lesenswert?

Hallo Clemens,
es läuft wie erhofft!

Ich konnte trotzdem keinen richtigen Bezug zur ISR finden. Eine 
hilfreiche Stelle war ein "Interrupt Overview", Seite 28.
http://web.eng.fiu.edu/watsonh/EEL4709/MSP430/MSP430Timers.pdf

Die Header-Datei und das Datenblatt des µC brachten auch Infos, aber 
letztlich brauchte ich noch die Hilfe eines MSP430 - Buches.

http://www.hutech.edu.vn/dinhkem/khoacntt/attachments/article/2809/MSP430%20Microcontroller%20Basics.pdf

Den Link dazu habe ich hier im Forum erhalten.
1
// ----------------------------------------------------------------------
2
// Interrupt service routine for TAIFG , called after random interval
3
// ----------------------------------------------------------------------
4
#pragma vector = TIMERA1_VECTOR
5
__interrupt void TIMERA1_ISR (void) // Flag NOT cleared automatically
6
{
7
TACTL_bit.TAIFG = 0; // Acknowledge interrupt
8
__low_power_mode_off_on_exit();
9
}
10
// ----------------------------------------------------------------------
11
// Interrupt service routine for TACCR0.CCIFG , called on capture
12
// ----------------------------------------------------------------------
13
#pragma vector = TIMERA0_VECTOR
14
__interrupt void TIMERA0_ISR (void) // Flag cleared automatically
15
{
16
__low_power_mode_off_on_exit();
17
}

Es wurde bei der Messung der Sleep-Dauer klar, mit 1 MHz SMCLK-Takt wird 
es erst ab 1 ms (TA0CCR0 = 1000) genauer. Darunter spielt die Laufzeit 
eine zunehmende Rolle. Ich werde vermutlich auf 8 MHz oder vielleicht 16 
MHz gehen. Aber das wird sich noch zeigen.

Nochmals vielen Dank für Deine Geduld.
mfg klaus

von Klaus R. (klara)


Angehängte Dateien:

Lesenswert?

Hallo,
ich habe mal die fertige sleep-Funktion hier beigefügt.
mfg klaus

von jöjöjö (Gast)


Lesenswert?

Klaus R. schrieb:
> Im Abschnitt 12.2.6 des User's Guide finde ich leider Angabe über die
> Zuordnung von Variablen und Vektoren.

???

Dort steht:
12.2.6 Timer_A Interrupts
Two interrupt vectors are associated with the 16-bit Timer_A module:
• TACCR0 interrupt vector for TACCR0 CCIFG
• TAIV interrupt vector for all other CCIFG flags and TAIFG
...
12.2.6.3 TAIV Software Example
...

Was braucht man noch?


Klaus R. schrieb:
> brauchte ich noch die Hilfe eines MSP430 - Buches

Natürlich muss man sich mit dem Thema beschäftigen. Das fliegt dir nicht 
nachts zu. :-<

Klaus R. schrieb:
> switch(TA0IV)
probier mal
switch (__even_in_range(TAIV, 0x0A))

und damit für Dritte das Ganze besser zu lesen ist, nutze die defines 
aus dem header file anstatt der Werte
/* T0_A3IV Definitions */
#define TA0IV_NONE             (0x0000)       /* No Interrupt pending */
#define TA0IV_TACCR1           (0x0002)       /* TA0CCR1_CCIFG */
#define TA0IV_TACCR2           (0x0004)       /* TA0CCR2_CCIFG */
#define TA0IV_6                (0x0006)       /* Reserved */
#define TA0IV_8                (0x0008)       /* Reserved */
#define TA0IV_TAIFG            (0x000A)       /* TA0IFG */
1
switch(__even_in_range(TAIV, TA0IV_TAIFG)) {  
2
  case TA0IV_NONE:
3
     __no_operation();      // SET BREAKPOINT HERE
4
     break;
5
  case TA0IV_TACCR1:
6
     __no_operation();      // SET BREAKPOINT HERE
7
     break;
8
...

von Klaus R. (klara)


Lesenswert?

jöjöjö schrieb:
> Klaus R. schrieb:
>> Im Abschnitt 12.2.6 des User's Guide finde ich leider Angabe über die
>> Zuordnung von Variablen und Vektoren.
>
> ???
>
> Dort steht:
> 12.2.6 Timer_A Interrupts
> Two interrupt vectors are associated with the 16-bit Timer_A module:
> • TACCR0 interrupt vector for TACCR0 CCIFG
> • TAIV interrupt vector for all other CCIFG flags and TAIFG
> ...
> 12.2.6.3 TAIV Software Example
> ...
>
> Was braucht man noch?
>
Ja das ist ja genau das Dilemma. Im Headerfile msp430g2553.h finde ich 
zwar die Namen der Vektoren, aber welchen Interrups sind die zugeordnet?

Jetzt mit meinen neuen Informationen könnte ich ja raten.

TIMER0_A0_VECTOR ..... /* 0xFFF2 Timer0_A CC0 */
TIMER0_A1_VECTOR ..... /* 0xFFF0 Timer0)A CC1, TA0 */
TIMER1_A0_VECTOR ..... /* 0xFFFA Timer1_A CC0 */
TIMER1_A1_VECTOR ..... /* 0xFFF8 Timer1_A CC1-4, TA1 */

Auch die Namen der dazugehörigen ISR-Funktionen werden wohl mal mit und 
ohne dem Zusatz _ISR geschrieben.

> probier mal
> switch (__even_in_range(TAIV, 0x0A))
>
Diesen Ausdruck verwendet John Davies im besagten Buch auch stetig. 
Spart man sich wirklich so viel Laufzeit?

> und damit für Dritte das Ganze besser zu lesen ist, nutze die defines
> aus dem header file anstatt der Werte

Ich hatte es quasi aus den Samples von TI übernommen, finde die Defines 
aber auch lesbarer.

Besten Dank.
Klaus

von Clemens L. (c_l)


Lesenswert?

Klaus R. schrieb:
>> • TACCR0 interrupt vector for TACCR0 CCIFG
>> • TAIV interrupt vector for all other CCIFG flags and TAIFG
>
> Im Headerfile msp430g2553.h finde ich zwar die Namen der Vektoren,
> aber welchen Interrups sind die zugeordnet?

Einer für CCR0, der andere für alle anderen CCRs und TA.

Mit zwei TimerA-Modulen sind das dann vier Interrupt-Vektoren:

> TIMER0_A0_VECTOR ..... /* 0xFFF2 Timer0_A CC0 */
> TIMER0_A1_VECTOR ..... /* 0xFFF0 Timer0)A CC1, TA0 */
> TIMER1_A0_VECTOR ..... /* 0xFFFA Timer1_A CC0 */
> TIMER1_A1_VECTOR ..... /* 0xFFF8 Timer1_A CC1-4, TA1 */

Und die Vektor-Adressen kannst du auch im Datenblatt nachschlagen.

(Die Namen der Timer-Symbole sind historisch gewachsen und deshalb sehr 
inkonsistent.)

> Auch die Namen der dazugehörigen ISR-Funktionen werden wohl mal mit und
> ohne dem Zusatz _ISR geschrieben.

Der Name der ISR-Funktion ist egal.

>> switch (__even_in_range(TAIV, 0x0A))
>
> Spart man sich wirklich so viel Laufzeit?

Das kommt auf den Compiler an.

von Klaus R. (klara)


Lesenswert?

Danke für die Erläuterung der Hintergründe.
mfg klaus

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
Noch kein Account? Hier anmelden.