Ich habe letzten Monat viel Zeit damit verbracht, UART (für MIDI) dazu zu bringen, mit einem STM (STM32F103C8T6) unter Verwendung von Interrupts zu arbeiten, ohne großen Erfolg.
Heute Abend mit DMA funktionierte es jedoch recht schnell.
Da, soweit ich gelesen habe, DMA schneller ist und die CPU entlastet, warum nicht immer DMA zugunsten von Interrupts verwenden? Zumal es auf dem STM32 einige Probleme zu geben scheint.
Ich verwende STM32CubeMx/HAL.
Während DMA die CPU entlastet und somit die Latenz anderer Interrupt-gesteuerter Anwendungen, die auf demselben Kern laufen, verringern kann, sind damit Kosten verbunden:
Es gibt nur eine begrenzte Anzahl von DMA-Kanälen und es gibt Beschränkungen, wie diese Kanäle mit den verschiedenen Peripheriegeräten interagieren können. Ein anderes Peripheriegerät auf demselben Kanal ist möglicherweise besser für die DMA-Nutzung geeignet.
Wenn Sie beispielsweise alle 5 ms eine Massen-I2C-Übertragung haben, 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 ein Kostenfaktor für sich. (Normalerweise wird das Einrichten von DMA als komplexer angesehen als das Einrichten einer normalen Interrupt-gesteuerten Übertragung pro Zeichen, aufgrund der Speicherverwaltung, mehr beteiligter Peripheriegeräte, DMA, das Interrupts selbst verwendet, und der Möglichkeit, dass Sie die ersten paar Zeichen außerhalb von DMA analysieren müssen wie auch immer, siehe unten.)
DMA kann zusätzliche Energie verbrauchen , da es noch eine weitere Domäne des Kerns ist, die getaktet werden muss. Andererseits können Sie die CPU während der DMA-Übertragung aussetzen, wenn der Kern dies unterstützt.
Für DMA sind Speicherpuffer erforderlich (es sei denn, Sie führen Peripherie-zu-Peripherie-DMA durch), sodass einige Speicherkosten damit verbunden sind.
(Die Speicherkosten können auch bei der Verwendung von zeichenweisen Interrupts anfallen, aber sie können auch viel kleiner sein oder ganz verschwinden, wenn die Nachrichten sofort innerhalb des Interrupts interpretiert werden.)
DMA erzeugt eine Latenz , da die CPU nur 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 mit zeichenweisen Interrupts verarbeitet werden müssen: Wenn Sie beispielsweise mit einem XBee verbunden sind, lesen Sie zuerst den Pakettyp und die Größ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 End-of-Message-Trennzeichen verwenden: zum Beispiel textbasierte Protokolle, die '\n'
als Trennzeichen verwenden. (Es sei denn, das DMA-Peripheriegerät unterstützt die Übereinstimmung mit einem Zeichen.)
Wie Sie sehen können, gibt es hier viele Kompromisse zu berücksichtigen. Einige beziehen sich auf Hardwarebeschränkungen (Anzahl der Kanäle, Konflikte mit anderen Peripheriegeräten, übereinstimmende Zeichen), andere basieren auf dem verwendeten Protokoll (Trennzeichen, bekannte Länge, Speicherpuffer).
Um einige anekdotische Beweise hinzuzufügen, ich habe all diese Kompromisse in einem Hobbyprojekt erlebt, bei dem viele verschiedene Peripheriegeräte mit sehr unterschiedlichen Protokollen verwendet wurden. Es gab einige Kompromisse zu machen, hauptsächlich basierend auf 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 erwähnten I2C-Übertragung alle 5 ms Vorrang vor der UART-Übertragung alle paar Sekunden gegeben, die denselben DMA-Kanal verwendet. Eine andere UART-Übertragung, die häufiger und mit mehr Daten stattfindet, hat dagegen Vorrang vor einer anderen I2C-Übertragung, die seltener stattfindet. Es sind alles Kompromisse.
Natürlich hat die Verwendung von DMA auch Vorteile, aber das ist nicht das, wonach Sie gefragt haben.
Die Verwendung von DMA bedeutet normalerweise, dass Sie nicht mehr bei jedem Zeichen einen Interrupt nehmen, sondern erst, nachdem ein "Puffer voll" von Zeichen empfangen (oder gesendet) wurde. Dies erhöht die Latenz bei 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, wo ein paar ms hier und da zu ernsthaften Spielbarkeitsproblemen bei Live-Auftritten führen können.
DMA ist kein Ersatz für Interrupts – sie werden normalerweise zusammen verwendet! Wenn Sie beispielsweise DMA verwenden, um Daten über einen UART zu senden, benötigen Sie immer noch einen Interrupt, der Ihnen mitteilt, wann der Sendevorgang abgeschlossen ist.
Die Verwendung von DMA führt zu einigen interessanten Fragen und Herausforderungen, die über alle anderen Überlegungen zur Verwendung von UART-Peripheriegeräten hinausgehen. Ich gebe Ihnen ein paar Beispiele: Angenommen, Ihr uC sitzt mit anderen Geräten auf einem RS485-Bus (oder was auch immer). Es gibt viele Nachrichten im Bus, einige sind für Ihr uC bestimmt, andere nicht. Nehmen Sie außerdem an, dass diese Busnachbarn alle ein unterschiedliches Datenprotokoll sprechen, was impliziert, dass die Nachrichtenlängen unterschiedlich sind.
Einige Fragen, die nur bei der Verwendung von DMA auftauchen, sind:
Jedenfalls nur ein Denkanstoß.
Auf der Empfangsseite (soweit ich mich erinnere) endet DMA entweder bei einer Zeichenübereinstimmung oder bei der Endzählung. Einige Protokolle und viele interaktive Anwendungen passen nicht so einfach in dieses Modell, und Sie müssen die Dinge wirklich Zeichen für Zeichen handhaben. Die DMA-Techniken können auch spröde sein, wenn die Kommunikationsverbindung unzuverlässig ist. Der Verlust eines einzigen Zeichens im Stream kann Ihre DMA-Zustandsmaschine leicht durcheinander bringen.
Ich habe den STM32CubeMx/HAL jetzt in einigen Projekten verwendet und festgestellt, dass die von ihm generierte UART-Handhabungssoftware auf der Empfangsseite eindeutige 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. Sie erhalten einen Interrupt, sobald die Übertragung abgeschlossen ist, und können die UART TX Complete Callback-Funktion verwenden, um Ihrem Hauptcode anzuzeigen, dass die Übertragung abgeschlossen ist, und Sie können einen weiteren Datenblock senden.
Wenn es um den Datenempfang geht, setzen die von ST bereitgestellten Funktionen alle 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 einen Puffer 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 zu verwenden, um Daten zu empfangen, indem Sie sequentielle Einzelzeichenübertragungen einrichten, bedeutet die Einrichtungszeit für jede davon, dass Sie Zeichen bei etwas anderem als den langsamsten Datenraten verlieren (der Baudrate, die Sie verwenden beginnen, Daten zu verlieren, hängt von Ihrer Prozessortaktgeschwindigkeit ab) und wird den Prozessor übermäßig belasten, sodass keine Befehlszyklen für andere Verarbeitungen übrig bleiben
Um dies zu umgehen, habe ich meine eigene Interrupt-Handler-Funktion geschrieben, die die Daten in einem kleinen lokalen Ringpuffer speichert und einen Zähler setzt, der vom Hauptcode gelesen wird (ein RTOS-Zählsemaphor), um anzuzeigen, dass empfangene Daten bereit sind. Der Hauptcode kann dann nach Belieben die Daten aus diesem Puffer sammeln, es spielt keine Rolle, ob es eine gewisse Verzögerung beim Sammeln der Daten gibt, vorausgesetzt, dass der lokale Puffer nicht überläuft, bevor die Daten gesammelt werden.
Harry Swensson
Michel Keijzers
Chris Stratton
Chris Stratton
AK
Oldtimer
Oldtimer
Oldtimer
Michel Keijzers
Jon
Michel Keijzers
DiBosco
Michel Keijzers
Michel Keijzers