Ich versuche, USART-Nachrichten auf einem Mikrocontroller zu empfangen, der vom PC stammt und ihm befiehlt, bestimmte Tests auszuführen. Ich verwende STM32F4, ich habe mich für die Verwendung von DMA entschieden, da Nachrichten auf demselben USART, die vom Mikrocontroller stammen, 2000000 bps haben müssten, da ich es nicht mit einer niedrigeren Bitrate oder ohne DMA zum Laufen bringen könnte. Ich verwende binär statt ASCII für beide Richtungen.
Was ich bisher entworfen habe, "funktioniert" (aktualisiert fast in Echtzeit), vorausgesetzt, die Rate der gesendeten Nachrichten ist konstant, da jede neue Nachricht die Löschung der vorherigen aus dem DMA-Register erzwingt. Wenn jedoch initiiert wird und nur ein einziger Anweisungs-/USART-Frame gesendet wird, der zufällig weniger als die Datenmenge ist, die für die empfangen werden soll HAL_UART_Receive_DMA()
, bleibt die Nachricht hängen, bis sie von der nächsten Nachricht verschoben wird.
Ein noch schlimmeres Szenario wäre, wenn USART_take_size
100 ist und es einen einzelnen 10-Byte-Rahmen im DMA-Puffer gibt. In diesem Fall müsste ich auf weitere 9 (10 Byte) Frames warten, bevor ich sie alle auf einmal empfange.
So sieht mein Callback aktuell aus.
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
// uart handle, global array, numb of bytes to trigger interrupt
HAL_UART_Receive_DMA (huart, raw_serial, USART_take_size);
// function that adds received bytes to the circular buffer
AppendSerial(raw_serial,USART_take_size);
}
Ein Thread entnimmt Bytes aus dem Ringpuffer, durchläuft sie und extrahiert basierend auf SOF- und EOF-Bytes die Nachricht innerhalb des Rahmens.
Mein Problem ist, dass die ankommenden Frames unterschiedlich lang sind. Wenn USART_take_size
x festgelegt ist und der Frame ankommt und die Länge kleiner als x ist, muss er im Pufferarray im extrahierenden Thread sitzen, bis ein weiterer Frame ankommt lässt notwendige Bytes erscheinen, damit der Parser die eingebettete Nachricht erhält.
Ich sehe drei Möglichkeiten, dies zu lösen, und jede scheint nicht richtig zu sein:
Auf 1 setzen USART_take_size
, was den ganzen Punkt von DMA zunichte macht
Haben Sie eine feste Rahmengröße und füllen Sie den Inhalt vor EOF mit Nullen auf, wenn der Inhalt kleiner als die USART_take_size
. Das wirkt einfach schlampig und verschwenderisch. Es müsste auch bei jedem Frame ausgelöst werden, obwohl die Gesamtrate in diesem Fall viel höher sein könnte.
Konfigurieren Sie den DMA so, dass er ISR bei einer variablen Anzahl von empfangenen Bytes auslöst, vorausgesetzt, es gab vorher eine gewisse Inaktivität.
Ich weiß nicht viel über DMA, und obwohl es ein Kompromiss zwischen der Anzahl der ausgelösten ISR und der Mindestnachricht zu sein scheint, die Sie lesen können, denke ich, dass es eine Möglichkeit geben sollte, Nachrichten nach einer Zeit der Inaktivität zu erhalten. Jeder andere Lösungsansatz wäre toll. Danke schön!
Ich hatte ein ähnliches Problem in einem meiner Projekte. Ich habe es gelöst, indem ich einen ausreichend langen Puffer erstellt habe, um die längstmögliche Nachricht zu speichern, und DMA zur Übertragung von USART verwendet habe. Um das Ende der Nachricht zu erkennen, habe ich einen Leerlaufleitungserkennungs-Interrupt verwendet, der ausgelöst wird, wenn der USART-Empfänger den Empfang von Daten einstellt.
Der STM32 DMA kann nicht selbst das tun, was Sie wollen. Um mit Nachrichten variabler Länge umgehen zu können, müsste es in der Lage sein, die Nachricht intelligent zu parsen und die Länge unter Verwendung des von Ihnen gewählten Codierungsschemas zu bestimmen. Das geht weit über seine Möglichkeiten hinaus.
Mir fallen für dein Problem eigentlich nur zwei Möglichkeiten ein:
Verarbeiten Sie einfach jedes Byte in der USART-Empfangs-ISR. Der STM32 hat einen ziemlich geringen Interrupt-Overhead. Wenn Ihre Anwendung damit umgehen kann, ist es manchmal am besten, einfach zu gehen, auch wenn es vielleicht nicht so elegant ist.
Sie können den DMA einfach so konfigurieren, dass er als Fifo-Puffer arbeitet. Der DMA lädt Bytes in den Puffer und Sie richten dann einen periodischen Timer-Interrupt ein, um den Puffer auf Daten zu prüfen und Nachrichten zu analysieren. Dies ist ein Zwischenansatz im Vergleich zu Option 1, da Sie damit den Interrupt-Overhead auf Kosten der Latenzzeit reduzieren können. Sie können die Timer-Abfrageperiode gegen die Fifo-Größe abstimmen, um Ihre speziellen Anforderungen an die Echtzeitverarbeitung auszugleichen. Es gibt auch Tricks, die Sie mit den DMA-Level-Triggern machen können, obwohl die Nützlichkeit davon von Ihrem Nachrichtenformat abhängt.
pjc50
Peter Schmidt
mega_creamery
mega_creamery
Peter Schmidt
mega_creamery
DKNguyen