Forum: Mikrocontroller und Digitale Elektronik Problem mit UART bei STM32 (UART_WaitOnFlagUntilTimeout)


von Oz z. (ozzy)


Lesenswert?

Hallo, ich sitze hier seit geraumer Zeit an einem Problem, bei dem ich 
einfach nicht weiterkomme. Also, ich möchte über UART von einem STM32 
mit einem anderen Modul kommunizieren. Dafür habe ich eine 
Statemaschine, die die empfangenen Daten verarbeitet. Nach jedem Byte 
wird dieses in einen Ringbuffer geschrieben:
1
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
2
{
3
  if (huart == &uart3)
4
  {
5
   fFifoIn( &RX_BUFFER, RX_UART4_Buffer[0]);
6
   HAL_UART_Receive_IT( &huart4, RX_UART4_Buffer, RX_BUFFER_SIZE);
7
  }
8
}
Damit der Interrupt dann wieder ausgelöst wird, aktiviere ich ihn 
wieder, so wie hier beschrieben: 
https://riptutorial.com/de/stm32/example/29940/echoanwendung---hal-bibliothek)

Das Senden geschieht über
1
HAL_UART_Transmit
.

Häufig funktioniert das auch, aber manchmal passiert augenscheinlich gar 
nichts. Mit dem Debugger konnte ich feststellen, dass er in diesen 
Fällen in der Funktion
1
UART_WaitOnFlagUntilTimeout
 für immer feststeckt. MIt dem Scope habe ich gesehen, dass der STM32 
aber korrekt die Daten rausschickt, und das Modul auch korrekt 
antwortet. Diese Antwort wird allerdings nichts verarbeitet. Hat da 
jemand von Euch eine Idee?

Vielen Dank im Voraus,
Ozzy

von Stefan F. (Gast)


Lesenswert?

Kann es sein, dass deine Interruptroutine zu langsam ist und du daher 
Zeichen verpasst?

Versuche das ganze mal mit einer erheblich geringeren Baudrate, das 
müsste dann ja besser klappen, wenn ich richtig geraten habe.

von Oz z. (ozzy)


Lesenswert?

Moin,

Ich glaube eigentlich nicht, dass das das Problem ist. Wenn ich Zeichen 
verpassen würde, würde das meine Statemachine händeln und mir anzeigen, 
Ich hänge wohl wirklich in dieser WaitOnFlagUntilTimeout fest. Ich habe 
eher die Vermutung dass da ein Problem auftritt wenn er gleichzeitig 
senden und empfangen will. Aber ich finde da nichts, wie ich das beheben 
kann...

von Ingo Less (Gast)


Lesenswert?

Funktioniert das Timeout nicht? Das soll genau das, ein Hängenbleiben, 
verhindern

von STK500-Besitzer (Gast)


Lesenswert?

Deine If-Abfrage prüft auf Usart3, aber du verwendest usart4?

Etwas mehr (kompilierbarer) Code wäre gar nicht schlecht.

von Harry L. (mysth)


Angehängte Dateien:

Lesenswert?

Ich hab dir mal einen funktionierenden Code zum Einlesen einer Zeile 
über die Serielle mit einfachem Zeileneditor angehängt.
Der nutzt ebenfalls HAL und ist genau wie deiner Interupt-gesteuert.

Vielleicht hilft dir das  weiter.

von W.S. (Gast)


Lesenswert?

Oz z. schrieb:
> Ich habe
> eher die Vermutung dass da ein Problem auftritt wenn er gleichzeitig
> senden und empfangen will.

Aber genau das ist doch die Normalität.

Also mal ganz einfach gesagt: Der UART gibt einen Interrupt sowohl beim 
Leersein des Sendepuffers (statisch!) als auch beim Vollsein/werden des 
Empfangspuffers. Deine Interrupt-Routine muß auf beides gefaßt sein 
und beide Dinge behandeln.

Normalerweise macht man das so, daß man beide Datenströme (Rx und Tx) in 
passenden Ringpuffern im Lowlevel-Treiber zwischenpuffert, so daß der 
eigentliche transfer in beide Richtungen rein interruptgesteuert geht.

Wenn am Rx nix ankommt, gibt's eben nix zu lesen, das ist normal.

Wenn es aber für den Tx nix zu senden gibt, würde er jedoch immer wieder 
einen Int werfen.

Also muß man in der Interrupt-Routine den Int bei Leerlaufen des Tx 
abschalten, wenn es nichts zu senden gibt - und folgerichtig muß man 
diesen Int wieder freigeben, nachdem man ein Zeichen in den 
Sende-Ringpuffer geschrieben hat.

Ich halte das Führen von Puffern in beiden Richtungen für essenziell - 
es sei denn, man hat im Rx-Zweig bereits einen ausreichenden Fifo in der 
Hardware. Dann kann man auf den Rx-Puffer im Lowlevel-Treiber 
verzichten.

Lies mal dieses zum Vergleich:
1
__irq void USART1_IRQHandler (void)
2
{ char c;
3
  int  i, j;
4
5
  if (USART1_ISR & ((1<<5)|(1<<3)))     // RX: Zeichen empfangen oder Overrun
6
  { c = USART1_RDR;
7
    i = U1Buf.InWP;
8
    j = (i+1) & (IBLEN-1);
9
    if (j!=U1Buf.InRP)
10
    { U1Buf.InBuf[i] = c;
11
      U1Buf.InWP = j;
12
    }
13
  }
14
15
  if  (USART1_ISR & (1<<7))              // TX: Sendepuffer leer geworden
16
  { i = U1Buf.OutRP;
17
    if (i!=U1Buf.OutWP)                  // ob es was zu senden gibt
18
    { USART1_TDR  = U1Buf.OutBuf[i];
19
      U1Buf.OutRP = (i+1) & (OBLEN-1);
20
    }
21
    else
22
    { USART1_CR1 &= ~(3<<6);             // nö, TXEIE und TCIE ausschalten
23
    }
24
  }
25
}
26
27
/* Zeichen senden */
28
char V24Char_Out1 (char c)
29
{ long L;
30
  int i, j;
31
32
  i = U1Buf.OutWP;
33
  j = (i+1) & (OBLEN-1);
34
35
  L = Ticks + 100;              // 100*10 ms Tick vom SysTick = 1 sec
36
  while (j == U1Buf.OutRP);     // wenn Puffer voll
37
  { if (L < Ticks) return 0;    // Timeout
38
  }
39
40
  // Puffer nicht voll, also Zeichen einstapeln
41
  U1Buf.OutBuf[i] = c;
42
  U1Buf.OutWP = j;
43
  USART1_CR1 |= (1<<7);         // Sendeinterrupt einschalten
44
  return c;
45
}

Mit sowas klappt das ganze. Wenn du jedoch sowas wie HAL etc. benutzt, 
dann müßtest du intensiv darin nachschauen, was dort tatsächlich so 
alles mit den diversen HW-Registern angestellt wird. Ich habe nämlich 
den Eindruck, daß der Fehler im nicht ganz richtigen Zusammenspiel 
zwischen HAL und deinem Code liegt.

Mein Rat: Mach es einfacher und übersichtlicher als mit HAL und 
Konsorten, sonst kommst du aus dem Debuggen nicht heraus.

W.S.

von Oz z. (ozzy)


Lesenswert?

Hallo,

entschuldigt bitte, dass ich mich jetzt erst zurückmelde...
Vielen Dank für Eure ganzen Antworten und Überlegungen!!!
Ich habe mich nun an dem Beispiel von Harry L. orientiert und nun läuft 
alles ohne Probleme!

Vielen Dank noch einmal!

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.