Forum: Mikrocontroller und Digitale Elektronik UART Sendevorgang funktioniert nicht


von Sebastian V. (na_servas)


Lesenswert?

Hallo Leute,

Ich habe ein Problem mit meinem UART Sendevorgang:
Ih füge mit sprintf eine Floatzahl an einen char string an was auch 
funktioniert. Allerdings bekomme ich dann keine UART Ausgabe.
Wenn ichsprintf ausklammere und den string "händisch" beschreibe, dann 
funktioniert es.
Den ADC Einlesevorgang habe ich auskommentiert, weil dieser bereits 
funktioniert.

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

Vielen Danke für jede Hilfe,
Sebastian :)

von spess53 (Gast)


Lesenswert?

Hi

>Meien Vermutung ist, dass der MSP nicht in den Sende-Interrupt geht.
>Aber wieso??

Also bei AVRs kommt der TX-Interrupt, wenn ein Zeichen gesendet wurde. 
Dazu muss man aber die Übertragung erst mal von 'Hand' anstoßen.

MfG Spess

von Fritz G. (fritzg)


Lesenswert?

String2 zu klein? Lass dir mal strlen ausgeben, darf maximal 11 sein.

von Karl H. (kbuchegg)


Lesenswert?

Sebastian Vvvvvv schrieb:

> Wenn ichsprintf ausklammere und den string "händisch" beschreibe.

Dann fehlt ein Satzteil.
Ein Satz, der mit 'wenn' beginnt, benötigt ein 'dann'.

Also. Was ist dann?

funktioniert es etwa dann?

> char string2[12];

mach da mal ein bischen mehr.
12 ist nicht gerade viel.
>     sprintf(string2, "I = %f", I);
Für den konstanten Textanteil sind das schon mal 4 Positionen. Dann noch 
die 0-Terminierung, machvt 5 Zeichen. 12-5 macht 7. d.h. für die Zahl 
bleiben dir 7 Stellen übrig. Wenn dein sprintf bei Fliesskommazahlen zb 
5 Nachkommastellen ausgibt, dann bist du mit 347.14500 schon aus dem 
Array draussen.

Du lebst wohl gerne gefährlich? Eine Variable mit einer derart wichtigen 
Bedeutung wie ein Zeichenzähler nennt man doch nicht 'i'. Die hat sich 
wirklich einen besseren Namen verdient. Und dann auch noch eine Variable 
namens 'I'? Das nennt man Drahtseilakt ohne Netz und doppelten Boden.

Sowas
>   if (i == sizeof string2 - 1) // TX over?

willst du auch nicht benutzen.
Du willst genau dann aufhören, wenn der String zu Ende ist und nicht 
wenn das Array aufhört. Ein String ist zu Ende, wenn du im String auf 
die 0-Terminierung stösst.
1
  if( string2[i] != '\0' )
2
    UCA0TXBUF = string2[i++];
3
  else
4
  {
5
    i = 0;  //Zähler für Übertragung reseten
6
    UC0IE &= ~UCA0TXIE; // Disable USCI_A0 TX interrupt
7
  }
8
}


(Schon alleine, dass du hier die Operation am 'i' komentieren musst, ist 
ein Indiz, dass du die Variablen anders benennen sollst. 'string2' kann 
alles sein, genauso wie 'i'. Was spricht gegen
1
  if( uartOutBuffer[ uartOutBufferCnt ] != '\0' )
2
    UCA0TXBUF = uartOutBuffer[ uartOutBufferCnt++ ]
3
  else
4
  {
5
    uartOutBufferCnt = 0;

und schon muss nichts kommentiert werden. Die Anweisung ist 
selbsterklärend und spricht für sich. Die Zeit, die du in Kommentare 
steckst, ist besser aufgehoben, wenn du vernünftige, sprechende 
Variablennamen benutzt.

von Sebastian V. (na_servas)


Lesenswert?

Die Übertragung funktioniert ja auch, wenn ich sprintf nicht verwende. 
kann es sein, dass diese Funktion zu lange braucht?

Der MSP schafft es ja die Zahl auf den String zu schreiben (im 
Debugmodus des CCS6 gesehen).
Also "will" er es nur nicht senden.

von Udo S. (urschmitt)


Lesenswert?

spess53 schrieb:
>>Meien Vermutung ist, dass der MSP nicht in den Sende-Interrupt geht.
>>Aber wieso??
>
> Also bei AVRs kommt der TX-Interrupt, wenn ein Zeichen gesendet wurde.
> Dazu muss man aber die Übertragung erst mal von 'Hand' anstoßen.

So ist normalerweise der Send Interrupt. Er wird ausgelöst wenn ein 
Zeichen gesendet wurde, damit die ISR dann das nächste Zeichen senden 
kann.

@Sebastian:
Du sendet in der IRQ ohne vorher zu prüfen ob überhaupt etwas im String 
steht.
Du schreibst in der Interruptroutine auf die Variable 'i', sie ist aber 
nicht volatile.

von spess53 (Gast)


Lesenswert?

Hi

>So ist normalerweise der Send Interrupt. Er wird ausgelöst wenn ein
>Zeichen gesendet wurde, damit die ISR dann das nächste Zeichen senden
>kann.

Aber UCA0TXBUF = string2... taucht nur im Interrupt auf. Wie soll der 
aufgerufen werde?

MfG Spess

von Sebastian V. (na_servas)


Lesenswert?

Die Interruptroutine mit dem i Counter müsste doch funktionieren, da sie 
auch mit einem anderen string funktioniert.

Gibt es denn typische Probleme, die bei sprintf auftreten=

von Karl H. (kbuchegg)


Lesenswert?

Sebastian Vvvvvv schrieb:

> Gibt es denn typische Probleme, die bei sprintf auftreten=

Ja.
Wenn dein Array zu klein ist.

Und nein: Ich seh doch im Debugger, dass da das richtige drinn steht, 
gilt nicht.
Ist das Array zu klein, bügelst du dir die unmittelbar im Speicher 
angrenzenden Variablen nieder.

Mach die 12 größer. An dieser Stelle ist während der Entwicklung 
knausrig sein der falsche Weg.

von Sebastian V. (na_servas)


Lesenswert?

Hmm auch mit einer Array-Größe von 20 funkt es nicht :/...

von not enaugh time for usci (Gast)


Lesenswert?

Wo wird denn der USCI Interrupt enabled? Wird i mit 0 initialisiert?



Aber was, wenn, dann ...
Im Timer Interrupt wird am Anfang auf den ADC gewartet und am Ende 
sprintf aufgerufen. Wie lange dauert das und wie oft kommt der 
Interrupt? Was höhere Priorität, Timer oder USCI?

von Sebastian V. (na_servas)


Lesenswert?

mit i=0 initialisieren habe ich es auch schon versucht...

also der Interrupt wirde jede ms aufgerufen.
Wie kann ich den nachsehen, wie lange diese Aktionen dauern?
Die Priorität sollte doch von selber geregelt werden oder?

Wenn nicht wäre es ideal, wenn der Timer Interrupt erst wieder aktiviert 
wird, wenn der USCI Interrupt fertig ist. Doch wie mach ich das, sodass 
anschließend der Timer Interrupt wieder dort weitermacht, wo er gestoppt 
wurde??

von not enaugh time for usci (Gast)


Lesenswert?

Wirf ein Blick auf die Timer Initialisierung, nicht auf den Kommentar.

In einer ISR sollte man nicht unnötig warten. Und, der ADC hat einen 
eigenen Interrupt.

von Sebastian V. (na_servas)


Lesenswert?

Was könnte ich da an der Initialiserung groß ändern?

Könnte ich anstatt des Warten dann einen Ablauf wie:

ISR starten -> ADC Einlesung + sprintf -> ISR Stopp -> UART Übertragung 
-> UART Ende -> ISR starten ...

einfach realisieren?

von help (Gast)


Lesenswert?

Sebastian Vvvvvv schrieb:
> Was könnte ich da an der Initialiserung groß ändern?

Der Code passt nicht zu den Kommentaren.
Zeig einmal deinen Code ohne sprintf, der funktioniert.

Sebastian Vvvvvv schrieb:
> einen Ablauf wie

- Clock initialisieren
- ADC initialisieren
- USCI initialisieren
- ADC starten
- endless loop

ADC löst Interrupt aus -> in der ADC-ISR Ausgabepuffer füllen und 
Ausgabe-Interrupt aktivieren -> beim letzten Zeichen in der USCI-ISR ADC 
starten -> ...

von Sebastian V. (na_servas)


Lesenswert?

Es lag daran, dass sprintf in der Timer-A ISR nicht fertig wurde und 
ohne Watch-Dog dies nicht erkannt wurde und der Aufruf der UART 
Übertragung nicht aufgerufen wurde.

Lösung:
-erhöhen der Prozessorfrequenz
-erhöhen von Periodendauer von Timer_A

Danke für die 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.