Forum: Mikrocontroller und Digitale Elektronik STM32F0 UART - Überschüssiges Byte erkennen


von pxc (Gast)


Lesenswert?

Hallo zusammen,

ich versuche mit dem UART1 des STM32F0C8 ein kleines Protokoll zu bauen, 
dass 4 Bytes empfängt. Dazu werden vier Bytes von mir ausgelesen und in 
ein Array geschrieben. Falls ein Byte zu viel ankommt und nicht gelesen 
wird, wird ja das RXNE (Rx not empty) Flag gesetzt und falls dann noch 
eins dazukommt und das nicht gelesene überschreiben will das ORE 
(overrun error) Flag.

Nur irgendwie kann ich die Flags nicht abfragen, bzw der µC springt 
nicht in die RXNE-Abfrage.

Meine Funktion sieht wie folgt aus:

1
Aufruf:
2
3
vGetUARTData(aRxBuffer, 4, aTimeOutErrorTx, UART_TIMEOUT_CAL);
4
5
6
7
8
Und Implementierung:
9
10
VOID vGetUARTData(USIGN8* aRxBuf, USIGN16 wDataLength, USIGN8* aErrorTx, USIGN16 wTimeout){
11
  
12
  USIGN8 i;
13
  
14
  __HAL_UART_FLUSH_DRREGISTER(&huart1);
15
  
16
        // Receive the 4 Bytes from UART
17
  if ( HAL_UART_Receive(&huart1, aRxBuf, 4, wTimeout)  == HAL_TIMEOUT) { 
18
    HAL_UART_Transmit(&huart1, aErrorTx, wDataLength, wTimeout);
19
  }
20
    
21
        // check if more than 4 Bytes were received
22
  if((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) == SET )){
23
    
24
    dwDummy = (huart1.Instance->RDR);
25
                // dismiss the RxBuffer
26
    for(i=0; i<=3; i++){aRxBuf[i] = 0xFF;}
27
  }
28
  
29
  if( __HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE) == SET ){
30
    __HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_OREF);
31
    // dismiss the RxBuffer
32
    for(i=0; i<=3; i++){aRxBuf[i] = 0xFF;}
33
  }
34
    
35
36
    
37
  // check for parity, dismiss the received frame if PE set
38
  if((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_PE) == SET)){
39
    // clear the parity error
40
    __HAL_UART_CLEAR_PEFLAG(&huart1); // clears RXNE  // hier anhalten zeigt das gesetzte RXNE flag, in der if oben springt der µC nicht rein
41
    // dismiss the RxBuffer
42
    for(i=0; i<=3; i++){aRxBuf[i] = 0xFF;}
43
  }
44
45
}



Wenn ich 5 Bytes sende (mit falscher Parity) springt der µC nach Erhalt 
in den Breakpoint vom UART_FLAG_PE. In der Registern sieht man auch, 
dass das RXNE gesetzt ist, nur hätte er schon oben in die RXNE-Abfrage 
springen sollen, oder nicht?

Weiß jemand weiter?

von Stefan K. (stefan64)


Lesenswert?

HAL_UART_Receive(&huart1, aRxBuf, 4, wTimeout)

wartet, bis 4 Bytes empfangen wurden (falls es kein TO gibt) und kommt 
dann SOFORT zurück. Falls ein 5. Byte gesendet wird, dann kommt dies 
erst viel später als Deine RXNE-Abfrage an.

Ausnahme:
Zwischen die Zeilen
1
    HAL_UART_Transmit(&huart1, aErrorTx, wDataLength, wTimeout);
2
  }
3
    
4
        // check if more than 4 Bytes were received
5
  if((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) == SET )){
funkt ein ISR oder Taskwechsel, der länger dauert als 10/fBaud.

Für die ORE-Abfrage gilt sinngemäss das Gleiche.

Wenn Du einen Breakpoint setzt, dann siehst Du wahrscheinlich die Flags, 
weil die UART-Register nach dem Breakpoint erst mit Verzögerung 
eingelesen werden.

Viele Grüße, Stefan

von W.S. (Gast)


Lesenswert?

pxc schrieb:
> ich versuche mit dem UART1 des STM32F0C8 ein kleines Protokoll zu bauen,
> dass 4 Bytes empfängt.

Du schreibst wirr.
Du kannst mit einem Stück Peripherie kein Protokoll bauen. Grundsätzlich 
nicht. Stattdessen solltest du dich hinsetzen mit einem Blatt Papier und 
Stift und dir überlegen, wie du eine geordnete Kommunikation zwischen 
zwei voneinander völlig unabhängigen Geräten zuwege bekommst - 
unabhängig vom Medium dazwischen. Bedenke, daß du niemals einfach nur 
starr darauf beharren kannst, eine bestimmte Anzahl von Zeichen zu einem 
bestimmten Zeitpunkt zu erhalten. Die Gegenstelle und das 
zwischenliegende Kabel führen nämlich ein Eigenleben. Und die Art des 
Zeichentransportes ist asynchron.

Also denk dir z.B.aus, daß dein Protokoll mit einem bestimmten Code 
eingeleitet wird, dann z.B. deine 4 bytes in Form von 8 Hex-zeichen 
(0..9, A..F) übertragen wird und dann ein Endekenner gesendet wird. 
Alternativ eine Prüfsumme. So etwas ist ein Protokoll. Natürlich kannst 
du es auch anders gestalten, z.B. UU encoded oder sonstwie. Aber aus 
jedem Zeichen solltest du erkennen können, ob es ein gültiges Zeichen 
ist und wo ein Blockanfang ist.

W.S.

von pxc (Gast)


Lesenswert?

@ W.S.
Ist mir schon klar, das ganze "Protokoll" läuft schon und von den 4 
Bytes ist eins das Startbyte und eins das Stoppbyte. Das Erkennen von 
Zusatzbytes die sich evtuell durch Störungen einschleichen könnten 
sollte nur noch ein nettes Sicherheitsfeature sein, damit das 
4-Byte-Frame dann verworfen wird und die Übertragung nochmal wiederholt 
wird.


@Stefan K.

>und kommt dann SOFORT zurück. Falls ein 5. Byte gesendet wird, dann kommt
>dies erst viel später als Deine RXNE-Abfrage an.

Das würde es zumindest erklären. Die Funktion läuft eigentlich nur in 
der Initialisierungsphase und diese ist bis auf den SysTick komplett 
uninterrupted, ein paar ms Delay nach dem Receive von 4 Bytes würden 
nicht wehtun, weil die Gegenstelle dann eh erstmal darauf wartet was der 
µC mit den Empfangenen Bytes macht. Fällt dir vielleicht noch eine 
andere/bessere Möglichkeit ein?

von pxc (Gast)


Lesenswert?

Danke, so gehts! Eben ausprobiert :-)
1
VOID vGetUARTData(USIGN8* aRxBuf, USIGN16 wDataLength, USIGN8* aErrorTx, USIGN16 wTimeout){
2
  
3
  USIGN8 i;
4
  
5
  __HAL_UART_FLUSH_DRREGISTER(&huart1);
6
  
7
  if ( HAL_UART_Receive(&huart1, aRxBuf, 4, wTimeout)  == HAL_TIMEOUT) { 
8
    HAL_UART_Transmit(&huart1, aErrorTx, wDataLength, wTimeout);
9
  }
10
    
11
  // gives the uart shift register some time to receive an additional byte
12
  HAL_Delay(3);
13
  
14
  // only works with the prior Delay 
15
  // check : Receiver not empty? Overrun Error? Parity error?
16
  if(  (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) == SET) || \
17
      (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE)   == SET) || \
18
      (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_PE)   == SET)) {   
19
      
20
    dwDummy = (huart1.Instance->RDR); // clear RXNE
21
    __HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_OREF); 
22
    __HAL_UART_CLEAR_PEFLAG(&huart1);
23
    // dismiss the RxBuffer
24
    for(i=0; i<=3; i++){aRxBuf[i] = 0xFF;}  
25
  }
26
    
27
}

von Dominik B. (odysseus1710)


Lesenswert?

Ich benutze diese Bibliotheken nicht, daher blicke ich auch nicht ganz 
dahinter, was die einzelnen Funktionen genau bewerkstelligen.

Wie Stefan bereits geschrieben hat, wartete dein Programm ja nicht 
darauf, dass noch ein Byte kommt. Es ist sehr unwahrscheinlich, dass 
genau zu dem Zeitpunkt deiner Abrage ein Byte eingetrudelt ist.

pxc schrieb:
> VOID vGetUARTData(USIGN8* aRxBuf, USIGN16 wDataLength, USIGN8* aErrorTx,
> USIGN16 wTimeout){

Soll das die ISR des UART sein?
Falls ja wird bei einem Interrupt direkt zu Beginn mit

pxc schrieb:
> __HAL_UART_FLUSH_DRREGISTER(&huart1);

wohl das Datenregister geleert und RXNE damit zurückgesetzt.

Falls das eine normale Funktion ist, könntest du nach deinen ersten 4 
Byte auf ein 5. warten (Polling):
1
while(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != SET);

Besser wäre natürlich ein Interrupt

von pxc (Gast)


Lesenswert?

@ Dominik B.
Ne, ist keine ISR. Einfach nur eine Funktion, die nach dem wegsenden von 
Daten aufgerufen wird um auf die Antwort zu warten.

Das mit FLUSH_DRREGISTER... ist natürlich fehl am Platz dort, danke für 
den Hinweis!

von Stefan K. (stefan64)


Lesenswert?

pxc schrieb:
> Das mit FLUSH_DRREGISTER...

Mit diesem Aufruf löscht Du alle ggf. vorher empfangenen Daten. Ob das 
Sinn macht, musst Du selbst entscheiden.

Dein Problem ist, dass PC und mc zueinander asynchron laufen. Das muss 
Dein Protokoll-Aufbau berücksichtigen, wenn er robust sein soll.

Auch wenn es von der Effizienz nicht optimal ist, benutze ich deshalb 
gerne ein ASCII-Protokoll. Ein Datensatz oder Befehl in eine Zeile, mit 
CR abgeschlossen. Das hat den Vorteil, dass die EndeKennung einer msg 
(hier: CR) nicht in den Msg-Daten vorkommen kann, anders als bei vielen 
Binärprotokollen. Weiterer Vorteil: auf PC-Seite reicht fürs Erste ein 
Terminal für die Kommunikation.


Viele Grüße, Stefan

P.S.:
> // gives the uart shift register some time to receive an additional
> byte
>   HAL_Delay(3);

Sowas funktioniert zwar fürs Erste, ist aber auf Dauer eine Fussangel, 
z.B. wenn später einmal die Baudrate geändert wird. Besser ist es, wenn 
das Protokoll komplett timingunabhängig von beiden Seiten ist.

von pxc (Gast)


Lesenswert?

@ Stefan K.
>Ob das Sinn macht, musst Du selbst entscheiden.

Macht es nicht, da hat wohl der Morgenkaffee noch gefehlt. Habs schon 
wieder rausgenommen. Bzw. vor das Transmit gepackt, damit beim Receiven 
auf jeden Fall nichts im RDR steht:
1
while(RxBuffer-Frames != soundso ){
2
   flush RDR
3
   Befehl mC -> PC
4
   warte auf Antwort mit vGetUARTData(...)
5
}

Da fällt mir grad noch auf, dass auch im Timeout-Fall der Rx 
sicherheitshalber zurückgesetzt werden sollte...aber danke nochmal für 
den Hinweis warum es in erster Linie nicht so hinhaute wie ich wollte :)

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.