Forum: Mikrocontroller und Digitale Elektronik UART: Endlosschleife: Lese-Buffer leeren?


von Roland N. (eroli)


Lesenswert?

Hallo zusammen,

ich weiß nicht genau wieso, aber meine Sende-Routine endet immer wieder 
in einer Endlosschleife.

Ich initialisiere den UART so:
1
  UCA0CTL1 = UCSWRST + UC7BIT + UCSSEL_1;  
2
  UCA0CTL0 = UCPEN + UCPAR;   // Parity enabled, Even Parity
3
  
4
  // BRCLK Baudrate
5
  //---------------
6
  UCA0BR0 = 0x03;                           // 32k/2400 - 13.65
7
  UCA0BR1 = 0x00;                           //
8
  UCA0MCTL = 0x06;                          // Modulation
9
  
10
  UCA0CTL1 &=~ UCSWRST;                     // Initialize USART state machine
11
  IE2 |= UCA0RXIE;                          // Enable USART1 TX interrupt --> ISR bei Empfang

Die Sende und Empfangsroutine sieht dann so aus:
1
#pragma vector = USCIAB0RX_VECTOR
2
__interrupt void USCIAB0RX_ISR(void)
3
{
4
    if (UCA0RXBUF == ('1'))
5
    {
6
      Uart_SendText("End");
7
      IE2 &= ~UCA0RXIE;
8
      UCA0TXBUF = 0;
9
      LeaveNode();
10
      m_Uart = FALSE;
11
    }
12
    else
13
    {
14
      // Echo received char
15
      while(!(IFG2&UCA0TXIFG));  
16
      UCA0TXBUF = UCA0RXBUF;
17
      IFG2 = 0;
18
    }
19
}

Auf gut Deutsch: Wenn eine "1" empfangen wird, wird der Empfang 
eingestellt. Andernfalls soll das empfangene Zeichen einfach nur 
zurückgegeben werden.

Das Resultat ist nun leider, dass der uC endlos lang sendet und ich 
damit nicht wirklich was anfangen kann.

Meine Vermutung ist, dass UCA0RXBUF nicht geleert wird. Muss ich das 
selber machen?

Gibt es andere schöne Routinen, die nicht in einer Endlosschleife enden?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Roland M. schrieb:
> Meine Vermutung ist, dass UCA0RXBUF nicht geleert wird.
Du kannst den nicht LEEREN. Da steht immer irgendwas drin...  :-/

> Muss ich das selber machen?
Ob ein Zeichen über die serielle Schnitte angekommen ist, siehst du an 
einem Empfangsflag in einem Statusregister.

Wenn du sagen würdest, um welchen uC sich das da handelt, könnte dir 
sicher noch jemand den Namen des Registers sagen...

BTW: warum nimmst du nicht einfach eine fertige Empfangsreoutine? Jeder 
Hersteller hat da ein paar Beispielprogramme z.B. in einer App-Note zur 
Verfügung...

von Roland N. (eroli)


Lesenswert?

Es handelt sich um einen MSP430FG4618.

Ich habe bisher keine fertige Empfangsroutine gefunden, werde aber mal 
die Application Notes durchwühlen...


EDIT:

So funktioniert die ROutine noch und liefert mir wunderbar ein Echo...
1
#pragma vector = USCIAB0RX_VECTOR
2
__interrupt void USCIAB0RX_ISR(void)
3
{
4
    while (!(IFG2 & UCA0TXIFG))
5
    {  }                                        // USART1 TX buffer ready?
6
    UCA0TXBUF = UCA0RXBUF;
7
}

Wenn ich sie nur leicht abändere, sodass ich eine 2 kriegen will, wenn 
ich eine 1 sende, dann kriege ich schon wieder nur Datenmüll raus OBWOHL 
ich doch noch nichtmals die 1 gesendet habe...
1
#pragma vector = USCIAB0RX_VECTOR
2
__interrupt void USCIAB0RX_ISR(void)
3
{
4
    while (!(IFG2 & UCA0TXIFG))
5
    {  }                                        // USART1 TX buffer ready?
6
    char byte = UCA0RXBUF;
7
    byte += 1;
8
    UCA0TXBUF = byte;
9
}

Wirklich, ich blicke da nicht durch...

von Roland N. (eroli)


Lesenswert?

Kann mir da sonst keiner mehr etwas zu sagen? :-(

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Roland M. schrieb:
> Wenn ich sie nur leicht abändere, sodass ich eine 2 kriegen will, wenn
> ich eine 1 sende, dann kriege ich schon wieder nur Datenmüll raus OBWOHL
> ich doch noch nichtmals die 1 gesendet habe...
Von wo aus sendest du? Vom PC aus? Drückst du zwischendurch auf Enter?

> __interrupt void USCIAB0RX_ISR(void)
> {
>     while (!(IFG2 & UCA0TXIFG))
>     {}
Man wartet nicht in einem Interupt. Auf gar nichts!
Du kannst im Interrupt die Daten auslesen, zwischenspeichern und ein 
Flag setzen. Und dann in der Main-Loop das Flag überprüfen, und wenn es 
gesetzt ist, die Daten zurückschicken.

von Roland N. (eroli)


Lesenswert?

Gut, dann werde ich das morgen früh mal abändern und aus dem Interrupt 
eine Funktion aufrufen, die dann das empfangene Byte bearbeitet.

Aber ändert das überhaupt etwas?

Wenn ich aus dem Interrupt eine Funktion aufrufe und diese dann wartet?

Also hab ich dich richtig verstanden, wenn ich keine Funktion aufrufen 
soll, sondern in der MainLoop das "Ereignis" abarbeiten soll, ja?

von M. K. (sylaina)


Lesenswert?

Roland M. schrieb:
> Wenn ich sie nur leicht abändere, sodass ich eine 2 kriegen will, wenn
> ich eine 1 sende, dann kriege ich schon wieder nur Datenmüll raus OBWOHL
> ich doch noch nichtmals die 1 gesendet habe...
> #pragma vector = USCIAB0RX_VECTOR
> __interrupt void USCIAB0RX_ISR(void)
> {
>     while (!(IFG2 & UCA0TXIFG))
>     {  }                                        // USART1 TX buffer ready?
>     char byte = UCA0RXBUF;
>     byte += 1;
>     UCA0TXBUF = byte;
> }

Versuch mal den Empfangsbuffer als erstes zu leeren. Wie Lothar schon 
sagte: In ner Interruptroutine wartet man auf nichts, dafür gibts main() 
und co ;) .
Problem ist schonmal, dass du erst wartest, dass der Sendebuffer leer 
ist bevor du den Empfangsbuffer, der den Interrupt ausgelöst hat, 
sicherst. Das ist aber eigentlich das Erste, das man hier sichert (und 
das sollte so schnell wie möglich passieren, deshalb die erste Aktion im 
Interrupt eigentlich). Wenn du zu lange nämlich damit wartest kanns 
passieren, dass dir nachfolgende Zeichen den Empfangsbuffer versaun.

von M. K. (sylaina)


Lesenswert?

Roland M. schrieb:
> Wenn ich aus dem Interrupt eine Funktion aufrufe und diese dann wartet?

Dann kannste auch gleich im Interrupt warten, ist ja das gleiche in 
gruen.

Schau dir mal Ring-Buffers an. Normalerweise (so mach ichs immer) sicher 
ich die Zeichen im Ringbuffer und bearbeite außerhalb des Interrupts 
diesen.

von Roland N. (eroli)


Lesenswert?

Schreibst du die Daten direkt im Interrupt in den Ringbuffer oder erst 
in eine variable und dann in der main in den Buffer?

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.