STM32 DMA aktivieren in USART-Interrupt -> Byte doppelt

OP #2046960
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
}
Angehängte Dateien:
#2047024
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
OP #2047092
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
}

Antwort schreiben

Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.