Warum nicht immer DMA zugunsten von Interrupts mit UART auf STM32 verwenden? [geschlossen]

9

Ich verbringe letzten Monat viel Zeit damit, UART (für MIDI) dazu zu bringen, mit einem STM (STM32F103C8T6) unter Verwendung von Interrupts zu arbeiten, ohne großen Erfolg.

An diesem Abend mit DMA hat es jedoch ziemlich schnell funktioniert.

Da DMA meines Erachtens schneller ist und die CPU entlastet, warum nicht immer DMA zugunsten von Interrupts verwenden? Zumal es beim STM32 einige Probleme zu geben scheint.

Ich verwende STM32CubeMx / HAL.

Michel Keijzers
quelle
2
Warum nicht? Das ist entweder eine Meinungsfrage, eine Frage nach dem möglichen technischen Grund oder auf die gleiche Weise zu weit gefasst, und daher keine Frage, die hierher gehört. Um ein zufälliges Beispiel zu nennen: DMA bedeutet eine höhere Latenz bei der Inanspruchnahme der Daten, insbesondere da Sie keinen wirklichen Vorteil erhalten, wenn Sie nicht zulassen, dass mehrere Zeichen erfasst werden. Oft ist das in Ordnung, manchmal nicht.
Chris Stratton
6
Wenn es Wochen gedauert hat, bis Interrupts funktionieren, liegt das daran, dass Sie die Aufgabe falsch angegangen sind. Es könnte durchaus länger dauern, bis DMA funktioniert - es ist tatsächlich eine komplexere Aufgabe. Die offensichtliche Leichtigkeit der komplexeren Aufgabe gegenüber der einfacheren hängt vermutlich von den Ressourcen ab, die Sie für die einzelnen Aufgaben verwendet haben, nicht vom Mechanismus selbst.
Chris Stratton
5
Gehen Sie niemals davon aus, dass dma die CPU freigibt, manchmal ja, die CPU läuft weiter, manchmal nein, der Prozessor ist eingefroren, um den Bus für die dma-Engine zu halten. Es ist trivial, dies mit einer Arm-Implementierung zu tun. Ich kann also nicht einfach sagen, dass alle Arme so sind und alle x86s so oder was auch immer. Es ist nicht so einfach. Sie müssen immer das Systemdesign überprüfen und vielleicht ein bisschen hacken. Der Chip, den Sie haben, kann sehr gut den Armkern freisetzen, dies ist nur ein Kommentar zu dma. Was Ihre Frage betrifft, macht es keinen Sinn, dass Sie nicht mithalten können, und dma + int ist wahrscheinlich die vollständige Lösung, wenn Sie nicht nur eine Umfrage durchführen können.
old_timer
5
Interrupts sind an der seriellen STM32F-Schnittstelle ziemlich trivial. Warum posten Sie keine Frage mit Ihrem Code, damit einige von uns versuchen können, herauszufinden, wo Sie falsch liegen? Es ist nie eine gute Idee, Code zu hacken, bis er funktioniert, ohne zu verstehen, was das zugrunde liegende Problem war.
Jon
7
Meiner (nicht so) bescheidenen Meinung nach ist dies einer der Nachteile der Verwendung des schrecklichen, aufgeblähten Würfels. Schreiben Sie die Software von Grund auf neu, Sie werden genau lernen, wie der UART funktioniert (weil Sie müssen), Sie werden das Peripheriegerät viel besser verstehen und auf lange Sicht sparen Sie so viel Zeit.
DiBosco

Antworten:

24

Während DMA die CPU entlastet und somit die Latenz anderer Interrupt-gesteuerter Anwendungen verringern kann, die auf demselben Kern ausgeführt werden, sind damit Kosten verbunden:

  • Es gibt nur eine begrenzte Anzahl von DMA-Kanälen und es gibt Einschränkungen hinsichtlich der Interaktion dieser Kanäle mit den verschiedenen Peripheriegeräten. Ein anderes Peripheriegerät auf demselben Kanal ist möglicherweise besser für die DMA-Verwendung geeignet.

    Wenn Sie beispielsweise alle 5 ms eine I2C-Massenübertragung durchführen, scheint dies ein besserer Kandidat für DMA zu sein als ein gelegentlicher Debug-Befehl, der auf UART2 eintrifft.

  • Das Einrichten und Verwalten von DMA ist eine Kosten für sich. (Normalerweise wird das Einrichten von DMA als komplexer angesehen als das Einrichten einer normalen Interrupt-gesteuerten Übertragung pro Zeichen. Dies liegt an der Speicherverwaltung, mehr beteiligten Peripheriegeräten, der Verwendung von Interrupts durch DMA selbst und der Möglichkeit, dass Sie die ersten Zeichen außerhalb von DMA analysieren müssen wie auch immer, siehe unten.)

  • DMA kann zusätzliche Energie verbrauchen , da es sich um eine weitere Domäne des Kerns handelt, die getaktet werden muss. Auf der anderen Seite können Sie die CPU anhalten, während die DMA-Übertragung läuft, wenn der Kern dies unterstützt.

  • Für DMA sind Speicherpuffer erforderlich (es sei denn, Sie führen DMA von Peripheriegerät zu Peripheriegerät durch), sodass damit einige Speicherkosten verbunden sind.

    (Die Speicherkosten können auch bei der Verwendung von Interrupts pro Zeichen anfallen, aber sie können auch viel kleiner sein oder überhaupt verschwinden, wenn die Nachrichten sofort innerhalb des Interrupts interpretiert werden.)

  • DMA erzeugt eine Latenz, da die CPU erst benachrichtigt wird, wenn die Übertragung abgeschlossen / halb abgeschlossen ist (siehe die anderen Antworten).

  • Außer beim Streamen von Daten in / aus einem Ringpuffer müssen Sie im Voraus wissen, wie viele Daten Sie empfangen / senden werden.

    • Dies kann bedeuten, dass die ersten Zeichen einer Nachricht mithilfe von Interrupts pro Zeichen verarbeitet werden müssen. Wenn Sie beispielsweise eine Schnittstelle zu einem XBee herstellen, lesen Sie zuerst den Pakettyp und die Paketgröße und lösen dann eine DMA-Übertragung in einen zugewiesenen Puffer aus.

    • Bei anderen Protokollen ist dies möglicherweise überhaupt nicht möglich, wenn sie nur Trennzeichen für das Ende der Nachricht verwenden: beispielsweise textbasierte Protokolle, die '\n'als Trennzeichen verwendet werden. (Es sei denn, das DMA-Peripheriegerät unterstützt das Abgleichen eines Zeichens.)

Wie Sie sehen können, sind hier viele Kompromisse zu berücksichtigen. Einige beziehen sich auf Hardwareeinschränkungen (Anzahl der Kanäle, Konflikte mit anderen Peripheriegeräten, Übereinstimmung mit Zeichen), andere basieren auf dem verwendeten Protokoll (Trennzeichen, bekannte Länge, Speicherpuffer).

Um einige anekdotische Beweise hinzuzufügen, habe ich mich all diesen Kompromissen in einem Hobbyprojekt gestellt, bei dem viele verschiedene Peripheriegeräte mit sehr unterschiedlichen Protokollen verwendet wurden. Es gab einige Kompromisse, hauptsächlich aufgrund der Frage "Wie viele Daten übertrage ich und wie oft werde ich das tun?". Dies gibt Ihnen im Wesentlichen eine grobe Schätzung der Auswirkungen einer einfachen Interrupt-gesteuerten Übertragung auf die CPU. Ich habe daher der oben genannten I2C-Übertragung alle 5 ms Vorrang vor der UART-Übertragung alle paar Sekunden eingeräumt, bei der derselbe DMA-Kanal verwendet wurde. Eine andere UART-Übertragung, die häufiger und mit mehr Daten stattfindet, hat andererseits Vorrang vor einer anderen I2C-Übertragung, die seltener stattfindet. Es sind alles Kompromisse.

Natürlich hat die Verwendung von DMA auch Vorteile, aber darum haben Sie nicht gebeten.

Jonas Schäfer
quelle
Vielen Dank für Ihre ausführliche Antwort. MIDI wird der kritischste Teil sein, daher denke ich, dass DMA dafür geeignet ist (obwohl die Geschwindigkeit niedrig ist: 31250 Baud). Ich habe genug DMA-Kanäle, später werde ich einen anderen STM32 verwenden, wenn ich 4 USARTs verwende. Ich muss die CPU nicht anhalten, da sie 5 V USB-Strom hat, und ich muss die Verarbeitung zwischen den Nachrichten durchführen (um die Nachrichten in der Hauptschleife zu verarbeiten). Ich habe einen 256-Byte-Lese- und einen 256-Byte-Sendepuffer. Ich kann es später bei Bedarf erhöhen. Der STM32f103c8t6 hat 20 KB RAM, der eventuelle STM, den ich verwenden werde, hat 192 KB.
Michel Keijzers
Und Sie geben mir eine sehr gute Idee, wie ich mich verbessern kann. Bisher lese ich immer 1 Byte und überprüfe kontinuierlich, ob eine vollständige (MIDI) Nachricht empfangen wird. Aber ich kann das erste Byte lesen, und abhängig davon ist meistens die Größe bekannt und kann nach dem Rest fragen. Das hat mich einen weiteren kleinen Puffer gekostet, aber das ist in Ordnung.
Michel Keijzers
Das Lesen einzelner Bytes mit DMA ist sehr ineffizient. Für eine geringere Latenz und eine höhere Effizienz wäre es günstig, Interrupts pro Zeichen zu verwenden, bis Sie die Größe kennen, und dann auf DMA umzuschalten.
Jonas Schäfer
Nun, ich hatte viele Probleme mit Interrupts (ohne DMA). Ich denke, ich werde einen 1-Byte-DMA-Empfang verwenden, und danach weiß ich, wie viele Bytes ich erwarten und eine DMA-Anfrage ausführen werde, um mehr zu erhalten.
Michel Keijzers
6
Das ist wahrscheinlich ein Fehler - Sie sollten Ihren einfachen Interrupt-Code ohne DMA korrigieren.
Chris Stratton
10

Die Verwendung von DMA bedeutet normalerweise, dass Sie nicht mehr jedes Zeichen unterbrechen, sondern erst, nachdem ein "Puffer voller" Zeichen empfangen (oder gesendet) wurde. Dies erhöht die Latenz der Verarbeitung dieser Zeichen - das erste Zeichen wird erst verarbeitet, nachdem das letzte Zeichen im Puffer empfangen wurde.

Diese Latenz kann eine schlechte Sache sein, insbesondere in einer latenzempfindlichen Anwendung wie MIDI, bei der einige ms hier und da schwerwiegende Probleme mit der Spielbarkeit von Live-Auftritten verursachen können.

Dave Tweed
quelle
Ich empfange jeweils 1 Byte (also einen 'DMA'-Puffer von 1 Byte) und speichere ihn nach jedem DMA-Rückruf dieses einen Bytes in einem Ringpuffer, den ich manuell verarbeite. In meiner Hauptschleife möchte ich nach vollständigen MIDI-Nachrichten suchen und diese verarbeiten.
Michel Keijzers
3
DMA wird normalerweise verwendet, um mehrere Bytes abzurufen und nur dann zu unterbrechen, wenn alle empfangen wurden. Eine Unterbrechung nach nur einem Byte ist normal, wenn DMA nicht verwendet wird. Daher frage ich mich: Was bringt die zusätzliche Komplikation, DMA dafür zu verwenden?
Steve Melnikoff
5
@MichelKeijzers Dann ist das, was Sie tun, ziemlich genau das gleiche, was Sie in reinen Interrupt-gesteuerten Implementierungen tun würden. Daher bietet die Verwendung von DMA in diesem Fall keinen Vorteil, und Ihr ursprüngliches Problem wird wahrscheinlich nicht durch den DMA gelöst, sondern durch das Umschreiben Ihres (ISR-, Setup-) Codes.
JimmyB
@ JimmyB ... danke ... aber aufgrund der Antwort von Jonas unten werde ich eine Verbesserung vornehmen, um so viele Bytes zu lesen, wie die Nachricht lang ist. Ich weiß das nach Erhalt des ersten Bytes (in den meisten Fällen). Dann ist es vorteilhafter, DMA über Interrupts zu verwenden.
Michel Keijzers
8

DMA ist kein Ersatz für Interrupts - sie werden normalerweise zusammen verwendet! Wenn Sie beispielsweise DMA zum Senden von Daten über einen UART verwenden, benötigen Sie immer noch einen Interrupt, um zu erfahren, wann der Sendevorgang abgeschlossen ist.

Dämmerung -inaktiv-
quelle
Richtig, vielleicht ist gerade auf dem STM32 der (reine Nicht-DMA-) Interrupt-Mechanismus im Vergleich zu direktem DMA etwas ungeschickt.
Michel Keijzers
2
@duskwuff Nicht wirklich; Sie können abfragen, um zu sehen, wann der DMA abgeschlossen ist, und Sie möchten dies möglicherweise , da einer der Hauptgründe für die Verwendung von DMA darin besteht, dass Sie sich nicht mit der seriellen Schnittstelle befassen müssen, bis sich Ihr Programm in einem Zustand befindet, in dem es auf den empfangenen Vorgang reagieren kann Daten. Bei ausgehendem DMA können Sie lediglich abfragen, ob dem Sendepuffer weitere hinzugefügt werden können.
Chris Stratton
1
@MichelKeijzers: IDK der spezifische Chip, aber normalerweise ist die Alternative zu DMA nicht buchstäblich Interrupts, sondern programmierte E / A (wobei Sie CPU-Anweisungen verwenden, um Daten aus / in ein E / A-Register zu lesen / schreiben). In einem Interrupt-Handler führen Sie normalerweise einen Lesevorgang und dann möglicherweise einen anderen durch, falls beim Lesen des ersten Zeichens ein Zeichen eingegangen ist, insbesondere, wenn dadurch kein weiterer Interrupt ausgelöst wird. Oder lesen Sie, bis ein interner Puffer leer war, wenn es einen solchen Puffer gibt. Offensichtlich benötigen Sie mehr Interrupts für PIO und richten sie anders ein.
Peter Cordes
@ChrisStratton Guter Punkt ... bisher habe ich nicht geprüft, ob es möglich ist zu senden, ich sende nur etwas und überprüfe nicht, ob es in Ordnung ist. Wenn nicht, versuche ich es wahrscheinlich später noch einmal.
Michel Keijzers
@PeterCordes Es scheint, dass der STM32 genug Interrupts für DMA hat und ich jedes Mal nur 1 Byte lese. Selbst der einfachste STM32 (F103c8t6) verfügt über genügend DMA-Ports / Interrupts.
Michel Keijzers
5

Die Verwendung von DMA wirft einige interessante Fragen und Herausforderungen auf, die über alle anderen Überlegungen zur Verwendung von UART-Peripheriegeräten hinausgehen. Ich gebe Ihnen einige Beispiele: Angenommen, Ihr uC sitzt mit anderen Geräten auf einem RS485-Bus (oder einem anderen Bus). Es gibt viele Nachrichten im Bus, einige sind für Ihre uC bestimmt, andere nicht. Nehmen Sie außerdem an, dass diese Busnachbarn alle ein anderes Datenprotokoll sprechen, was impliziert, dass die Nachrichtenlängen unterschiedlich sind.

Einige Fragen, die nur bei der Verwendung von DMA auftauchen, sind:

  • wann unterbreche ich
    • DMAs unterbrechen nur dann wirklich gerne, wenn sie eine voreingestellte Datenmenge übertragen haben.
    • Was tun Sie, wenn Sie nie genug Daten erhalten, um einen DMA-Interrupt auszulösen?
  • Was ist, wenn Sie nur eine Teilnachricht erhalten, wenn der DMA unterbricht?
  • Wie sehen Ihre Empfangspuffer aus? Sind sie linear oder kreisförmig?
    • DMA kann ein widerspenstiger Ringpuffer-Teilnehmer in dem Sinne sein, dass es nur die Adressgrenze einhält, aber kein Problem damit hat, an den anderen Zeigern im Kreispuffersystem vorbei zu blasen.

Jedenfalls nur Denkanstöße.

pgvoorhees
quelle
Vielen Dank für diese Überlegungen. Derzeit erhalte ich immer 1 Byte und speichere es in einem Ringpuffer, da meine Nachrichten (MIDI) tatsächlich unterschiedliche Längen haben können und ich nicht weiß, welche ich als nächstes bekomme. In meiner Hauptschleife überprüfe ich, ob vollständige Nachrichten vorliegen, um sie zu verarbeiten (und wenn sie vollständig sind, entferne ich sie aus dem Ringpuffer). Ich werde also immer genug Daten erhalten (es sei denn, ich würde Bytes verpassen, ich muss das überprüfen). Mein Empfangspuffer ist nur 1 Byte, aber ich kopiere ihn in einen Ring- / Umlaufpuffer. Ich habe nicht überprüft, ob es voll ist (muss hinzugefügt werden).
Michel Keijzers
Hey, keine Sorge. Ich bin sicher, Ihre Bewerbung wird gut programmiert sein. Wie andere bereits erwähnt haben, ist DMA großartig, aber nicht kostenlos. Es führt zusätzliche Überlegungen in das System ein, die nicht existieren, wenn Sie ohne Verwendung davonkommen können.
pgvoorhees
Nun, ich hoffe, ich bin noch ein Anfänger.
Michel Keijzers
3

Auf der Empfangsseite (wie ich mich erinnere) endet DMA entweder bei einer Zeichenübereinstimmung oder bei der Terminalanzahl. Einige Protokolle und viele interaktive Anwendungen passen nicht einfach in dieses Modell, und Sie müssen wirklich Zeichen für Zeichen mit den Dingen umgehen. Die DMA-Techniken können auch spröde sein, wenn die Kommunikationsverbindung unzuverlässig ist. Wenn Sie ein einzelnes Zeichen im Stream verlieren, kann dies Ihre DMA-Zustandsmaschine leicht durcheinander bringen.

Dean Franks
quelle
Ich empfange tatsächlich Byte für Byte und kopiere es manuell in einen Ringpuffer, um es später zu verarbeiten.
Michel Keijzers
1

Ich habe den STM32CubeMx / HAL jetzt in einigen Projekten verwendet und festgestellt, dass die von ihm generierte UART-Handhabungssoftware auf der Empfangsseite deutliche Mängel aufweist.

Beim Senden möchten Sie normalerweise einen Datenblock oder eine Textzeile senden. In diesem Fall wissen Sie im Voraus, wie lange die Datenübertragung dauert, und daher ist die Verwendung des DMA eine naheliegende Lösung. Sobald die Übertragung abgeschlossen ist, erhalten Sie eine Unterbrechung und können mithilfe der UART TX-Rückruffunktion Ihrem Hauptcode anzeigen, dass die Übertragung abgeschlossen ist, und Sie können einen weiteren Datenblock senden.

Wenn es um den Datenempfang geht, setzen alle von ST bereitgestellten Funktionen voraus, dass Sie wissen, wie viele Zeichen das sendende Gerät Ihnen geben wird, bevor es mit dem Senden beginnt. Normalerweise ist dies nicht bekannt. Die Interrupt-Funktion legt die empfangenen Daten in einem Puffer ab und zeigt nur an, dass Daten verfügbar sind, wenn die vordefinierte Anzahl von Zeichen empfangen wurde. Wenn Sie versuchen, die DMA- oder Interrupt-Funktion zum Empfangen von Daten zu verwenden, indem Sie sequentielle Einzelzeichenübertragungen einrichten, bedeutet die Einrichtungszeit für jede dieser Funktionen, dass Sie Zeichen mit einer anderen als der langsamsten Datenrate (der Baudrate, die Sie erhalten) verlieren Der Beginn des Datenverlusts hängt von der Taktrate Ihres Prozessors ab und lädt den Prozessor übermäßig, sodass keine Befehlszyklen für eine andere Verarbeitung verbleiben

Um dies zu umgehen, habe ich meine eigene Interrupt-Handler-Funktion geschrieben, die die Daten in einem kleinen lokalen Ringpuffer speichert und eine Anzahl festlegt, die vom Hauptcode (einem RTOS-Zählsemaphor) gelesen wird, um anzuzeigen, dass empfangene Daten bereit sind. Der Hauptcode kann dann die Daten aus diesem Puffer nach Belieben erfassen. Es spielt keine Rolle, ob sich die Erfassung der Daten verzögert, vorausgesetzt, der lokale Puffer läuft nicht über, bevor die Daten erfasst werden.

uɐɪ
quelle
Ich mache genau das gleiche (glaube ich). Ich lese jeweils 1 Byte und speichere es in einem zyklischen Puffer. Ich beabsichtige, in der Hauptschleife nach vollständigen Nachrichten zu suchen. Kann allerdings etwas verbessert werden.
Michel Keijzers
Denken Sie, ich könnte auf das Problem stoßen, dass das Einrichten des DMA jedes Mal meinen Prozessor / fehlende Zeichen mit 31.250 Baud überlastet?
Michel Keijzers
1
Solange Sie den DMA so einrichten, dass mehrere Zeichen gleichzeitig übertragen werden, ist dies kein Problem. Ich habe 4 UARTs mit 115200 und höher und I2C mit DMA ohne Probleme. Die UART-Übertragungen sind alle ~ 20 Bytes oder länger. Das Problem war die Verwendung von DMA für den Empfang auf dem UART (L4-Prozessor bei 80 MHz, 9600 Baud).
17.
Momentan setze ich es auf jeweils 1 Byte, aber ich kann es verbessern (indem ich das erste Byte mache und dann überprüfe, wie viele weitere Bytes benötigt werden).
Michel Keijzers