Verwendung der Character Match-Funktionalität in einem STM32F7-Chip

Ich versuche, einen UART-Empfänger zu implementieren, der funktioniert, indem er ein '\ 0' im Datenstrom findet, ODER wenn er 256 Zeichen empfängt, an welcher Stelle die Daten von einem Interrupt verarbeitet werden sollten. Der Punkt ist, DMA für den Empfang zu verwenden, aber wenn eines dieser Ereignisse eintritt, sollte es aufhören, den aktuellen Puffer zu füllen und die Daten zu verarbeiten. Der Grund ist, dass ich COBS-Codierung verwende , um meine Daten zu rahmen.

Der STM32F7-Chip hat etwas namens Character Match als Teil der Modbus-Spezifikation, das ich durch Setzen der Bits im Konfigurationsregister zum Laufen gebracht habe, da es von den HAL-Bibliotheken (stm32cubemx, die diesen Code beim Regenerieren entfernen würden) nicht unterstützt wird:

MODIFY_REG(huart->Instance->CR1, UART_CR1_FIELDS, tmpreg | USART_CR1_CMIE);

Und am Ende des UART-Interrupt-Handlers mache ich das

UART8->ICR |= USART_ISR_CMF;

Um das Interrupt-Flag für die Zeichenübereinstimmung zu löschen, damit es nicht erneut ausgeführt wird.

So wie ich es verstehe, sind die Interrupt-Handler in STM32f7xx_it.c tatsächlich die ersten Funktionen

void DMA1_Stream6_IRQHandler(void) and void UART8_IRQHandler(void)

in meinem Fall, die ausgeführt werden, wenn Interrupts in einem Peripheriegerät auftreten, und von diesen Funktionen wird ein Handler (HAL_xxx_IRQHandler()) ausgeführt, der überprüft, welcher Interrupt aufgetreten ist, und alle benutzerdefinierten Funktionen in Abhängigkeit von den Interrupt-Flags ausführen, wie zum Beispiel:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle) {
    //process data, this runs when DMA finishes receiving bytes
}

Das Problem ist, dass nicht klar ist, welche der Interrupt-Funktionen ausgeführt wird, wenn ich zum Beispiel DMA verwende und meine angegebene Datenmenge über UART erhalten habe. Führt es den UART-Interrupt oder den DMA-Stream-Interrupt aus? Wie kann ich zuverlässig in der gleichen Callback-Funktion auf '\0' (uart?) oder 256 Bytes (dma?) landen, um meine Daten zu verarbeiten, ohne die Interrupts zu verpfuschen?

Die zu übertragende Datenmenge ist eine DMA-Funktion; Wenn Sie diesen Wert erreichen, wird der DMA-Handler aufgerufen. Ich würde ein einfaches globales wie 'CMATCH' verwenden und es setzen, wenn das CMF-Flag gesetzt ist (der USART-Interrupt wird durch dieses Flag ausgelöst) und das verwenden, um einen zuverlässigen Funktionsablauf zu erhalten.
Brauchen Sie eigentlich DMA? Das ist ein ziemlich schneller Prozessor, der wahrscheinlich nicht durch das Ausführen eines Empfangsinterrupts mit typischen Baudraten gestört wird. Während Sie also vielleicht herausfinden können, wie Sie dies mit DMA zum Laufen bringen, könnte es konzeptionell sauberer und schneller sein, alles in Software zu entwickeln und zu entwickeln können Sie möglicherweise schneller zum nächsten echten Blockierungsproblem Ihres Projekts übergehen ... und gleichzeitig die Portabilität offen lassen, um möglicherweise eines Tages eine kostenreduzierende Portierung auf ein einfacheres Ziel ohne die Zeichenabgleichsfunktion durchzuführen. Wenn Sie die Hardware-Unterstützung benötigen, machen Sie es, aber wenn nicht, überlegen Sie sich eine Strategie.
@ChrisStratton Dieser Mikrocontroller wird 2 serielle Ports mit 6 MBaud ausführen, während er andere Dinge auf der CPU erledigt. Also ja, Leistung ist definitiv ein Problem.

Antworten (2)

Das DMA-CNDTR-Register zählt nach jeder Übertragung auf Null herunter und zeigt an, wie viele Elemente noch zu übertragen sind. Sie können damit überprüfen, ob der DMA läuft oder nicht.

Ihre UART-Interrupts laufen, falls aktiviert, immer neben den DMA-Übertragungen und -Interrupts, sodass die UART-Interrupts das CNDTR-Register des DMA überprüfen müssen, um zu wissen, ob sie tatsächlich ausgeführt werden sollen oder nicht.

Sie können Interrupts auch innerhalb von Interrupts deaktivieren. Ihr DMA-Transfer-Abschluss-Interrupt kann also Ihren Charakter-Empfangs-Interrupt deaktivieren (oder umgekehrt) und Sie können ihn an anderer Stelle wieder aktivieren. Denken Sie daran, dass Sie auch Interrupt-Prioritäten handhaben müssen, wenn die letzte DMA-Übertragung ein "\0"-Zeichen ist und gleichzeitig sowohl die DMA-Übertragung abgeschlossen als auch die Zeichenübereinstimmungs-Interrupts auslöst.

In meinem Code verwende ich die Zeichenübereinstimmung, um die Eingabetaste zu erkennen, um zu wissen, dass ein Benutzerbefehl eingegeben wurde, und um die vom DMA gespeicherte Nachricht im Speicher zu analysieren. Ich habe auch einen UART-Empfangsinterrupt, um Zeichen zu echoen, der das Zeichen direkt aus dem UART-Empfangsregister zurückgibt. Diese laufen alle gleichzeitig zusammen mit den DMA-Übertragungen und Interrupts.

Wenn Sie einen UART-Interrupt pro Zeichen ausführen, ist es nicht sehr sinnvoll, DMA auf der Empfangsseite überhaupt zu verwenden. Es kann sinnvoll sein, neben DMA nur den Zeichenübereinstimmungs-Interrupt als beratende Bedingung zu verwenden, aber dann kann es auch sinnvoll sein, das Flag nur zu Zeiten abzufragen, wenn es bequem wäre, mit dem Ergebnis umzugehen .
Wenn das Echo aktiviert ist, tut es dies, aber dies wird in einem symmetrischen PCB-Stapel verwendet, wo Endplatinen mit dem Benutzer kommunizieren, Zwischenplatinen jedoch miteinander kommunizieren. Sie führen alle die gleiche Software aus und das Echo ist für Zwischenplatinen nicht aktiviert, wodurch die CPU daran gehindert wird, einzugreifen, bis die Nachricht vollständig ist. Es gibt auch einige kreisförmige Puffergeschäfte, die CPU-Zyklen fressen würden, wenn sie in Interrupt-Code statt in DMA-Hardware ausgeführt würden. Ich muss meine Interrupts für die Motorkommutierung speichern.
Mein Zeichenübereinstimmungs-Interrupt fügt einem Zähler nur eine +1 hinzu, damit die CPU weiß, wie viele Nachrichten im Puffer zu parsen sind, da nicht garantiert werden kann, dass eine Nachricht bearbeitet werden kann, bevor eine andere eintrifft (aufgrund des Vorhandenseins von machine-to -machine-messages), bevor ein anderer hereinkommt, also kann ich nicht einfach abfragen. Es ist effizienter, als jedes Mal bis zum Ende des Puffers nach weiteren zu analysierenden Nachrichten zu suchen.
@Toor Was meinst du damit, dass es einem Zähler +1 hinzufügt? Was ist das genau für ein Register, denn das würde ungemein helfen.
Es ist kein Register. Es ist nur eine Zählervariable, die ich mache. Der Character-Match-Interrupt geht auf +1 an die Variable, und der Parsing-Algorithmus, der in der Hauptprogrammschleife läuft (im Grunde eine Form der Abfrage), geht bei jedem Parsing auf -1, bis er Null erreicht. Auf diese Weise kann ich mehrere Nachrichten puffern, bevor ich sie parse, und wenn ich sie parse, muss ich nicht den Rest des Puffers durchsuchen, um zu prüfen, ob noch eine Nachricht vorhanden ist. Es hält den Character-Match-Interrupt kurz.
Ich denke, das CMF-Bit wird auch dann gesetzt, wenn der Interrupt deaktiviert ist. Wenn Sie also nur True/False im Vergleich zur Anzahl der zu analysierenden Nachrichten verwenden möchten, können Sie den Zeichenübereinstimmungs-Interrupt einfach deaktiviert lassen und das CMF-Flag abfragen und zurücksetzen. Zurücksetzen durch Schreiben von 1 in CMCF.

Ich habe nicht mit STM32F7 gearbeitet, aber Sie können dieses Repository überprüfen . Ich habe eine Schritt-für-Schritt-Anleitung zur Verwendung von DMA mit Zeichenübereinstimmung und Empfänger-Timeout für STM32L4-MCUs geteilt. Wie ich sehen kann, sehen die Register für beide MCUs ähnlich aus.

Der Link zu dem von Ihnen erwähnten Repository schien bei mir nicht zu funktionieren. Ich habe Ihr UART Utils-Repository gefunden, aber es enthält nicht viel. Ich hätte großes Interesse an Ihrer DMA mit Character Match und Receiver Timeout App. Befindet es sich in einem anderen Repository? Danke
@Gene Schauen Sie sich den ChibiOS STM32 HAL an, der DMA mit Zeichenübereinstimmung unterstützt. Es steht unter der Apache-Lizenz. chibios.org/dokuwiki/doku.php?id=chibios:product:hal:start