STM32F103 - UART Loopback- die eigene Sendung empfangen..

OP #7544265
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..

Moderator (Firma: Titel) Persönliche Seite #7544282
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?

OP #7544294
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..

#7544373
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?

#7544463
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"

#7544520
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.

#7544525
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.

OP #7544572
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..

OP #7544605
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.

#7544632
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.

OP #7544944
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.

#7545000
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren