Forum: Mikrocontroller und Digitale Elektronik MSP430 UART senden


von Sebastian V. (na_servas)


Lesenswert?

Servus Leute!

Ich habe ein Problem mit meiner UART Übertragung.
Ich lese mit einem Interrupt der jede ms aufgerufen wird 3 ADCs ein. 
Dies funktioniert auch optimal. Allerdings sollen danach die Daten über 
UART weiterübertragen werden. Ich bin gerade dabei die UART Übertragung 
aufzusetzen, finde allerdings den Fehler nicht.
Unten als c-Code habe ich das Setup und den Interrupt, der das Senden 
übernehmen sollte.
Es wird mir allerdings nichts rausgeschickt.
Die Interrupts und des Setup für die UART Übertragung habe ich von hier:
http://www.embeddedrelated.com/showarticle/420.php
Dieser Code geht auch, es ich finde nur den Fehler in meiner 
Implementierung nicht.

1
#include <msp430.h>  //einbinden der MSP430 Programmbibiothek
2
3
#define RXD BIT1
4
#define TXD BIT2
5
6
unsigned int samples[3];  //Array für ADC Speicherung
7
const char string[] = { "Halololo\r\n" };
8
unsigned int i; //Counter
9
10
int  main(void)
11
{
12
  /**************ADC10 Settings**********************/
13
    ADC10CTL0 = SREF_0 + ADC10SHT_2 + REF2_5V + REFON + MSC + ADC10ON;
14
    ADC10CTL1 = INCH_5 + ADC10DIV_0 + CONSEQ_3 + SHS_0; //Multi-channel repeated conversion starting from channel 3
15
    ADC10AE0 = BIT5 + BIT4 + BIT3;  //Kanal A5 (BIT5), A4 (Bit4) und A3 (BIT3) aktivieren
16
    ADC10DTC1 = 3;  // Alle drei Kanäle bei einer Einlesung übertragen
17
18
    /**************DO Settings**********************/
19
    //P1DIR |= 0x08;
20
    //P1DIR |= 0x10;        // P1.3, P1.4, P1.5 als DO
21
    //P1DIR |= 0x20;
22
    P2DIR |= 0xFF; // All P2.x outputs
23
    P2OUT &= 0x00; // All P2.x reset
24
25
    /**************Clock Settings**********************/
26
    WDTCTL = WDTPW + WDTHOLD;               // Watchdog deaktivieren
27
    BCSCTL1 = CALBC1_1MHZ;          // Setze Taktfrequenz der System Clock auf 1MHz
28
    DCOCTL = CALDCO_1MHZ;          // Digitally Controlled Oscilator auf 1MHz einstellen
29
30
    /***************Timer_A Settings******************/
31
    CCTL0 = CCIE;                           // Aufruf der Interruptfunktion ermöglichen
32
    CCR0 = 1000;              // Gehe in den Interrupt nach 100 Durchläufen (bei 1MHz jede 0,1ms)
33
    TACTL = TASSEL_2 + MC_1;                // TASSEL_2: Wähle ACLK (Auxiliary clock) als Quellclock
34
                                              // MC_1: Der Timer zählt bis zum Wert von CCR0
35
    _BIS_SR(LPM0_bits + GIE);               // Interrupteintritt ermöglichen
36
37
  /*************Serielle Übertragung Setup*************/
38
     UCA0CTL1 |= UCSSEL_2;  // SMCLK
39
     UCA0BR0 = 104;         // 1MHz 9600
40
     UCA0BR1 = 0;           // 1MHz 9600
41
     UCA0MCTL = UCBRS0;     // Modulation UCBRSx = 1
42
     UCA0CTL1 &= ~UCSWRST;  // **Initialize USCI state machine**
43
     //IE2 |= UCA0RXIE;       // Enable USCI_A0 RX interru
44
     __bis_SR_register(GIE);
45
46
}

und der Interrupt für das Senden.

1
#pragma vector=USCIAB0TX_VECTOR
2
__interrupt void USCI0TX_ISR(void)
3
{
4
   // TX next character
5
  UCA0TXBUF = string[i++]; // TX next character
6
  if (i == sizeof string - 1) // TX over?
7
      UC0IE &= ~UCA0TXIE; // Disable USCI_A0 TX interrupt
8
}

: Bearbeitet durch User
von Dietrich L. (dietrichl)


Lesenswert?

Wo ist die while()-Schleife im main() ?

Gruß Dietrich

von Sebastian V. (na_servas)


Lesenswert?

Die hab ich weggelassen, weil das Programm bereits in die 
Interrupt-Routine des Timer_A reinspringt.
Ich glaube das Problem liegt daran, dass er mir nicht in den 
Sende-Interrupt geht, ich komme aber nicht auf den Grund warum?!

von msp430 (Gast)


Lesenswert?

Sebastian Vvvvvv schrieb:
> Die hab ich weggelassen, weil das Programm bereits in die
> Interrupt-Routine des Timer_A reinspringt.
>
...wo ist denn der Quelltext dazu?

von dummschwaetzer (Gast)


Lesenswert?

while(1) vergessen?
uart zu langsam (9600 ist ca 1 Zeichen je ms)

von Sebastian V. (na_servas)


Lesenswert?

Das ist jetzt mal mein ganzer code. Das einlesen mit den ADCs funzt 
perfekt, nur die Übertragung funktioniert nich nicht...
Ich habe aber keinen Plan, warum er mit nicht in den Sende-Interrupt 
springt????????????
1
#include <msp430.h>  //einbinden der MSP430 Programmbibiothek
2
3
#define RXD BIT1
4
#define TXD BIT2
5
6
unsigned int samples[3];  //Array für ADC Speicherung
7
const char string[] = { "Halololo\r\n" };
8
unsigned int i; //Counter
9
10
int  main(void)
11
{
12
  /**************ADC10 Settings**********************/
13
    ADC10CTL0 = SREF_0 + ADC10SHT_2 + REF2_5V + REFON + MSC + ADC10ON;
14
    ADC10CTL1 = INCH_5 + ADC10DIV_0 + CONSEQ_3 + SHS_0; //Multi-channel repeated conversion starting from channel 3
15
    ADC10AE0 = BIT5 + BIT4 + BIT3;  //Kanal A5 (BIT5), A4 (Bit4) und A3 (BIT3) aktivieren
16
    ADC10DTC1 = 3;  // Alle drei Kanäle bei einer Einlesung übertragen
17
18
    /**************DO Settings**********************/
19
    //P1DIR |= 0x08;
20
    //P1DIR |= 0x10;        // P1.3, P1.4, P1.5 als DO
21
    //P1DIR |= 0x20;
22
    P2DIR |= 0xFF; // All P2.x outputs
23
    P2OUT &= 0x00; // All P2.x reset
24
25
    /**************Clock Settings**********************/
26
    WDTCTL = WDTPW + WDTHOLD;               // Watchdog deaktivieren
27
    DCOCTL =   0;
28
    BCSCTL1 = CALBC1_1MHZ;          // Setze Taktfrequenz der System Clock auf 1MHz
29
    DCOCTL = CALDCO_1MHZ;          // Digitally Controlled Oscilator auf 1MHz einstellen
30
31
    /***************Timer_A Settings******************/
32
    CCTL0 = CCIE;                           // Aufruf der Interruptfunktion ermöglichen
33
    CCR0 = 1000;              // Gehe in den Interrupt nach 100 Durchläufen (bei 1MHz jede 0,1ms)
34
    TACTL = TASSEL_2 + MC_1;                // TASSEL_2: Wähle ACLK (Auxiliary clock) als Quellclock
35
                                              // MC_1: Der Timer zählt bis zum Wert von CCR0
36
    _BIS_SR(LPM0_bits + GIE);               // Interrupteintritt ermöglichen
37
38
  /*************Serielle Übertragung Setup*************/
39
     P1SEL |= RXD + TXD ; // P1.1 = RXD, P1.2=TXD
40
     P1SEL2 |= RXD + TXD ; // P1.1 = RXD, P1.2=TXD
41
     UCA0CTL1 |= UCSSEL_2; // SMCLK
42
     UCA0BR0 = 0x08; // 1MHz 115200
43
     UCA0BR1 = 0x00; // 1MHz 115200
44
     UCA0MCTL = UCBRS2 + UCBRS0; // Modulation UCBRSx = 5
45
     UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
46
     __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ int until Byte RXed
47
}
48
49
// Timer A0 interrupt service routine
50
#pragma vector=TIMER0_A0_VECTOR
51
__interrupt void Timer_A (void)
52
{
53
  ADC10CTL0 &= ~ENC;
54
  while (ADC10CTL1 & BUSY);
55
  ADC10SA = (unsigned int)samples;
56
  //automatische Speicherung der Daten in das Array "sample"
57
58
  ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
59
60
  if (samples[0] > 0x384)
61
  {
62
    P1OUT |= 0x08;    //LED an Pin 1.4 einschalten
63
64
  }
65
  else
66
  {
67
    P1OUT &= ~0x08;
68
  }
69
70
  if (samples[1] > 0x384)
71
  {
72
    P1OUT |= 0x10;    //LED an Pin 1.4 einschalten
73
  }
74
  else
75
  {
76
    P1OUT &= ~0x10;
77
  }
78
79
  if (samples[2] > 0x384)
80
    {
81
      P1OUT |= 0x20;    //LED an Pin 1.4 einschalten
82
    }
83
    else
84
    {
85
      P1OUT &= ~0x20;
86
    }
87
88
  i=0;
89
  UC0IE |= UCA0TXIE; // Enable USCI_A0 TX interrupt
90
91
}
92
93
#pragma vector=USCIAB0TX_VECTOR
94
__interrupt void USCI0TX_ISR(void)
95
{
96
   // TX next character
97
  UCA0TXBUF = string[i++]; // TX next character
98
  if (i == sizeof string - 1) // TX over?
99
      UC0IE &= ~UCA0TXIE; // Disable USCI_A0 TX interrupt
100
}

: Bearbeitet durch User
von sdg (Gast)


Lesenswert?

Sebastian Vvvvvv schrieb:
> /***************Timer_A Settings******************/
>     CCTL0 = CCIE;                           // Aufruf der
> Interruptfunktion ermöglichen
>     CCR0 = 1000;              // Gehe in den Interrupt nach 100
> Durchläufen (bei 1MHz jede 0,1ms)
>     TACTL = TASSEL_2 + MC_1;                // TASSEL_2: Wähle ACLK
> (Auxiliary clock) als Quellclock
>                                               // MC_1: Der Timer zählt
> bis zum Wert von CCR0
>     _BIS_SR(LPM0_bits + GIE);               // Interrupteintritt
> ermöglichen
>
>   /*************Serielle Übertragung Setup*************/
>      P1SEL |= RXD + TXD ; // P1.1 = RXD, P1.2=TXD
>      P1SEL2 |= RXD + TXD ; // P1.1 = RXD, P1.2=TXD
>      UCA0CTL1 |= UCSSEL_2; // SMCLK
>      UCA0BR0 = 0x08; // 1MHz 115200
>      UCA0BR1 = 0x00; // 1MHz 115200
>      UCA0MCTL = UCBRS2 + UCBRS0; // Modulation UCBRSx = 5
>      UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
>      __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ int until Byte
> RXed

Kann es sein dass dein Code niemals den UART initialisierungsteil 
aufruft, weil du vorher schon in den LPM gehst?

Das ist nur eine Idee, aber durch debuggen mit Breakpoints sollte man 
das ja schnell sehen ob die UART pins überhaupt inklusive IRQ gesetzt 
sind.

Die int main() ohne while(1) sollte funktionieren, das macht TI in 
seinen C Beispielen genauso.

von msp430 (Gast)


Lesenswert?

also ich habe schon lange nichts mehr mit MSP430 gemacht, aber irgendwie 
fehlt da etwas in den Interrupt-Routinen --> jeweils das 
"Wieder-Schlafenlegen" der MCU! (oder du brauchst tatsächlich den 
Endlos-Loop in main()...)

von msp430 (Gast)


Lesenswert?

sdg schrieb:
> Die int main() ohne while(1) sollte funktionieren, das macht TI in
> seinen C Beispielen genauso.

stimmt schon, aber wie bereits gesagt, dazu muss man die MCU nach der 
Interrupt-Abarbeitung auch wieder stoppen (Schlafenlegen).

von sdg (Gast)


Lesenswert?

hab grade nochmal geschaut, in den TI Beispielen machen die das auch 
nicht explizit, ich vermute der uC weiss dass er vorher im LPM war und 
kehrt automatisch nach dem IRQ dorthin zurück.

Vielleicht auch sicherheitshalber den Watchdog mal stoppen zu Beginn, 
nicht das der immer triggert und du Phantomprobleme suchst.

von Sebastian V. (na_servas)


Lesenswert?

Servus!
Ja das problem war, das ich den Timer-Interrupt  zu früh aufgerufen habe 
und die UART Pins wurden nicht eingestellt wurden.
Vielen Dank für die Hilfe :)

: Bearbeitet durch User
von go MSP430 (Gast)


Lesenswert?

Sebastian Vvvvvv schrieb:
> _BIS_SR(LPM0_bits + GIE);               // Interrupteintritt
> ermöglichen

Der Code dahinter wird nicht mehr ausgeführt und die ganze USCI 
Initialisierung erfolgt nicht.

msp430 schrieb:
> aber wie bereits gesagt, dazu muss man die MCU nach der
> Interrupt-Abarbeitung auch wieder stoppen (Schlafenlegen).

Nein! Die Bits für den LPM stehen im PSW. Das PSW wird bei Eintritt in 
die ISR gesichert und am Ende der ISR wieder restauriert. Das ist der 
Grund, warum es ohne while(1) geht.
Nur wenn man den LPM verlassen will, muss man in der ISR die LPM Bits 
löschen: __bic_SR_on_exit(LPM0_bits);

Sebastian Vvvvvv schrieb:
> Ja das problem war

Zeig doch bitte den funktionierenden Code vollständig.

von Sebastian V. (na_servas)


Lesenswert?

Hier der ganze funktionierende Code:
1
#include <msp430.h>  //einbinden der MSP430 Programmbibiothek
2
3
#define RXD BIT1
4
#define TXD BIT2
5
6
unsigned int samples[3];  //Array für ADC Speicherung
7
const char string[] = { "Halololo\r\n" };
8
unsigned int i; //Counter
9
10
int  main(void)
11
{
12
  /**************ADC10 Settings**********************/
13
    ADC10CTL0 = SREF_0 + ADC10SHT_2 + REF2_5V + REFON + MSC + ADC10ON;
14
    ADC10CTL1 = INCH_5 + ADC10DIV_0 + CONSEQ_3 + SHS_0; //Multi-channel repeated conversion starting from channel 5->4->3
15
    ADC10AE0 = BIT5 + BIT4 + BIT3;  //Kanal A5 (BIT5), A4 (Bit4) und A3 (BIT3) aktivieren
16
    ADC10DTC1 = 3;  // Alle drei Kanäle bei einer Einlesung übertragen
17
18
    /**************DO Settings**********************/
19
    //P1DIR |= 0x08;
20
    //P1DIR |= 0x10;    // P1.3, P1.4, P1.5 als DO
21
    //P1DIR |= 0x20;
22
    P2DIR |= 0xFF; // All P2.x outputs
23
    P2OUT &= 0x00; // All P2.x reset
24
25
    /**************Clock Settings**********************/
26
    WDTCTL = WDTPW + WDTHOLD;               // Watchdog deaktivieren
27
    DCOCTL =   0;
28
    BCSCTL1 = CALBC1_1MHZ;          // Setze Taktfrequenz der System Clock auf 1MHz
29
    DCOCTL = CALDCO_1MHZ;          // Digitally Controlled Oscilator auf 1MHz einstellen
30
31
    /***************Timer_A Settings******************/
32
    CCTL0 = CCIE;                           // Aufruf der Interruptfunktion ermöglichen
33
    CCR0 = 1000;              // Gehe in den Interrupt nach 1000 Durchläufen (bei 1MHz jede 1ms)
34
    TACTL = TASSEL_2 + MC_1;                // TASSEL_2: Wähle ACLK (Auxiliary clock) als Quellclock
35
                                              // MC_1: Der Timer zählt bis zum Wert von CCR0
36
37
38
  /*************Serielle Übertragung Setup*************/
39
     P1SEL |= RXD + TXD ; // P1.1 = RXD, P1.2=TXD
40
     P1SEL2 |= RXD + TXD ; // P1.1 = RXD, P1.2=TXD
41
     UCA0CTL1 |= UCSSEL_2; // SMCLK
42
     UCA0BR0 = 0x08; // 1MHz 115200
43
     UCA0BR1 = 0x00; // 1MHz 115200
44
     UCA0MCTL = UCBRS2 + UCBRS0; // Modulation UCBRSx = 5
45
     UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
46
47
48
     _BIS_SR(LPM0_bits + GIE);               // Timer_A Interrupte aufrufen
49
}
50
51
// Timer A0 interrupt service routine
52
#pragma vector=TIMER0_A0_VECTOR
53
__interrupt void Timer_A (void)
54
{
55
  ADC10CTL0 &= ~ENC;
56
  while (ADC10CTL1 & BUSY);
57
  ADC10SA = (unsigned int)samples;  //automatische Speicherung der Daten in das Array "sample"
58
59
  ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
60
61
  if (samples[0] > 0x384)
62
  {
63
    P1OUT |= 0x08;    //LED an Pin 1.4 einschalten
64
  }
65
  else
66
  {
67
    P1OUT &= ~0x08;
68
  }
69
70
  if (samples[1] > 0x384)
71
  {
72
    P1OUT |= 0x10;    //LED an Pin 1.4 einschalten
73
  }
74
  else
75
  {
76
    P1OUT &= ~0x10;
77
  }
78
79
  if (samples[2] > 0x384)
80
    {
81
      P1OUT |= 0x20;    //LED an Pin 1.4 einschalten
82
    }
83
    else
84
    {
85
      P1OUT &= ~0x20;
86
    }
87
88
  i=0;
89
  UC0IE |= UCA0TXIE; // Enable USCI_A0 TX interrupt
90
91
}
92
93
#pragma vector=USCIAB0TX_VECTOR
94
__interrupt void USCI0TX_ISR(void)
95
{
96
   // TX next character
97
  UCA0TXBUF = string[i++]; // TX next character
98
  if (i == sizeof string - 1) // TX over?
99
      UC0IE &= ~UCA0TXIE; // Disable USCI_A0 TX interrupt
100
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Sebastian Vvvvvv schrieb:

> #pragma vector=USCIAB0TX_VECTOR
> __interrupt void USCI0TX_ISR(void)
> {
>    // TX next character
>   UCA0TXBUF = string[i++]; // TX next character
>   if (i == sizeof string - 1) // TX over?
>       UC0IE &= ~UCA0TXIE; // Disable USCI_A0 TX interrupt
> }

sizeof?

Damit wirst du noch viel Freude haben. Vor allen Dingen bei variabel 
langen Strings. :-)

Warum nicht einfach: Wenn der String zu Ende ist, wird der Interrupt 
terminiert.
1
#pragma vector=USCIAB0TX_VECTOR
2
__interrupt void USCI0TX_ISR(void)
3
{
4
  if( string[i] )
5
    UCA0TXBUF = string[i++]; // TX next character
6
  else
7
    UC0IE &= ~UCA0TXIE; // Disable USCI_A0 TX interrupt
8
}

: Bearbeitet durch User
von Sebastian V. (na_servas)


Lesenswert?

Ich habe die UART Befehle und Interrupts aus einem Tutorial aus dem Inet 
und war froh, das es halt so eonfach funktioniert.
Zitat Lehrer: "Programmieren is kopieren von Grundprogrammen" oder so 
ähnlich :)
Danach bin ich eben vorgegangen.
Also vielen Dank für die Verbesserungsvorschläge :)

: Bearbeitet durch User
von goMSP (Gast)


Lesenswert?

Karl Heinz schrieb:
> sizeof?
>
> Damit wirst du noch viel Freude haben. Vor allen Dingen bei variabel
> langen Strings. :-)

Mmmhh, hier passt das doch:
Sebastian Vvvvvv schrieb:
> const char string[] = { "Halololo\r\n" };

Der wird nicht länger oder kürzer. ;-)

Sebastian Vvvvvv schrieb:
> Also vielen Dank für die Verbesserungsvorschläge

Da hätt ich auch noch einen:

Sebastian Vvvvvv schrieb:
> CCTL0 = CCIE;                           // Aufruf der
> Interruptfunktion ermöglichen
>     CCR0 = 1000;              // Gehe in den Interrupt nach 1000
> Durchläufen (bei 1MHz jede 1ms)
>     TACTL = TASSEL_2 + MC_1;                // TASSEL_2: Wähle ACLK
> (Auxiliary clock) als Quellclock
>                                               // MC_1: Der Timer zählt
> bis zum Wert von CCR0

Die Reihenfolge sollte angepasst werden:
CCR0 = 1000;
TACTL = TASSEL_2 | MC_1 | TACLR;
CCTL0 = CCIE;
damit das Ganze kontrolliert startet.

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.