Ich arbeite an der Firmware für einen STM32F103, der über RS232 mit 115200 Baud mit einer Motorsteuerung kommuniziert. Die Motorsteuerung (ein Copley Xenus XTL ) arbeitet nach einem "Sprechen, wenn angesprochen"-Protokoll. Ich verwende die ASCII-Programmierschnittstelle in den verlinkten Dokumenten. Der STM32 sendet immer denselben Befehl ("g r0x18"), um ein Register abzufragen, und die Motorsteuerung antwortet mit einer variablen Anzahl (~4-10 Bytes) von Zeichen, die mit einem Wagenrücklauf abgeschlossen werden ("v 12345", wobei die Anzahl der Stellen ist variabel). Ich habe Code, um die Antwort zu analysieren und einen numerischen Wert daraus zu ziehen. Sobald die Antwort geparst ist, sollte der Register-Poll-Befehl erneut an die Motorsteuerung übertragen werden. Der STM32 liest auch einen ADC-Kanal über DMA im Zirkularmodus im Hintergrund.
Ich würde dies gerne mit dem DMA-Controller implementieren, um alles so blockierungsfrei wie möglich zu machen, aber ich bin etwas verwirrt darüber, welche Interrupts ich verwenden sollte und wann sie ausgelöst werden. Ohne Verwendung des DMA-Controllers befindet sich der Parsing-Code derzeit im USART-RXNE-Interrupt. Angenommen, ich sende einen Befehl und die Motorsteuerung beginnt zu antworten. Ich glaube, der RXNE-Interrupt wird für jedes empfangene Byte ausgelöst, aber was ist mit dem DMA-Transfer-Complete-Interrupt? Gibt es in diesem Fall überhaupt einen funktionalen Unterschied zwischen der Verwendung des DMA TC-Interrupts und des USART RXNE-Interrupts?
Sie können DMA so konfigurieren, dass es im zyklischen Modus arbeitet, und mit einem angemessen großen Puffer können Sie Zeichen mit einer einfachen poll
Funktion so oft abrufen, wie Sie möchten. Speichern Sie einfach den Index des letzten abgerufenen Zeichens und verwenden Sie das DMA-Register, um zu prüfen, wie viele Zeichen es bis zum Rollover empfangen muss (das AFAIR-Register hat NDTR
in seinem Namen), und verarbeiten Sie die seit dem letzten Aufruf empfangenen Zeichen, und aktualisieren Sie dann den Index des letzten abgerufenen Zeichens.
Die beschriebene DMA-Nutzung macht Sie poll
unabhängig von dem Kontext, aus dem Sie es aufrufen, solange Sie eine Präemption verhindern. Dann können Sie den RX-Interrupt verwenden, um Abfrageaufrufe auszulösen, oder Sie können dies in einem anderen Kontext tun (z. B. mit einigen periodischen Ereignissen).
Dies ist im Allgemeinen effizient, wenn Frames lang genug sind, die Baudrate groß ist und die Interrupt-Latenz größer ist als die Empfangszeit eines einzelnen Zeichens.
Wenn die Daten jedoch langsam genug empfangen werden, können Sie sie auf die derzeitige Weise zeichenweise verarbeiten, und die Verwendung von DMA kann übertrieben sein.
Wie @Chris Stratton betonte, können Sie DMA auch einfach für die Einzelübertragung einstellen und lange genug warten, das Protokoll ändern oder ein anderes Signal als "Ende der Übertragung" verwenden - und dann den Frame im DMA-Puffer verarbeiten.
Sie sollten DMA-Interrupts verwenden. Ich verwende STM32F02 mit STM32-STL-Peripheriebibliotheken, aber die Idee ist für F01 ziemlich gleich:
NVIC_InitTypeDef NVIC_InitStructure;
//Enable DMA1 channel IRQ Channel
//Note: maybe in your implementation you don't have DMAx_Streamy, look for channel/ stream configurations in your microcontroller manual
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream1_IRQn; // This could be different in your implementation
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Enable DMA1 Channel Transfer Complete interrupt
DMA_ITConfig(DMA1_Stream1, DMA_IT_TC, ENABLE);
USART_DMACmd(ACCESSORY_UART, USART_DMAReq_Rx, ENABLE);
DMA_Cmd(DMA1_Stream1, ENABLE);
Die Funktion USART_DMACmd bindet UART an DMA, und ich denke, das ist Ihre Antwort. Auf diese Weise kopiert der DMA jedes Mal, wenn ein RXNE-Ereignis ausgelöst wird, ein Byte in den von Ihnen in der Konfiguration bereitgestellten Zeiger, unterbricht jedoch NUR, wenn ein Ereignis „Transfer Complete“ (DMA_IT_TC) ausgelöst wird, und ruft die entsprechende DMA-Funktion gemäß dem konfigurierten Kanal auf / streamen.
JimmyB
m.Alin
the parsing code currently resides in the USART RXNE interrupt
Ein besserer Weg wäre, nur den RX-Interrupt zu verwenden, um das aktuelle Byte in einen Ringpuffer zu legen. Überprüfen Sie dann in der Hauptschleife, ob der Puffer eine vollständige Nachricht enthält, und analysieren Sie diese Nachricht.JimmyB
gbulmer
Chris Stratton
Joe Baker
Connor Wolf
A better way would be to only use the RX interrupt to put the current byte in a circular buffer.
- Dies gilt nur, wenn Sie 1. innerhalb eines begrenzten Zeitrahmens auf bestimmte Ereignisse reagieren müssen und 2. nicht über die Möglichkeiten für verschachtelte Interrupts verfügen . Da sich dies auf einem STM32 befindet, verfügt es über ein NVIC mit mehreren Interrupt-Prioritäten, sodass eine signifikante Logik in den Interrupt-Handlern durchaus akzeptabel ist.