Forum: Mikrocontroller und Digitale Elektronik MSP430: Mehrere Interrupts pro Timer


von Johannes (menschenskind)


Lesenswert?

Hallo

Ich habe 3 LEDS an den Ports des Timer B hängen und man kann für diesen 
Timer bis zu 7 Interrupts für unterschiedliche Compare-Werte auslösen 
lassen.
Jetzt hab ich TB_0.1, TB_0.2 und TB_0.3 aber ich finde in der 
MSP-Headerdatei nur diesen Eintrag:
1
#define TIMERB1_VECTOR       TIMER0_B1_VECTOR /* Timer0_B7 CC1-6, TB */
Wonach meine ISR so aussehen könnte:
1
#pragma vector=TIMERB1_VECTOR
2
__interrupt void TB_0.1(void){
3
  TBCCR1 += 100;                      
4
}

Aber welchen VECTOR nehme ich für die anderen beiden ISRn?

Weil TIMERB2_VECTOR und TIMERB3_VECTOR existieren nicht.

Danke
Hannes

von who (Gast)


Lesenswert?

Um welchen Type geht es? Gib einmal die ganze Bezeichnung an.

von Mrs MSP (Gast)


Lesenswert?

Dein Freund ist Family User Guide.

In der ISR zum TIMER_B1_VECTOR wird über das TB0IV die Interrupt-Quelle 
ermittelt. In der TIMER_B0_VECTOR ISR nur CC0.
Dazu steht bestimmt etwas in den Header-File Kommentaren.

Tipp:
Zur Auswertung kommt das __even_in_range ins Spiel.

von DirkZ (Gast)


Lesenswert?

Mrs MSP schrieb:
> Tipp:
> Zur Auswertung kommt das __even_in_range ins Spiel.

aber nur, wenn man keinen GCC verwendet.

von Johannes (menschenskind)


Lesenswert?

Ok, sehr schön. Hatte dann auch überlegt, dass es evtl. mit ner 
Switch-Case-Anweisung geht.
Das wäre jetzt mein Code:
1
#pragma vector=TIMERB1_VECTOR
2
__interrupt void TB(void){
3
4
  switch(__even_in_range(TBIV,3)){
5
    case 0x02:
6
      TBCCR1 += 100;                      
7
    case 0x04:
8
      TBCCR2 += 50;
9
    case 0x06:
10
      TBCCR3 += 10;
11
  }
12
}

Kann das Programm grad noch nicht testen, da der Programmer anderweitig 
im Einsatz ist, aber sieht das so korrekt aus. Die case-Werte hab ich 
der Tabelle im User Guide auf Seite 394 entnommen.
Wenn ein Interrupt kommt, werden dann diese TBCCR1..6 CCIFG entsprechend 
auf 1 gesetzt?
Ist meine Case-Abfrage dann richtig?

von Uwe (Gast)


Lesenswert?

break?

von Johannes (menschenskind)


Lesenswert?

Uups natürlich....hehe

Aber meine Frage gilt eher dem
1
switch(__even_in_range(TBIV,3)){
Weil im User Guide steht , dass für NUM (in dem Fall hab ich 3 
eingesetzt) eine Zahl ab 0 eingetragen werden muss.
Ist das dann die Anzahl der zu verarbeitenden Interrupts? Weil 0 würde 
dann ja wenig Sinn ergeben...

von Johannes (menschenskind)


Lesenswert?

So hab's jetzt mal angeworfen, aber es blinkt leider nix :(
Die ISR wird auch nie aufgerufen (LED leuchtet nicht).
Bitte um Feedback, ob der Timer richtig initialisiert ist.
Danke
1
#include "io430.h"
2
#include "io430x54xA.h"
3
4
/*********** Setup *********************************/
5
#define  LED_PORT P4OUT
6
#define OUTPUT 0x7E //* Bit 1-6 are included in Mask
7
8
#define LED_1 ( (1*P4OUT3) | (0*P4OUT2) | (1*P4OUT1) )
9
#define LED_2 0x60 
10
11
/****** MAIN ***************************************/
12
void main( void )
13
{   
14
    WDTCTL = WDTPW + WDTHOLD; //*Stop watchdog timer to prevent time out reset
15
  
16
/** Setup Timer B **/
17
         TBCTL =  TBSSEL_3 +   //*Clocksource is SMCLK
18
      TBIE +    //*Enable Interrupt 
19
      TBCLR +    //*Clear Timer
20
      MC_2 +    //*Continuous Mode
21
      CNTL_1 +  //*CounterLength 0x3FF
22
      TBCLGRP_0 ;  //*No Grouping
23
  
24
  
25
  TBCCTL1 =   OUTMOD_4 +  //* Mode "Toggle Output" for TB0.1
26
        CCIE;
27
  TBCCR1 = 2000;
28
    
29
  TBCCTL2 =  OUTMOD_4 +  //* Mode "Toggle Output" for TB0.2
30
        CCIE;
31
    
32
  TBCCTL3 =  OUTMOD_4 +  //* Mode "Toggle Output" for TB0.3
33
        CCIE;
34
  
35
36
/** Setup Port4 as Output **/
37
  P4DIR = OUTPUT;
38
  
39
/** Toggle LED for Init **/  
40
  LED_PORT = 0x0E;
41
  __delay_cycles(1000000);
42
  LED_PORT ^= 0x0E;
43
  
44
  while(1){}   
45
  
46
}
47
48
#pragma vector=TIMERB1_VECTOR
49
__interrupt void TB(void){
50
51
  LED_PORT ^= 0x0E;
52
  __delay_cycles(1000000);
53
54
  switch(__even_in_range(TBIV,3)){
55
    case 0x02:
56
      TBCCR1 += 100;
57
      break;
58
      
59
    case 0x04:
60
      TBCCR2 += 50;
61
      break;
62
      
63
    case 0x06:
64
      TBCCR3 += 10;
65
      break;
66
  }
67
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

1
switch(__even_in_range(TBIV,3))

Das ist eine Optimierungsunterstützung für den Compiler, die hier wohl 
falsch genutzt wird:
1
The __even_in_range intrinsic provides a hint to the compiler 
2
when generating switch statements for interrupt vector routines.
3
The intrinsic is usually used as follows:
4
  switch (__even_in_range(x, NUM)){ ... }
5
6
The __even_in_range intrinsic returns the value x to control the
7
switch statement, but also tells the compiler that x must be an 
8
even value in the range of 0 to NUM, inclusive.

NUM ist hier 3, aber zwei von drei case-Fällen größer.

Entweder korrigieren oder ... weglassen.
1
switch (TBIV)

von Johannes (menschenskind)


Lesenswert?

Ok, aber die ISR wird ja überhaupt nicht aufgerufen...
Das ist ja bis jetzt noch das Problem mit Priorität +++ :)

von DS aus W (Gast)


Lesenswert?

Hi Johannes,

wie wäre es mit einem GIE ( _EINT() )?
Oder versteckst Du das in der Tiefe Deiner SW?

Gruss
Dietmar

von DS aus W (Gast)


Lesenswert?

Ach ja-
zum Thema __even_in_range-
NUM soll den möglichen Wert begrenzen(timer_B also 0x0A für den TBIFG.)
Das ist zur Sicherheit, wenn irgendetwas verbogen wird.
Alles > NUM wird ignoriert.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

DS aus W schrieb:
> Alles > NUM wird ignoriert.

Und alles, was ungerade ist.

von who (Gast)


Lesenswert?

Johannes Hofmann schrieb:
> Die ISR wird auch nie aufgerufen (LED leuchtet nicht).

Das ist deine Debug Strategie???

Johannes Hofmann schrieb:
> __delay_cycles(1000000);

Was soll denn das in der ISR???

Da dir die noch Grundlagen fehlen, würde ich von der Try <> Error 
Variante Abstand nehmen und einen Debugger einsetzen. Der µC hat 
bestimmt das Spy By Wire Interface.

Leider gibst du den µC Typen nicht preis. Zum Verständnis habe ich darum 
ein Beispiel fürs launchpad mit dem G2255 geschrieben:
1
#include "io430.h"
2
3
volatile int foo = 0;
4
5
int main( void )
6
{
7
  // Stop watchdog timer to prevent time out reset
8
  WDTCTL = WDTPW + WDTHOLD;
9
  
10
  BCSCTL1 = CALBC1_1MHZ;
11
  DCOCTL = CALDCO_1MHZ;
12
  
13
  TA0CCR0 = 0xFFFF;                 // if up mode is used
14
  TA0CTL |= TASSEL1 | MC1 | TAIE;
15
  
16
  TA0CCR1 = 0xF000;
17
  TA0CCTL1 |= CCIE;
18
  
19
  TA0CCR2 = 0x0100;
20
  TA0CCTL2 |= CCIE;
21
  
22
  __enable_interrupt();
23
  
24
  while (1) ;
25
26
  // return 0;
27
}
28
29
#pragma vector=TIMER0_A1_VECTOR
30
__interrupt void Timer0_A1_ISR(void) {
31
  
32
  switch (__even_in_range(TA0IV,0x0A)) {
33
    case TA0IV_TACCR1 :
34
      foo = 1;
35
      break;
36
    case TA0IV_TACCR2 :
37
      foo = 2;
38
      break;
39
    case TA0IV_TAIFG :
40
      foo = 5;
41
      break;
42
    default :
43
      foo =  -1;
44
  }
45
}

von Johannes (menschenskind)


Lesenswert?

Naja, mit der LED hab ich das bisher immer so gemacht. Aber ich könnte 
mir im Debugmodus natürlich die einzelnen Register auch mal anschauen.
Hab mir ja ein paar Tutorials angeschaut und Beispiele zur 
Timeransteuerung, aber eben noch keine Erfahrung, wo ich genau nach dem 
Fehler suchen könnte.

Manche benutzen bei der Wertzuweisung eines Registers manchmal den '+'- 
anstatt den '|'-Operator. Muss man dabei irgendwas beachten oder ist das 
vom Effekt her deckungsgleich?

von who (Gast)


Lesenswert?

Johannes Hofmann schrieb:
> ich könnte
> mir im Debugmodus natürlich die einzelnen Register auch mal anschauen

Du bist soooo großzügig. >:->

Man kann aber auch Breakpoints setzen. ;-)

Schau dir zuerst die ISR an versuch dein Programm ans Laufen zu 
bekommen. Das mit den Feinheiten kommt später. Ach, aber auf keinen Fall
reg += bit1 + bit7;
    ^^
schreiben!!!

von DS aus W (Gast)


Lesenswert?

Das Schlimme ist, 0b0001 + 0b0010 = 0b0011
und TI macht das in ihren Beispielen.
Der Effekt ist tatsächlich gleich.
Aber das ist Murks (schlechter Stil).
Ein ODER ist ein ODER - immer.
Ein + ist eine arithmetische Operation.
Gewöhne Dir bitte den Quatsch von TI nicht an.

Dann wäre es nett, wie schon geschrieben, wenn Du
Controllertyp und Umgebung nennen würdest ;-)
Meinen Hinweis von oben findest Du im Beispiel von who:
__enable_interrupt();

Schönen Abend noch!

von who (Gast)


Lesenswert?

Solange nur mit Flags gerechnet wird und eine Zuweisung erfolgt kommt 
das gleiche raus. Aber bei Konstanten aus 2 oder mehr Bit oder bei 
Einbeziehung des left value mit bereits gesetzten Bits f.... du dich ins 
Knie.

Also nicht nur schlechter Stil, sondern oft falsch !

von Johannes (menschenskind)


Lesenswert?

Es ist ein MSP430F5438A.
Arbeite mit der IAR Workbench.

Die ganzen Register für die Konfiguration sind doch von vorneherein alle 
mit 0 initialisiert. Und nur wenn ich ne andere Konfiguration als die 
Standardeinstellungen haben möchte, muss ich die entsprechenden Bits auf 
1 setzen. Ist doch so?

von Johannes (menschenskind)


Lesenswert?

Also im TBIV-Register wechseln die entsprechenden Interruptflags auch 
ganz artig, aber es kommen auch Kombinationen vor, die gar nicht auf 
meine 3 Interruptvektoren zutreffen.

von Johannes (menschenskind)


Lesenswert?

Also die ISR funktioniert, aber ich sehe leider nichts an den LEDS
Das OUT-Signal müsste ja nun an den 3 Pins anliegen und somit die LEDs 
schalten.
Habe auch nichts im User Guide gefunden, dass man da für die PWM noch 
was konfigurieren müsste.
Der Timer zähl bis 0xFFFF und eine Manipulation des TBR-Wertes bringt 
auch keinen Erfolg.
Langsam wird's unschön... :(
1
#include "io430.h"
2
#include "io430x54xA.h"
3
4
/*********** Setup *********************************/
5
#define  LED_PORT P4OUT
6
#define OUTPUT 0x7E //* Bit 1-6 are included in Mask
7
8
#define LED_1 ( (1*P4OUT3) | (0*P4OUT2) | (1*P4OUT1) )
9
#define LED_2 0x60 //* ( (0<<P4OUT6) | (1<<P4OUT5) | (1<<P4OUT4) )
10
11
#define ON 1
12
#define OFF 0
13
#define DELAY_1 10000
14
15
/****** MAIN ***************************************/
16
void main( void )
17
{   
18
    WDTCTL = WDTPW | WDTHOLD; //*Stop watchdog timer to prevent time out reset
19
  
20
/** Setup Timer B **/
21
  TBCTL =  TBSSEL_2 |     //*Clocksource is SMCLK
22
        TBIE |    //*Enable Interrupt 
23
        TBCLR |    //*Clear Timer
24
        MC_2 |    //*Continuous Mode
25
        CNTL_0;    //*CounterLength 0xFFF  
26
    
27
  TBCCTL1 =   OUTMOD_4 |  //* Mode "Toggle Output" for TB0.1
28
        CCIE;    //* Enable Interrupt for Register
29
  TBCCR1 =   2000;    
30
    
31
  TBCCTL2 =  OUTMOD_4 |  //* Mode "Toggle Output" for TB0.2
32
        CCIE;
33
    
34
  TBCCTL3 =  OUTMOD_4 |  //* Mode "Toggle Output" for TB0.3
35
        CCIE;  
36
37
/** Setup Port4 as Output **/
38
  P4DIR = OUTPUT;
39
  
40
/** Toggle LED for Init **/  
41
  LED_PORT = 0x0E;
42
  __delay_cycles(DELAY_1);
43
  LED_PORT ^= 0x0E;
44
    __delay_cycles(DELAY_1);
45
  
46
  __enable_interrupt();  //* Enable Interrupts
47
  
48
  while(1);
49
  
50
}
51
52
#pragma vector=TIMERB1_VECTOR
53
__interrupt void TB(void){
54
55
  //switch(__even_in_range(TBIV,3)){
56
  switch(TBIV){
57
    case 0x02:
58
      TBCCR1 += 1000;
59
      break;
60
      
61
    case 0x04:
62
      TBCCR2 += 100;
63
      break;
64
      
65
    case 0x06:
66
      TBCCR3 += 50;
67
      break;
68
    default:
69
      break;
70
  }
71
}

von DirkZ (Gast)


Lesenswert?

Johannes Hofmann schrieb:
> Es ist ein MSP430F5438A.
> Arbeite mit der IAR Workbench.

Nur der MSP430F5438A oder das MSP-EXP430F5438?

Schau mal hier: 
http://320volt.com/wp-content/uploads/2010/10/msp430x23x0_tb_07.c.html

//   As coded with TBCLK ~1.2MHz DCO, toggle rates are:
//   P4.0 = TBCCR0 = 1200000/(2*200) = 3000Hz
//   P4.1 = TBCCR1 = 1200000/(2*400) = 1500Hz
//   P4.2 = TBCCR2 = 1200000/(2*600) = 1000Hz

toggle - an/aus: leuchtet die LED bei 3khz?


Oder lieber PWM?
http://320volt.com/wp-content/uploads/2010/10/msp430x23x0_tb_10.c.html

von who (Gast)


Lesenswert?

Johannes Hofmann schrieb:
> Der Timer zähl bis 0xFFFF und eine Manipulation des TBR-Wertes bringt
> auch keinen Erfolg

Welchen Erfolg erwartest du? Wenn es Änderungen der Ausgänge an P4 sein 
sollen, musst du sie auch beeinflussen. Entweder setzt oder löscht du 
die Ausgänge selbst, oder du nutzt die alternativen Pin-Funktionen. Und 
woher weiss der µC was du willst? Natürlich, ein Konfigurationsregister: 
PxSEL.

Jetzt geht es im Family User Guide weiter.

von Johannes (menschenskind)


Lesenswert?

Hm, und warum ist dann nicht ein klitzekleiner Hinweis im User Guide 
aufgeführt, dass man das P4SEL noch manipulieren muss.
Jetzt funktioniert es.

Gleich noch ne Frage:
Kann man aus irgeneinem Register den Maximalwert des Timers abrufen? Aus 
TB0R kriegt man den aktuellen Zählwert.
Mit CNTL_0 bis CNTL_3 kann man den Wert ja nur einstellen.

von Dennis (Gast)


Lesenswert?

Das ist im Datenblatt zum Controller aufgeführt.

Und was heißt der Maximalwert? Bei 16Bit: 65535

von Johannes (menschenskind)


Lesenswert?

Naja, wenn ich den Wert per Registereinstellung ändere, dann muss sich 
der Controller den Wert ja irgendwo herholen.
Und das würd ich auch gern. Aus Gründen der Übersichtlichkeit des Codes.

von Dennis (Gast)


Lesenswert?

Du schreibst den doch in die CCR-Register

von who (Gast)


Lesenswert?

Johannes Hofmann schrieb:
> und warum ist dann nicht ein klitzekleiner Hinweis im User Guide
> aufgeführt, dass man das P4SEL noch manipulieren muss.

Bei einem Blick auf das Bauteilsymbol fällt dir sicherlich auf, dass 
verschiedene Pins mehrere Funktionen haben können. Kann der µC raten was 
du möchtest? Warum muss man überhaupt programmieren? ;-)

Im Kapitel 12.2.6 finde ich einen "klitzekleinen Hinweis".

Aber lesen ist schwer, Englisch auch noch, aber ... im Datenblatt auf 
Seite 72 gibt 's soagar ein Bildchen vom Port. ;-)

Keine Ausreden mehr: Lesen, Programmieren, Debuggen und Freuen!

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.