Forum: Mikrocontroller und Digitale Elektronik MSP430: Timer_A Output Units


von Thaller (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen

Ich bin gerade dabei einen UART mit dem MSP430F147 zu programmieren. Da 
ich später einen kleineren MSP430 ohne USART-Einheit verwenden werde, 
versuche ich das Problem mit dem Timer_A zu lösen.

Die Beispielprogramme von TI habe ich mir angeschaut und angepasst, 
funktioniert hat das ganze nicht. Nun habe ich das ganze noch ein wenig 
umgebaut, so dass es in den meisten Fällen funktioniert. Leider stimmt 
aber oftmals die Bitdauer einzelner Bits nicht, was dann beim Empfang 
auf dem PC zu falschen Werten führt. Um das Problem zu isolieren habe 
ich die Software mal auf das essentielle beschränkt und habe da das 
selbe Problem feststellen können.

Den Code könnt ihr unten sehen. Ich Toggle dabei den Out0 Ausgang des 
Timer_A jeweils nach 1 ms, indem ich in der Interrupt-Routine zwischen 
Set- und Reset-Mode wechsle. Der Wechsel des Out Mode erfolgt über den 
Out Mode 7, wie es im Family Guide empfohlen wird. Dass ich nicht den 
Toggle-Mode verwende ist Absicht, das Problem tritt dann nicht. Um das 
Ausganssignal mit den Zeitpunkten der Aufrufe der Interrupt-Routine 
vergleichen zu können invertiere ich jeweils noch den Zustand einer LED.

Im Ahang seht ihr ein Bild. Auf dem Kanal 1 unten ist der Ausgang Out0 
des Timer_A zu sehen. Oben auf Kanal 2 seht ihr das Signal der LED. Das 
sechste und das achte High-Bit weisen einen Fehler auf. Ersteres ist zu 
kurz, letzteres zu lang.

Wisst ihr, wieso dieser Fehler auftritt? Für eure Hilfe bin ich schon 
jetzt dankbar.

Grüsse
Thaller
1
#include "io430.h"
2
#include "intrinsics.h"
3
4
#define TXD 0x02                                            // P1.1 für TX
5
#define LED 0x80                                            // P1.7 für LED
6
#define Time 8000                                           // 1 ms
7
8
unsigned char counter = 0;
9
10
void main (void)
11
{  
12
  WDTCTL = WDTPW + WDTHOLD;
13
 
14
  // 8 MHz für MCLK, SMCLK
15
  BCSCTL2 = SELM_2 + SELS;
16
  BCSCTL1 &= ~XT2OFF;
17
18
  TACTL = TASSEL_2 + MC_2;                                  // SMCL für TA, Cont. Mode
19
  TACTL &= ~TAIE;
20
  TACCTL0 = OUT;                                            // Mark auf TX
21
  
22
  P1SEL = TXD;                                              // TA OUT0 für TX
23
  P1DIR = TXD + LED;
24
  P1OUT |= LED;                                            // LED ein
25
26
  __bis_SR_register(GIE);                                   //Gener Interrupt Enable
27
  
28
  
29
  TACCR0 = TAR + Time;                                      // Zeitpunkt des nächsten Interrupts
30
  TACCTL0 |= OUTMOD_1 + CCIE;                               // Set Mode, Compare Interrupt Enable
31
  
32
  while(1);
33
}
34
35
36
// Timer A0 interrupt service routine
37
#pragma vector = TIMERA0_VECTOR
38
__interrupt void Timer_A (void)
39
{                               
40
  TACCR0 += Time;                                           // Nächstn Interrupt festlegen
41
42
  if(counter % 2)                                           // Toggle
43
  {
44
    TACCTL0 |= OUTMOD_7;                                    // Über Out Mode 7
45
    TACCTL0 &= (0xFF1F | OUTMOD_5);                         // in Out Mode Reset wechseln
46
                    
47
  }
48
  else
49
  {
50
    TACCTL0 |= OUTMOD_7;                                  // Über Out Mode 7
51
    TACCTL0 &= (0xFF1F | OUTMOD_1);                       // in Out Mode Set wechseln       
52
  }
53
  
54
  P1OUT ^= LED;                                            // LED invertieren
55
 
56
  counter++;
57
}

von Christian R. (supachris)


Lesenswert?

Thaller wrote:
> Hallo zusammen
>
> Ich bin gerade dabei einen UART mit dem MSP430F147 zu programmieren. Da
> ich später einen kleineren MSP430 ohne USART-Einheit verwenden werde,
> versuche ich das Problem mit dem Timer_A zu lösen.
>
> Die Beispielprogramme von TI habe ich mir angeschaut und angepasst,
> funktioniert hat das ganze nicht.

Hmm...was hat denn nicht funktioniert? Bei mir klappt das bestens, mit 
einem 6MHz Quarz sogar bis 57600 Baud. Wenn man einen Baudratenquarz 
nimmt, müsste noch viel höher gehn. Man muss nur die Bitzeiten genau 
ausrechnen. Über 57600 kamen bei meinem 6MHz Quarz dann krumme Werte 
raus, und das geht bei der Software-UART nicht.

von Roman T. (rthaller)


Lesenswert?

Versucht habe ich es mit einem angepassten Beispiel gemäss 
untenstehendem Code. Dabei habe ich alles für den Empfang entfernt, da 
ich dies momentan nicht brauche.

1
//******************************************************************************
2
#include "io430.h"
3
#include "intrinsics.h"
4
5
#define TXD   0x02                          // TXD on P1.1
6
7
8
#define Bitime_5  417                        // ~ 0.5 bit length
9
#define Bitime    833                       // ~ 9604 baud
10
11
unsigned int RXTXData;
12
unsigned char BitCnt;
13
14
void TX_Byte(void);
15
16
void main (void)
17
{
18
  WDTCTL = WDTPW + WDTHOLD;
19
 
20
  // 8 MHz für MCLK, SMCLK
21
  BCSCTL2 = SELM_2 + SELS;
22
  BCSCTL1 &= ~XT2OFF;
23
24
  TACTL = TASSEL_2 + MC_2 + TACLR;                                  // SMCL für TA, Cont. Mode
25
  TACCTL0 = OUT;                                            // Mark auf TX
26
  
27
  P1SEL = TXD;                                              // TA OUT0 für TX
28
  P1DIR = TXD;
29
30
  __bis_SR_register(GIE);
31
  
32
  while(1)
33
  {
34
    RXTXData =64;
35
    TX_Byte();                                // TX Back RXed Byte Received
36
  }
37
}
38
39
// Function Transmits Character from RXTXData Buffer
40
void TX_Byte (void)
41
{
42
  BitCnt = 0xA;                             // Load Bit counter, 8data + ST/SP
43
  TACCR0 = TAR;                               // Current state of TA counter
44
  TACCR0 += Bitime;                           // Some time till first bit
45
  RXTXData |= 0x100;                        // Add mark stop bit to RXTXData
46
  RXTXData = RXTXData << 1;                 // Add space start bit
47
  TACCTL0 = OUTMOD_0 + CCIE;                   // TXD = mark = idle
48
  while ( TACCTL0 & CCIE );                   // Wait for TX completion
49
}
50
51
// Timer A0 interrupt service routine
52
#pragma vector=TIMERA0_VECTOR
53
__interrupt void Timer_A (void)
54
{
55
  TACCR0 += Bitime;                           // Add Offset to CCR0
56
57
  if ( BitCnt == 0)
58
    TACCTL0 &= ~ CCIE;                        // All bits TXed, disable interrupt
59
  else
60
  {
61
    TACCTL0 |=  OUTMOD_2;                    // TX Space
62
    if (RXTXData & 0x01)
63
      TACCTL0 &= ~ OUTMOD_2;                   // TX Mark
64
    RXTXData = RXTXData >> 1;
65
    BitCnt --;
66
  }
67
}


Dabei sind die Bitfolgen falsch, das Signal geht am Schluss nicht wieder 
auf den High-Pegel zurück und es gibt daber auch kein Stoppbit.

Da es mir ehrlich gesagt auch schleierhaft ist, wieso im Beispiel der 
Out Mode 2 verwendet wird, habe ich es so umprogrammiert, dass ich es 
für mich schlüssig ist. Und wie gesagt funktioniert es auch in den 
meisten Fällen, nur stimmen manchmal die Bitzeiten nicht.

Die ausgerechneten Werte für die Bitzeiten stimmen schon, ich hab das 
mehrmals überprüft.

Aber auch wenn ich es nun mit dem TI-Beispiel doch noch hinbekäme, würde 
es mich brennend interessieren, wieso mein Programm diese Macken hat.

Das Auftreten der Interrupts in meinem Programm stimmt jedenfalls, das 
kann ich mit dem Referenzsignal, welches ich auf die LED ausgebe, 
überprüfen.

von Christian R. (supachris)


Lesenswert?

Also ich hab das im Prinzip gtenauso, und klappt zuverlässig:
1
#define Bitime_5  27                      // ~ 0.5 bit length + small adjustment
2
#define Bitime    52 
3
4
volatile unsigned char RX_Bytes[16];
5
unsigned char charcount;
6
7
unsigned int RXTXData;
8
unsigned char BitCnt;
9
extern volatile unsigned int SYSTEM_FLAGS;
10
11
volatile unsigned int TAR_Value;
12
13
// Function Transmits Character from RXTXData Buffer
14
15
void TX_Byte (void)
16
{
17
  BitCnt = 0xA;                             // Load Bit counter, 8data + ST/SP
18
  CCR0 = TAR;                               // Current state of TA counter
19
  CCR0 += Bitime;                           // Some time till first bit
20
  RXTXData |= 0x100;                        // Add mark stop bit to RXTXData
21
  RXTXData = RXTXData << 1;                 // Add space start bit
22
  CCTL0 = OUTMOD0 + CCIE;                   // TXD = mark = idle
23
  while ( CCTL0 & CCIE );                   // Wait for TX completion
24
}
25
26
// Function Readies UART to Receive Character into RXTXData Buffer
27
void RX_Ready (void)
28
{
29
  BitCnt = 0x8;                             // Load Bit counter
30
  CCTL0 = CCIS0 + OUTMOD0 + CM1 + CAP + CCIE;   // Sync, Neg Edge, Cap
31
}
32
33
interrupt (TIMERA0_VECTOR) Timer_A (void)
34
{
35
CCR0 += Bitime;                           // Add Offset to CCR0
36
37
// RX
38
  if (CCTL0 & CCIS0)                        // RX on CCI0B?
39
  {
40
    if( CCTL0 & CAP )                       // Capture mode = start bit edge
41
    {
42
    CCTL0 &= ~ CAP;                         // Switch from capture to compare mode
43
    CCR0 += Bitime_5;
44
    _BIC_SR_IRQ(SCG1 + SCG0);               // DCO remains on after reti
45
    }
46
    else
47
    {
48
    RXTXData = RXTXData >> 1;
49
      if (CCTL0 & SCCI)                     // Get bit waiting in receive latch
50
      RXTXData |= 0x80;
51
      BitCnt --;                            // All bits RXed?
52
      if ( BitCnt == 0)
53
//>>>>>>>>>> Decode of Received Byte Here <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
54
      {
55
        CCTL0 &= ~ CCIE;                      // All bits RXed, disable interrupt
56
    RX_Bytes[charcount] = RXTXData;
57
    if((RXTXData == 0x0d))
58
    {
59
      charcount = 0;
60
      SYSTEM_FLAGS |= COMMAND_COMPLETE;
61
    }
62
    else charcount++;
63
    SYSTEM_FLAGS |= CHAR_RECEIVED;
64
            
65
      //_BIC_SR_IRQ(LPM0_bits);               // Clear LPM3 bits from 0(SR)
66
      }
67
//>>>>>>>>>> Decode of Received Byte Here <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
68
    }
69
  }
70
// TX
71
  else
72
  {
73
    if ( BitCnt == 0)
74
    CCTL0 &= ~ CCIE;                        // All bits TXed, disable interrupt
75
    else
76
    {
77
      CCTL0 |=  OUTMOD2;                    // TX Space
78
      if (RXTXData & 0x01)
79
      CCTL0 &= ~ OUTMOD2;                   // TX Mark
80
      RXTXData = RXTXData >> 1;
81
      BitCnt --;
82
    }
83
  }
84
}
85
86
void SendString(unsigned char *string)
87
{
88
  while(*string)
89
  {
90
    RXTXData = *string;
91
    TX_Byte();
92
    string++;
93
  }
94
}
95
96
int putchar(int c)
97
{
98
  RXTXData = (unsigned char)c;
99
  TX_Byte();
100
  
101
  return 0;
102
}
103
104
void PutSingleChar(int c)
105
{
106
  RXTXData = (unsigned char)c;
107
  TX_Byte();
108
}

Hab einen 6MHz Quarz dran und benutze den Timer A so:
1
TACTL = TASSEL_2 + MC_2 + TACLR + ID_1;   // SMCLK, continuous mode

Vor jedem Senden muss der Int freigegeben werden, aber das weißt su ja.

von Roman T. (rthaller)


Lesenswert?

Nun, ich seh da auch keinen Unterschied in der Funktion, aber bei mir 
läuft es definitiv nicht richtig.

von Bernhard (Gast)


Lesenswert?

// Function Transmits Character from RXTXData Buffer
void TX_Byte (void)
{
  BitCnt = 0xA;                             // Load Bit counter, 8data + 
ST/SP
  TACCR0 = TAR;                               // Current state of TA
                                               //counter
  TACCR0 += Bitime;                           // Some time till first 
bit
  RXTXData |= 0x100;                        // Add mark stop bit to
                                            // RXTXData
  RXTXData = RXTXData << 1;                 // Add space start bit
  TACCTL0 = OUTMOD0 + CCIE;                   // TXD = mark = idle
  while ( TACCTL0 & CCIE );                   // Wait for TX completion
}

Benutz die Funktion hier. Du musst den Unterstrich bei OUTMOD0 
weglassen.
Sollte hoffentlich gehen

von Roman T. (rthaller)


Lesenswert?

Danke für eure Hilfe, aber ob nun OUTMOD_0 oder OUTMOD0 kommt auf den 
Compiler an. Ich benutze die aktuelle IAR Kickstart Edition und da ist 
es als OUTMOD_0 definiert, das andere kennt er, wenn ich mich recht 
besinne, gar nicht.

Nun, ich begreif trotzdem nicht, wieso weder das TI-Beispiel noch mein 
Programm funktionieren.

von szimmi (Gast)


Lesenswert?

Doch doch, er kennt beides.
Beim IAR isses IMHO so, dass mit Underscore immer die einzelnen Modi 
(OUTMOD_0...OUTMOD_7) und ohne Underscore die einzelnen Bits 
(OUTMOD0...OUTMOD2) in den Registern bezeichnet werden. Is ein bissl 
tricky und fehlerträchtig, aber man gewöhnt sich an alles :-)

von Roman T. (rthaller)


Lesenswert?

Achso. Dabei könnte ich schwören, dass der beim kompilieren reklamiert 
hat und ich es deshalb geändert habe. Nun, ich werde das montags 
ausprobieren und hier  reinschreiben, ob es funktioniert hat oder nicht. 
Vielen dank auf jeden Fall.

von Roman T. (rthaller)


Lesenswert?

Nachtrag:

Ich habe den IAR kurz zu Hause installiert und verstehe nun auch, wieso 
er bei mir OUTMOD0 nicht gekannt hat. Ich habe das standardmässige 
Headerfile "io430.h" eingebunden, welches dann wiederum das Headerfile 
"io430x14x.h" einbindet. Und in diesem ist OUTMOD0 gegenüber dem 
Headerfile "msp430x14x.h" im Beispiel von TI nicht definiert. Da frage 
ich mich schon, wieso zwei Headerfiles existieren, welche nicht gleich 
sind. Die unterscheiden sich noch diversen weiteren Punkten.

Und nun ist mir auch endlich klar, wieso im Beispiel von TI in der 
Interruptroutine das Bit OUTMOD2 beinflusst wird. Das führt wie in 
meinem ersten Beispiel zu einem Umschalten zwischen OUTMOD_1 und 
OUTMOD_5, da das erste Bit OUTMOD0 ja bereits in TX_Byte() gesetzt 
wurde.

Vielen Dank für die Hilfe, nun ist mir schon einiges klarer.

Aber es würde mich nun doch noch interessieren, wieso mein Programm aus 
dem ersten Eintrag nicht sauber funktioniert. Dort schalte ich ja auch 
einfach zwischen OUTMOD_1 und OUTMOD_5 um.

von Roman T. (rthaller)


Lesenswert?

So, ich habe das Beispiel von TI mit dem richtigen Headerfile nochmals 
ausprobiert und die Probleme sind wieder die gleichen.

von Roman T. (rthaller)


Lesenswert?

Ich habe das selbe Programm, angepasst für den internen Clock, nun mal 
auf dem eZ430-F2013 von TI laufengelassen, und da funktioniert es 
einwandfrei. Scheint wohl ein Hardware- oder Prozessorproblem zu sein.

Nochmals Danke für eure Hilfe.

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.