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..
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.
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..
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?
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.
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.
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 .....
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.
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.
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..
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.
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..
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.
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.
@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.
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.
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.
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.
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.