Forum: Mikrocontroller und Digitale Elektronik STM32 UART DMA Ende eines Pakets


von Thomas (Gast)


Lesenswert?

Hallo zusammen

Ich hoffe mal, dass ich nicht der erste bin, mit diesem Problem.

Arbeite mit dem STM32F4 discovery Bord und erhalte alle 50ms ein 
Datenpaket per UART, das 100 Bytes lange ist. (Baudrate 460800 ca. 
2.5ms)
Da dies auf 4 UART Kanälen stattfindet, möchte ich die Daten per DMA 
einlesen.

Nun mein Problem
Wenn von einem Paket ein oder mehrere Bytes verloren gehen, nimmt der 
DMA diese vom nächsten Paket, bis er den HAL_UART_RxCpltCallback 
ausgelöst.
Wie kann ich nun sicher sein, dass die 100 Bytes von selben Paket sind 
und nicht 0 bis 95 vom ersten Paket und 0 bis 4 vom zweiten?

Die Umsetzung sollte möglichst einfach und robust sein deshalb, möchte 
ich nicht unbedingt eine Lösung mit Timer oder erstes Byte einzeln ein 
zu lesen.
Gibt es dazu eine HAL Funktion.

Danke schon mal im Voraus

von Chris (Gast)


Lesenswert?

Dafür musst du die Daten aufs Format parsen und dann entsprechend den 
DMA stoppen.
Woher soll das DMA wissen wann eins deiner Pakete vollständig ist?

von W.S. (Gast)


Lesenswert?

Thomas schrieb:
> Arbeite mit dem STM32F4 discovery Bord und erhalte alle 50ms ein
> Datenpaket per UART, das 100 Bytes lange ist. (Baudrate 460800 ca.
> 2.5ms)
> Da dies auf 4 UART Kanälen stattfindet, möchte ich die Daten per DMA
> einlesen.

Wie schnell läßt du den µC denn takten? Die Bytes kommen so etwas mit 50 
kHz herein - und das 4 mal, das heißt, pro Byte hast du insgesamt rund 
500 Maschinentakte bei 100 MHz Systemtakt.

Das ließe sich bei sinnvoller Gestaltung der Interrupthandler auch noch 
ohne DMA machen - aber das dürfte nicht der knackpunkt sein.
Was du tatsächlich brauchst, ist vielmehr irgend ein Protokoll, also 
eine Förmlichkeit, wie deine Datenpakete auszusehen haben. 
Logischerweise auch mit Prüfsummen etc.

Sonst wird das nichts.

Also denke dir mal ein Protokoll aus.

Ach ja, denke auch daran, daß serielle Daten, wenn sie denn asynchron 
gesendet werden, immer mal ne Pause von zumindest einem halben Stopbit 
brauchen, sonst kommt es bei fortlaufender Übertragung dann doch mal zum 
Synchronisationsverlust. Da wäre es besser, nicht mit 460 kBaud zu 
übertragen und dann 47 ms zu pausieren, sondern stattdessen die Baudrate 
herunterzusetzen, mit 2 Stoppbit zu arbeiten und damit genügend Zeit zum 
Verarbeiten zu haben.

Und wenn das in deinen Systementwurf nicht hineinpaßt, dann hast du was 
verkehrt gemacht und dir ein zu enges Korsett überhelfen lassen.

W.S.

von Thomas (Gast)


Lesenswert?

Danke für die schnellen Antworten.

Der uC läuft mit 168Mhz also maximal Speed

Danke W.S. für den Tipp mit der Baudrate.
Habe sie einmal definiert und dann nicht mehr darüber nachgedacht, dass 
sie vielleicht unnötig zu schnell sind.

Das mit dem Protokoll habe ich aber noch nicht ganz verstanden.
Lese ich dann einfach 200 Bytes ein und Prüfe anhand der bekannten 
Protokollstruktur von wo bis wo meine gültigen Daten sind?

von DerDan (Gast)


Lesenswert?

Wie schon gesagt kann der DMA dir nicht garantieren das die 100 
empfangen Bytes immer auch logisch nur zu einem paket gehören.

Auf einem AVR32 kann man den UART so einstellen, das er nach einer IDLE 
Zeit einen Interrupt generiert wird. Dort mach ich das so das mit diesem 
Interrupt das Ende des eines Packets (mit variabler Länge) erkannt wird.
Der DMA wird dann neu aufgesetzt.

von W.S. (Gast)


Lesenswert?

Thomas schrieb:
> Das mit dem Protokoll habe ich aber noch nicht ganz verstanden.

Wenn man Pakete von Daten braucht, dann muß man sich was einfallen 
lassen, um deren Anfang und die korrekte Länge beim Empfänger 
feststellen zu können. Das ist das A und O für jegliche Verbindung. 
Stell dir vor, du schaltest deinen Empfänger irgendwann ein und er muß 
aus dem Zeugs, was da so hereinströmt, seine Synchronisierung auf die 
Datenpakete finden. Das selbe ist nötig, falls er mal durch irgendwas 
aus dem Tritt gekommen ist und dann den Anfang des nächsten Paketes 
suchen muß.

Wie du das anstellst, ist dein Problem. Wen du bei deinem Datenstrom 
irgend ein Zeichen hast, was definitiv dort vorkomen DARF, dann kannst 
du sowas als Synchronzeichen benutzen. Ebenso kannst du eine Art 
Signatur aus mehreren Zeichen erfinden, die dir den Paketbeginn anzeigt 
(Beispiel: RDS-Empfang). Du kannst aber auch UU codieren, um Zeichen 
frei zu kriegen und dennoch nicht allzuviele Bits zu vergeuden.

W.S.

von Georg (Gast)


Lesenswert?

Thomas schrieb:
> Das mit dem Protokoll habe ich aber noch nicht ganz verstanden

Das hat einen doppelten Zweck:

1. erkennen, wann ein Paket startet und endet.
2. den Erfolg der Übertragung prüfen.

zu 1 gibt es verschiedene Möglichkeiten, z.B. eindeutige Zeichen für 
Anfang und Ende, bei ASCII-Daten sind dafür die Sonderzeichen STX und 
ETX verwendbar, oder man schliesst jeden Record mit CR ab. Das muss 
natürlich beim Empfang jedes Zeichens geprüft werden, was mit DMA 
wahrscheinlich nicht geht, im Interrupt aber schon. Eine andere 
Möglichkeit ist eine längere Pause zwischen den Records, wie schon 
angedeutet, dann kann man erkennen, dass ein Record zu Ende ist, aber 
ausserhalb der DMA-Funktion mit einem Timer. Time Interrupts sind 
generell wesentlich flexibler zur Protokollverarbeitung als DMA.

zu 2 nimmt man Prüfsummen oder CRC, das ist wieder ein anderes Thema, 
aber kein unwichtiges. Bei externen Übertragungen ist immer mit Fehlern 
zu rechnen.

Georg

von Thomas (Gast)


Lesenswert?

Hallo Zusammen

Danke für die Hilfe.
Ich denke jetzt habe ich eine ziemlich robuste Lösung.

Ich habe das Protokoll so angepasst, dass die ersten beiden Bytes immer 
gleich sind. Als eine Art Startbedingung die ich überprüfe.
Danach überprüfe ich anhand der CRC Checksumme ob der Rest der Daten in 
Ordnung ist. Sollte es also eine Vermischung der Pakete geben, wird dies 
auch mit der falschen CRC erkannt.

Als zweite Massnahme wird der DMA erst 10ms nach einem DMA Callback 
wider gestartet. (10m da ein Datenpaket jetzt ca. 9ms lang ist) Somit 
soll der DMA wider auf die Pakete Synchronisiert werden, so dass der DMA 
nicht mitten in einem Paket neu gestartet wird.
Ausserdem kann ich mit dieser Methode auch gleich erkennen, wenn nach 
weiteren 50ms kein Callback aufgetreten ist, dass das Paket zu kurz war 
und den DMA Neustarten.

Vielen Dank an alle für die Hilfe

Thomas

von rmu (Gast)


Lesenswert?

Es gibt auch einen receive-timeout, der einen interrupt auslösen kann. 
Falls die Pakete garantiert in einem Rutsch übertragen werden und 
zwischen Paketen garantiert eine kurze Pause ist, kann man den reveice 
timeout auf z.B. 5 Zeichen einstellen, und in dem dann ausgelösten 
interrupt den DMA deaktivieren, und die Verarbeitung des Pakets 
anstossen. Dadurch wirds auch einfacher, nach verlorenen Zeichen wieder 
auf ein frisches Paket aufzusetzen und das Protokoll zu synchronisieren.

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.