Forum: Mikrocontroller und Digitale Elektronik STM32 DMA aktivieren in USART-Interrupt -> Byte doppelt


von Janis W. (jotwe)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich habe hier einen Effekt, den ich nicht nachvollziehen kann: Ich 
empfange Datenpakete an der USART1 eines STM32, die im vierten Byte eine 
Längeninformation enthalten. Sobald ich diese Längeninformation habe, 
möchte ich von Interrupt-Betrieb auf DMA umschalten. Dazu konfiguriere 
ich in der USART-Interrupt-Routine den DMA:
1
// Configure and activate DMA for reception
2
DMA_Cmd(DMA1_Channel5, DISABLE);
3
// Set DMA address
4
DMA1_Channel5->CMAR = (uint32_t)&(receptionBuffer[bufferWriteIndex].buffer[4]);
5
// Set received length
6
DMA1_Channel5->CNDTR = receptionBuffer[bufferWriteIndex].buffer[3];
7
DMA_Cmd(DMA1_Channel5, ENABLE);

Nun wird aber - für mich überraschend - in meinem Buffer das Längen-Byte 
zweimal abgelegt, einmal durch den USART-Interrupt (gewünscht) und 
einmal durch den DMA (unerwünscht). Aus einem Paket

0xFF 0xFF 0x01 0x02 0x01 0xFB

mit der Längeninformation 0x02 wird damit

0xFF 0xFF 0x01 0x02 0x02 0x01. Eigentlich müsste durch das Auslesen per
1
// Get received byte
2
receivedByte = USART_ReceiveData(USART1);

doch der USART-Buffer geleert worden sein?! Auch ein vorheriges 
manuelles Rücksetzen des RXNE-Flags ändert nichts. Warum erkennt der DMA 
das Byte trotzdem noch einmal als "neu" empfangen? Im Reference Manual 
konnte ich bisher keine Erklärung finden...

Im Anhang ist mein kompletter Quellcode mit Initialisierung der USART 
und des DMA. Hier meine etwas verkürzte USART-Interrupt-Routine:
1
void USART1_IRQHandler(void) {
2
3
// Private variables
4
uint8_t receivedByte;
5
6
// USART receive data register is not empty
7
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
8
9
// Get received byte
10
receivedByte = USART_ReceiveData(USART1);
11
12
// If packet does not start with two start bytes, refuse packet and reset
13
// reception buffer
14
if ((receptionBuffer[bufferWriteIndex].length < 2) && (receivedByte != 0xFF)) {
15
16
// Reset reception buffer
17
receptionBuffer[bufferWriteIndex].length = 0;
18
19
return;
20
21
}
22
23
// If packet length is not valid, refuse packet and reset reception buffer
24
if ((receptionBuffer[bufferWriteIndex].length == 3) && ((receivedByte < 2) || (receivedByte > 250))) {
25
26
// Reset reception buffer
27
receptionBuffer[bufferWriteIndex].length = 0;
28
29
return;
30
31
}
32
33
// Copy received byte to buffer and increase buffer length
34
receptionBuffer[bufferWriteIndex].buffer[receptionBuffer[bufferWriteIndex].length++] = receivedByte;
35
36
// If packet length is known, configure and activate DMA for reception
37
if (receptionBuffer[bufferWriteIndex].length == 4) {
38
39
// Configure and activate DMA for reception
40
DMA_Cmd(DMA1_Channel5, DISABLE);
41
// Set DMA memory address
42
DMA1_Channel5->CMAR = (uint32_t)&(receptionBuffer[bufferWriteIndex].buffer[4]);
43
// Set DMA length
44
DMA1_Channel5->CNDTR = receptionBuffer[bufferWriteIndex].buffer[3];
45
DMA_Cmd(DMA1_Channel5, ENABLE);
46
47
}
48
49
}
50
51
}

von Achim M. (minifloat)


Lesenswert?

Datenblatt und errata genau angesehen?
Hersteller konsultiert(Mail/Telefon)?

Und als dreckiger Workaround: Längenangabe auslesen aber zunächst nicht 
in deinen (zu dem Zeitpunkt noch Software-)Puffer stecken. Dann DMA 
anmachen, weitere Bytes liegen im Puffer, dann dein Längenangabe Byte 
über das vom DMAC unerwünscht erzeugte drüberschreiben.
mfg mf

von Janis W. (jotwe)


Lesenswert?

Hi Mini Float,

so einen ähnlichen "dreckigen" Workaround habe ich zur Zeit laufen. 
Leider habe ich gelegentlich Übertragungsfehler und wollte es deshalb 
mal "sauber" lösen...

von (prx) A. K. (prx)


Lesenswert?

Ist der DMA abgeschaltet wenn alle seine Bytes durch sind? Oder hängt 
der mit count=0 aber enabled und hat sich den Request irgendwie gemerkt?

von Janis W. (jotwe)


Lesenswert?

Im DMA-Interrupt setze ich das Transfer-Complete-Flag zurück und 
deaktiviere den DMA:
1
// DMA interrupt handler
2
void DMA1_Channel5_IRQHandler(void) {
3
4
// DMA transfer complete
5
if (DMA_GetITStatus(DMA1_IT_TC5)) {
6
7
// Clear interrupt flag
8
DMA_ClearITPendingBit(DMA1_IT_TC5);
9
10
// Deactivate DMA
11
DMA_Cmd(DMA1_Channel5, DISABLE);
12
13
...
14
15
}

von (prx) A. K. (prx)


Lesenswert?

Schon versucht, DMAR (UART Bit) passend dazu aus/einzuschalten?

von Janis W. (jotwe)


Lesenswert?

Hm, bisher nicht. Das aktiviere ich bei der Initialisierung und lasse es 
dann immer aktiviert. Aber das könnte in der Tat eine Möglichkeit sein. 
Werde das morgen direkt mal probieren. Danke für den Hinweis!

von Janis W. (jotwe)


Lesenswert?

Hallo,

ich habe das DMAR-Bit in USART_CR3 jetzt berücksichtigt und die 
Kommunikation eine Weile getestet. Es scheint wirklich zu funktionieren! 
Das doppelte Byte ist nicht mehr da und die Kommunikation läuft stabil.

Vielen Dank nochmal für alle Tipps!

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.