Forum: Mikrocontroller und Digitale Elektronik STM32F103 - UART Loopback- die eigene Sendung empfangen..


von Jens R. (tecdroid)


Lesenswert?

Hi,
alsooo ich habe einen STM32F103 (BluePill) und möchte in ein Programm 
einen einfachen Test für die UARTs implementieren. Die Idee dahinter 
ist, dass ich Rx und Tx brücke, dann ein paar Byte raus sende und wieder 
empfange.. Vom Prinzip her hätte ich vermutet, dass sowas in der 
Richtung geht:

uint8_t out[] = {0xff,0x55,0x66};

uint8_t in[3];

HAL_UART_Transmit(uart, out, 3, 100);

HAL_UART_Receive(uart, in, 3);

Was ich dabei empfange ist aber großes Ka**.. :-( Wie gehts denn nun 
tatsächlich? Anders herum wäre es sicher einfacher aber der Controller 
soll ja seine Hardware selbst testen..

Grundsätzlich gibt es scheinbar die Funktion HAL_USART_TransmitReceive() 
aaaaber ich finde im STMCube keine Implementierung dafür..

von Steve van de Grens (roehrmond)


Lesenswert?

Jens R. schrieb:
> Wie gehts denn nun tatsächlich?

Ohne die Doku und Beispiele der HAL zu studieren empfehle ich, sie nicht 
zu benutzen.

Es geht ja auch ohne. Siehe meine Anleitung http://stefanfrings.de/stm32

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


Lesenswert?

Steve van de Grens schrieb:
> Ohne die Doku und Beispiele der HAL zu studieren empfehle ich, sie nicht
> zu benutzen.
Die HAL ist gerade im Bereich UART unheimlich eigenartig aufgebaut. Die 
dürfte wohl ein frisch studierter Schulabgänger programimert haben. Ich 
schließe mich diener Empfehlung vollumfänglich an.

Jens R. schrieb:
> Was ich dabei empfange ist aber großes Ka**
Und was passiert tstsächlich auf dem Bus? Zum Debuggen serieller Busse 
ist ein Speicheroszi oder ein Logikanalyzer zwingend.

Jens R. schrieb:
> Die Idee dahinter ist, dass ich Rx und Tx brücke, dann ein paar Byte
> raus sende und wieder empfange...
Wie wäre es, einfach nur 1 Byte zu senden und dann sofort im 
RX-Datenregister zu schauen, was empfangen wurde?

von Jens R. (tecdroid)


Lesenswert?

Lothar M. schrieb:
> Steve van de Grens schrieb:
>> Ohne die Doku und Beispiele der HAL zu studieren empfehle ich, sie nicht
>> zu benutzen.
> Die HAL ist gerade im Bereich UART unheimlich eigenartig aufgebaut. Die
> dürfte wohl ein frisch studierter Schulabgänger programimert haben. Ich
> schließe mich diener Empfehlung vollumfänglich an.

Ich hätt tatsächlich auch lieber was anderes benutzt aber wenn man schon 
Vorgaben hat..

> Jens R. schrieb:
>> Was ich dabei empfange ist aber großes Ka**
> Und was passiert tstsächlich auf dem Bus? Zum Debuggen serieller Busse
> ist ein Speicheroszi oder ein Logikanalyzer zwingend.

Erst mal gehts ja tatsächlich nur um die Drahtbrücke. Wenn das nicht das 
gewünschte Ergebnis liefert brauch ich die Hardware die dann dran soll 
gar nicht erst debuggen ;)

> Jens R. schrieb:
>> Die Idee dahinter ist, dass ich Rx und Tx brücke, dann ein paar Byte
>> raus sende und wieder empfange...
> Wie wäre es, einfach nur 1 Byte zu senden und dann sofort im
> RX-Datenregister zu schauen, was empfangen wurde?

Der Hinweis ist aber Gold wert. Muss nur noch schauen wie ich an das 
Register komme. Die UART_HandleTypeDef hat da einen Buffer.. mal schauen 
wie man den nutzt..

von Steve van de Grens (roehrmond)


Lesenswert?

Jens R. schrieb:
> Muss nur noch schauen wie ich an das Register komme.

Indem du es direkt liest, ohne HAL. Orientiere dich an meinem Beispiel.

von J. S. (jojos)


Lesenswert?

Die HAL UART Funktionen funktionieren, habe ich schon zigfach in allen 
Varianten eingesetzt.

von Harald K. (kirnbichler)


Lesenswert?

J. S. schrieb:
> Die HAL UART Funktionen funktionieren

Sicher, nur wie? Stellen die interruptgesteuertes Senden und Empfangen 
zur Verfügung?

Wartet HAL_UART_Transmit, bis das letzte Byte auch wirklich gesendet 
wurde, oder kehrt es sofort nach dem Eintragen der Bytes in einen 
(hypothetischen) Sendepuffer zurück?

Woher holt HAL_UART_Receive die Daten? Aus einem (hypothetischen) 
Empfangspuffer, den ein Interrupthandler im Hintergrund mit Daten 
befüllt?

Was macht es, wenn im Puffer zum Zeitpunkt des Aufrufs noch nicht genug 
Daten verfügbar sind? Wartet es oder kehrt es mit einem Fehlercode 
zurück?

von J. S. (jojos)


Lesenswert?

Eben, HAL kann keine Gedanken lesen. Das sind low level Funktionen.
Wer soll nach dem Senden wo wieviele Zeichen wie speichern? Um Fifo oder 
blockweises Speichern muss man sich selber kümmern.

von Harry L. (mysth)


Lesenswert?

Lothar M. schrieb:
> Die HAL ist gerade im Bereich UART unheimlich eigenartig aufgebaut. Die
> dürfte wohl ein frisch studierter Schulabgänger programimert haben. Ich
> schließe mich diener Empfehlung vollumfänglich an.

Das Gegenteil ist der Fall.
Gerade die UARTs funktionieren hervorragend mit HAL.
Man muss es nur einmal verstehen.

Hier habe ich gezeigt, wie man das macht:
Beitrag "[STM32/HAL] simples U(S)ART-Library"

von Wastl (hartundweichware)


Lesenswert?

Jens R. schrieb:
> HAL_UART_Transmit(uart, out, 3, 100);
>
> HAL_UART_Receive(uart, in, 3);

Ohne es wirklich ausprobiert zu haben: das sollte so nicht
funktionieren da der UART keine 3 Bytes puffern kann wenn
3 Bytes gesendet wurden. Da müsste mindestens ein Receive-
Interrupthandler implementiert sein .....

von Harald K. (kirnbichler)


Lesenswert?

Wastl schrieb:
> Da müsste mindestens ein Receive-
> Interrupthandler implementiert sein

Hätte ja sein, daß das ein anständiger HAL-Treiber macht ...

von Harry L. (mysth)


Lesenswert?

Wastl schrieb:
> Da müsste mindestens ein Receive-
> Interrupthandler implementiert sein .....

So ist es!
Genau deshalb gibt es HAL_UART_ReceiveIT

von Wastl (hartundweichware)


Lesenswert?

Ich sehe gerade dass beim Generieren des UART Codes mit HAL
ein Interrupt-Handler installiert wird (werden kann) der von

<stm32f1xx_it.c>

auf die ausführliche Behandlung in

<stm32f1xx_hal_uart.c>

springt. Damit sollte dann auch die lückenlose Abarbeitung
von empfangenen Daten abgedeckt sein (was ich in diesem HAL
Wust nicht überprüft habe).

Mit HAL hat man sich zwar der Sorge entleigt den Empfang von
Daten im Low-Level zu handlen aber das Ganze muss langsamer,
umständlicher sein da die HAL-Implementierung eine ganze
Menge von verschiedenen Fällen abdecken muss. Und der Code
wird sicher auch umfangreicher sein als eine selbstgestrickte
LL-Implementierung.

von Harry L. (mysth)


Lesenswert?

Wastl schrieb:
> Ich sehe gerade dass beim Generieren des UART Codes mit HAL
> ein Interrupt-Handler installiert wird

Schau dir doch einfach meinen Code mal an!

Wastl schrieb:
> Und der Code
> wird sicher auch umfangreicher sein als eine selbstgestrickte
> LL-Implementierung.

Die Anzahl der CodeZeilen hat rein gar nichts mit der Grösse des 
generierten Binärcode zu tun.
Ich glaube kaum, daß du das händisch wirklich deutlich kleiner 
hinbekommst, aber ganz sicher wird es deutlich unflexibler.

Ich hatte auch mit höheren Baudraten (>1Mbit) nie Probleme mit dem 
HAL-Code.

Mag sein, daß du mit Nutzung der LL-Funktionen nochmal 20% im Binärcode 
einsparen kannst, aber das kann man bei der gesamten Grösse des 
UART-Code (ca. 1-2 kByte) eigentlich vernachlässigen.
Der Code für die Ringbuffer -den man praktisch immer zusätzlich braucht- 
ist meist genauso groß oder grösser als der HAL-Code für die 
Grundfunktionen.

: Bearbeitet durch User
von Jens R. (tecdroid)


Lesenswert?

Wie gesagt, wahrscheinlich wäre die Funktion HAL_USART_TransmitReceive() 
genau das was ich bräuchte aber die HAL_USART*-Funktionen scheinen für 
den STM32F103 nicht wirklich zu existieren.
.. Insofern probier ichs dann wohl doch Low Level.. @Harry, Steve danke 
für die Links, ich schau da gerade mal rein... Ich mein, auf dem AVR 
hätt ich das in 2 Minuten gebaut.. auf ARM bin ich halt noch recht neu..

von Wastl (hartundweichware)


Lesenswert?

Jens R. schrieb:
> Ich mein, auf dem AVR hätt ich das in 2 Minuten gebaut.

Auf dem AVR hättest du dich auch um das Puffern durch
Interrupt-Receive-Handling kümmern müssen. Wenn du schon
HAL verwendest dann muss das auch berücksichtigt werden.

von Jens R. (tecdroid)


Lesenswert?

Soo, ich hab jetzt tatsächlich einfach nur die Register beschrieben und 
gelesen, das tut auch.
Jaaa, man müsste eiiiigentlich mehr machen aber es geht hier lediglich 
um den Test der angeschlossenen Peripherie, was ca. ein Mal passiert. Da 
will ich nicht noch mehr Zeit investieren..

Hier mal meine Lösung:

> uint32_t uart_txrx(UART_HandleTypeDef *uart, uint8_t data) {
>  /**
>   * Sendet und empfängt ein Byte. Der Empfangene Wert wird
>   * auf voller Registerbreite zurück gegeben.
>   */
>  //HAL_UART_Transmit(uart, &data, 1, 100);
>  if (__HAL_UART_GET_FLAG(uart, UART_FLAG_RXNE)) {
>    // leere das RX-Register
>    uint8_t x = 0;
>    x = uart->Instance->DR - x;
>  }
>  // sende sowie die Leitung frei ist
>  while (!__HAL_UART_GET_FLAG(uart, UART_FLAG_TXE));
>  uart->Instance->DR = data;
>  // empfange Daten
>  while (!__HAL_UART_GET_FLAG(uart, UART_FLAG_RXNE));
>  return uart->Instance->DR;
> }

Grundsätzlich ist das Leeren des Datenregisters wahrscheinlich nicht 
notwendig aber so bin ich sicher, keine Halbwahrheiten zu empfangen.

: Bearbeitet durch User
von J. S. (jojos)


Lesenswert?

das ist keine Lösung sondern Murks. Du möchtest Voll Duplex, aber 
programmierst Halb Duplex. Wenn während des Sendens schon empfangen 
werden soll, dann muss der Empfang vorher gestartet werden, und für 
mehrere Zeichen müssen die dann gepuffert werden.
Jetzt funktioniert es vielleicht, aber mit kritischem Timing. Die 
Peripherie im Cortex-M ist asynchron am Bus, das Senden erfolgt also 
eher mit Glück spät genug nach dem Beschreiben des Registers.
Ein UART_TransmitReceive ist Unfug beim UART, das es das nicht als 
Basisfunktion gibt ist keine Schwäche der HAL. So eine Funktion gibt es 
nicht mal beim H7 obwohl der Hardware Fifos hat. Gleichzeitiges Senden 
und Empfangen gibt es nur beim SPI weil das ein Schieberegister hat, ein 
UART arbeitet komplett anders.
Richtige Lösungen wurden mit einer Fifo Implementierung im Interrupt 
schon genannt, oder DMA wenn es größere Datenblöcke sind. DMA hat dann 
eine geringere Interruptlast. HAL macht die Nutzung von Interrupt und 
DMA einfach und die Funktionen haben einfach ein _IT oder _DMA als 
Suffix, einheitlich bei allen Schnittstellen. Und da muss man sich nicht 
um einen Interrupthandler kümmern, sondern einen Callback 
implementieren. HAL wertet auch Fehler aus, deshalb ist der ISR Code 
aufwändiger. Aber Fehlerbehandlung scheint bei vielen hier ja verpönt zu 
sein.

von Harry L. (mysth)


Lesenswert?

J. S. schrieb:
> Aber Fehlerbehandlung scheint bei vielen hier ja verpönt zu
> sein.

Hier gelten die 3 goldenen Programmierer-Regeln:

* im vollen Bewusstsein, daß man es sowieso besser kann niemals 
schauen, wie andere das Problem gelöst haben
* erst optimieren - dann programmieren
* Flash niemals zu mehr als 20% füllen

von Jens R. (tecdroid)


Lesenswert?

@J. S.: An dieser Stelle ist das Timing gar nicht mal so kritisch. Es 
laufen keine Interrupts die stören könnten und auch sonst tut die 
Maschine an dieser Stelle nicht wirklich irgendwas. Insofern leere ich 
das Empfangsregister, beschreibe das Senderegister und warte dann, dass 
das Empfangsregister beschrieben wurde. Was soll da schief gehen? 
Theoreeetisch könnte ich vorher noch warten bis der Sendevorgang 
abgeschlossen ist. Da aber tatsächlich für diesen Fall eine Brücke 
zwischen Sende- und Empfangspin liegen muss können maximal eine defekte 
Hardware oder kosmische Bits (aka defekte Hardware) das System stören.

Ja, die Lösung ist unelegant- und mir tun unelegante Lösungen auch weh.. 
aaaber sie funktioniert für den aktuellen Fall besser als alles was ich 
bisher probiert habe.

> Ein UART_TransmitReceive ist Unfug beim UART, das es das nicht als
> Basisfunktion gibt ist keine Schwäche der HAL.

Es gibt ein USART_TransmitReceive. Zumindest bietet mir die 
Autovervollständigung von CubeMX das an und eigentlich ist der C-Parser 
hinter Eclipse doch recht zuverlässig.

von Harry L. (mysth)


Lesenswert?

Jens R. schrieb:
> Es gibt ein USART_TransmitReceive. Zumindest bietet mir die
> Autovervollständigung von CubeMX das an

Ich kann mir bei bestem Willen nicht vorstellen, wozu das gut sein 
sollte, und habe auch noch niemals irgendwo Code gesehen, in dem das 
verwendet wurde.

Ohne Interrupts ist das Alles sowieso einfach nur ganz grosser Murks.

von Jens R. (tecdroid)


Lesenswert?

Die Diskussion darfst du gern allein weiter führen. Ich werfe noch ein, 
dass der DMA wirklich viiiiiiiel tollerer ist als Interrupts..  ;)

von Harry L. (mysth)


Lesenswert?

Prima, daß du meine These bestätigst!

Harry L. schrieb:
> * im vollen Bewusstsein, daß man es sowieso besser kann niemals
> schauen, wie andere das Problem gelöst haben

von J. S. (jojos)


Lesenswert?

Jens R. schrieb:
> aaaber sie funktioniert für den aktuellen Fall besser als alles was ich
> bisher probiert habe.

der signifikante Unterschied ist das jetzt nur ein Zeichen gesendet und 
wieder empfangen wird, im ersten Code waren es drei. Bei den drei 
Zeichen ist es Abhängig von der Bitrate und Laufzeit welches Zeichen 
erwischt wird, der UART liefert nur das zuletzt empfangene.

Jens R. schrieb:
> Es gibt ein USART_TransmitReceive.

ja, gibt es für USART tatsächlich. Aber diese schreibt/liest in einer 
loop genauso nur jeweils 1 Zeichen. Dann sollte man das device auch als 
u_s_art mit einer Leitung für den synchronen Takt konfigurieren, sonst 
macht das auch keinen Sinn.

Jens R. schrieb:
> eigentlich ist der C-Parser
> hinter Eclipse doch recht zuverlässig.

nur eigentlich, beim analysieren von bedingtem Code ist der nicht 
perfekt, also wertet defines nicht in der richtigen Reihenfolge aus und 
zeigt damit nicht korrekt an. Besser ist die compile_commands.json vom 
Compiler zu nutzen, aber bis CubeMX CMake richtig unterstützt dauert es 
wohl noch ein bisschen.

: Bearbeitet durch User
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.