mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik MSP430: Timer_A Output Units


Autor: Thaller (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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
#include "io430.h"
#include "intrinsics.h"

#define TXD 0x02                                            // P1.1 für TX
#define LED 0x80                                            // P1.7 für LED
#define Time 8000                                           // 1 ms

unsigned char counter = 0;

void main (void)
{  
  WDTCTL = WDTPW + WDTHOLD;
 
  // 8 MHz für MCLK, SMCLK
  BCSCTL2 = SELM_2 + SELS;
  BCSCTL1 &= ~XT2OFF;

  TACTL = TASSEL_2 + MC_2;                                  // SMCL für TA, Cont. Mode
  TACTL &= ~TAIE;
  TACCTL0 = OUT;                                            // Mark auf TX
  
  P1SEL = TXD;                                              // TA OUT0 für TX
  P1DIR = TXD + LED;
  P1OUT |= LED;                                            // LED ein

  __bis_SR_register(GIE);                                   //Gener Interrupt Enable
  
  
  TACCR0 = TAR + Time;                                      // Zeitpunkt des nächsten Interrupts
  TACCTL0 |= OUTMOD_1 + CCIE;                               // Set Mode, Compare Interrupt Enable
  
  while(1);
}


// Timer A0 interrupt service routine
#pragma vector = TIMERA0_VECTOR
__interrupt void Timer_A (void)
{                               
  TACCR0 += Time;                                           // Nächstn Interrupt festlegen

  if(counter % 2)                                           // Toggle
  {
    TACCTL0 |= OUTMOD_7;                                    // Über Out Mode 7
    TACCTL0 &= (0xFF1F | OUTMOD_5);                         // in Out Mode Reset wechseln
                    
  }
  else
  {
    TACCTL0 |= OUTMOD_7;                                  // Über Out Mode 7
    TACCTL0 &= (0xFF1F | OUTMOD_1);                       // in Out Mode Set wechseln       
  }
  
  P1OUT ^= LED;                                            // LED invertieren
 
  counter++;
}

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Roman Thaller (rthaller)
Datum:

Bewertung
0 lesenswert
nicht 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.

//******************************************************************************
#include "io430.h"
#include "intrinsics.h"

#define TXD   0x02                          // TXD on P1.1


#define Bitime_5  417                        // ~ 0.5 bit length
#define Bitime    833                       // ~ 9604 baud

unsigned int RXTXData;
unsigned char BitCnt;

void TX_Byte(void);

void main (void)
{
  WDTCTL = WDTPW + WDTHOLD;
 
  // 8 MHz für MCLK, SMCLK
  BCSCTL2 = SELM_2 + SELS;
  BCSCTL1 &= ~XT2OFF;

  TACTL = TASSEL_2 + MC_2 + TACLR;                                  // SMCL für TA, Cont. Mode
  TACCTL0 = OUT;                                            // Mark auf TX
  
  P1SEL = TXD;                                              // TA OUT0 für TX
  P1DIR = TXD;

  __bis_SR_register(GIE);
  
  while(1)
  {
    RXTXData =64;
    TX_Byte();                                // TX Back RXed Byte Received
  }
}

// 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 = OUTMOD_0 + CCIE;                   // TXD = mark = idle
  while ( TACCTL0 & CCIE );                   // Wait for TX completion
}

// Timer A0 interrupt service routine
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
  TACCR0 += Bitime;                           // Add Offset to CCR0

  if ( BitCnt == 0)
    TACCTL0 &= ~ CCIE;                        // All bits TXed, disable interrupt
  else
  {
    TACCTL0 |=  OUTMOD_2;                    // TX Space
    if (RXTXData & 0x01)
      TACCTL0 &= ~ OUTMOD_2;                   // TX Mark
    RXTXData = RXTXData >> 1;
    BitCnt --;
  }
}



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.

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich hab das im Prinzip gtenauso, und klappt zuverlässig:
#define Bitime_5  27                      // ~ 0.5 bit length + small adjustment
#define Bitime    52 

volatile unsigned char RX_Bytes[16];
unsigned char charcount;

unsigned int RXTXData;
unsigned char BitCnt;
extern volatile unsigned int SYSTEM_FLAGS;

volatile unsigned int TAR_Value;

// Function Transmits Character from RXTXData Buffer

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

// Function Readies UART to Receive Character into RXTXData Buffer
void RX_Ready (void)
{
  BitCnt = 0x8;                             // Load Bit counter
  CCTL0 = CCIS0 + OUTMOD0 + CM1 + CAP + CCIE;   // Sync, Neg Edge, Cap
}

interrupt (TIMERA0_VECTOR) Timer_A (void)
{
CCR0 += Bitime;                           // Add Offset to CCR0

// RX
  if (CCTL0 & CCIS0)                        // RX on CCI0B?
  {
    if( CCTL0 & CAP )                       // Capture mode = start bit edge
    {
    CCTL0 &= ~ CAP;                         // Switch from capture to compare mode
    CCR0 += Bitime_5;
    _BIC_SR_IRQ(SCG1 + SCG0);               // DCO remains on after reti
    }
    else
    {
    RXTXData = RXTXData >> 1;
      if (CCTL0 & SCCI)                     // Get bit waiting in receive latch
      RXTXData |= 0x80;
      BitCnt --;                            // All bits RXed?
      if ( BitCnt == 0)
//>>>>>>>>>> Decode of Received Byte Here <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      {
        CCTL0 &= ~ CCIE;                      // All bits RXed, disable interrupt
    RX_Bytes[charcount] = RXTXData;
    if((RXTXData == 0x0d))
    {
      charcount = 0;
      SYSTEM_FLAGS |= COMMAND_COMPLETE;
    }
    else charcount++;
    SYSTEM_FLAGS |= CHAR_RECEIVED;
            
      //_BIC_SR_IRQ(LPM0_bits);               // Clear LPM3 bits from 0(SR)
      }
//>>>>>>>>>> Decode of Received Byte Here <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    }
  }
// TX
  else
  {
    if ( BitCnt == 0)
    CCTL0 &= ~ CCIE;                        // All bits TXed, disable interrupt
    else
    {
      CCTL0 |=  OUTMOD2;                    // TX Space
      if (RXTXData & 0x01)
      CCTL0 &= ~ OUTMOD2;                   // TX Mark
      RXTXData = RXTXData >> 1;
      BitCnt --;
    }
  }
}

void SendString(unsigned char *string)
{
  while(*string)
  {
    RXTXData = *string;
    TX_Byte();
    string++;
  }
}

int putchar(int c)
{
  RXTXData = (unsigned char)c;
  TX_Byte();
  
  return 0;
}

void PutSingleChar(int c)
{
  RXTXData = (unsigned char)c;
  TX_Byte();
}

Hab einen 6MHz Quarz dran und benutze den Timer A so:
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.

Autor: Roman Thaller (rthaller)
Datum:

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

Autor: Bernhard (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Roman Thaller (rthaller)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: szimmi (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Roman Thaller (rthaller)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Roman Thaller (rthaller)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Roman Thaller (rthaller)
Datum:

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

Autor: Roman Thaller (rthaller)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.