STM32 USARt3 DMA Rx kopiert keine Daten

OP #7613299
Lesenswert?

Hallo,

ich nutze einen STM32F413VGT6 und die USART3. Ich habe einen n Byte großen Buffer, in dem circular die empfangenen Daten kopiert werden sollen.

In der Main Loop schicke ich sekündlich über die USART Daten raus und kann diese mit Putty lesen, das klappt also schonmal.

Ebenfalls zyklische schaue ich, ob NDTR sich verändert hat, also Zeichen angekommen sind. Wenn ich in putty ein Zeichen eintippe sehe ich auch, dass einmal in die If Verzweigung gesprungen wird und NDTR um +1 verringert wird, es ist also etwas passiert. Putty schickt kein CR oder LF mit. Die richtige Kombi aus Channel und Stream scheine ich auch gewählt zu haben, andernfalls sollte ja nichts passieren.

Die DMA Fifo ist beim Start initial deaktiviert.

Es gab mal bei manchen STM32 die Notwendigkeit, irgendwas mit (volatile uint8_t*) zu casten, da habe ich aber auch schon rumprobiert.

Im Buffer sind aber nur '\0' zu sehen. Ein Zeichen klassisch ohne DMA zu lesen mittels folgendem funktioniert:

1
while (!(oConfig.oUSART->SR & USART_SR_RXNE));
2
return oConfig.oUSART->DR;

Hier ein paar Code Ausschnitte:

USART Initialisierung:

1
    memset(buf_rx, 0, BUFFER_SIZE);
2

3
    DMA1->LIFCR = 0xffffffff;
4
    DMA2->LIFCR = 0xffffffff;
5

6
    dma = DMA1_Stream1;
7
    oUSART = USART3;
8

9
    dma->CR &= ~DMA_SxCR_EN;
10
    dma->CR |= 4 << DMA_SxCR_CHSEL_Pos;
11
    dma->PAR = (uint32_t)&oUSART->DR;
12
    dma->M0AR = (uint32_t)&buf_rx[0];
13
    dma->M1AR = (uint32_t)&buf_rx[0];
14
    dma->NDTR = BUFFER_SIZE;
15
    dma->CR |= DMA_SxCR_PL_1;
16
    dma->CR |= DMA_SxCR_PL_0;
17
    dma->CR |= DMA_SxCR_MINC;
18
    dma->CR |= DMA_SxCR_CIRC;
19
    //dma->CR |= DMA_SxCR_PSIZE_1;
20
    //dma->CR |= DMA_SxCR_MSIZE_1;
21
    dma->CR |= DMA_SxCR_EN;
22

23
    oUSART->CR1 &= ~USART_CR1_UE;
24
    oUSART->CR3 |= USART_CR3_DMAR;
25
    oUSART->CR1 = USART_CR1_TE | USART_CR1_RE;
26
    oUSART->CR1 |= USART_CR1_UE;
27

28
    setBaudrate(oConfig.u32Baudrate);

Main loop:

1
uint16_t USART::dataAvailabe() {
2
    uint16_t z = (BUFFER_SIZE - dma->NDTR);
3

4
    if (pos != z) {
5
        if (z > pos) {
6
            return z - pos;
7
        }
8
        else {
9
            return BUFFER_SIZE - pos + z;
10
        }
11
    }
12

13
    return 0;
14
}
15

16
uint8_t USART::get() {
17
    uint8_t data;
18
    //while (!(oConfig.oUSART->SR & USART_SR_RXNE));
19
    //return oConfig.oUSART->DR;
20
    if (dataAvailabe()) {
21
        data = buf_rx[pos];
22
        pos++;
23
        if (pos >= BUFFER_SIZE) {
24
            pos = 0;
25
        }
26
    }
27

28
    return data;
29
}
30

31

32
while(1){
33
        if(oHardware.oPort[0].dataAvailabe()){
34
            oHardware.oPort[0].send(oHardware.oPort[0].get());
35
        }
36

37
        if(t.Ready()){
38
            oHardware.oPort[0].enable(oHardware.oPort[0].getEnable() ? false : true);
39

40
            t.StartMs(1000);
41
            String s = "ADC: ";
42
            s += oHardware.oPort[0].getVoltage();
43
            s += "mV\r\n";
44
            oHardware.oPort[0].send(s.c_str());
45
        }
46
    }
#7613428
Lesenswert?

Womit hast du denn deinen Code geschrieben? Ausser ein paar Code-Fragmenten wissen wir (fast) nichts.

Du hast zwar ein Problem (halbwegs) dokumentiert, aber was soll der geneigte Leser denn damit anfangen? Was wäre denn dein Anliegen bzw. deine Fragestellung?

Ich sehe nichts von einer Interrupt-Service-Routine die aufgerufen werden müsste wenn dein DMA-Kanal etwas zu melden hätte. Das sind - in STM32-Sprachraum - die Funktionen

void DMA1_Stream1_IRQHandler(void) // Rx Handler void DMA1_Stream3_IRQHandler(void) // Tx Handler

für den USART3.

#7613449
Lesenswert?

Ist DMA für UART-Empfang nicht ein kleines bisschen übertrieben? Oder hast Du in Deiner eigentlichen Anwendung vor, mit sehr hohen Baudraten und großen, mehr oder weniger lückenlos übertragenen Datenblöcken zu arbeiten?

Für simple Benutzerinteraktion und sehr, sehr vieles mehr reicht ein stinknormaler Interrupthandler, in dem ein paar Zeilen Code empfangene Zeichen aus dem Rx-Buffer der UART rausholen und in ein Rx-Fifo (Speicher) eintragen.

Dafür braucht's wirklich kein DMA, es sei denn, die eigentliche Anwendung auf dem µC ist so ultrahochtimingkritisch, daß selbst ein banaler UART-Interrupt das Ding schon durcheinanderbringt.

Dann aber ist im Design eh' schon irgendwas kaputt.

#7613457
Lesenswert?

"NDT[15:0]: number of data items to transfer (0 up to 65535) ... This register decrements after each DMA transfer. Once the transfer is completed, this register can either stay at zero (when the stream is in normal mode) or be reloaded automatically with the previously programmed value in the following cases: – when the stream is configured in circular mode. ..."

Sprich: Im Ringbuffer-Modus ist die Prüfung auf Füllstand mittels NDT nicht sinnvoll.

OP #7613479
Lesenswert?

Wastl schrieb:

void DMA1_Stream1_IRQHandler(void) // Rx Handler void DMA1_Stream3_IRQHandler(void) // Tx Handler

Ich benötige keine Interrupts. Der DMA schreibt in einen Buffer parralel und somit kann ich im Hauptzyklus überprüfen, ob neue Daten eingetroffen sind.

Natürlich kann ich das auch mit Interrupts, aber wenn ich bis zu 10 UARTS habe und diese im Programm auch dynamisch neu zuordne möchte ich solch eine statische Programmierung vermeiden, denn der Interrupt muss immer in Form einer globalen Variable wissen, wo die Daten hin sollen. Mache ich das mit DMA kann ich es einfach am Ort des Aufrufs der Initialisierung entscheiden und festlegen mit dem PAR Register.

Andreas B. schrieb:

Sprich: Im Ringbuffer-Modus ist die Prüfung auf Füllstand mittels NDT nicht sinnvoll.

Doch, ich merke mir den vorherigen Zustand vom NDTR Register. Eine Änderung lässt darauf schließen, dass Daten eingetroffen sind. Es liegt an mir, den Buffer genügend groß zu machen.

Aber darum geht es ja nicht. Es ist funktioniert ja im Grunde alles, bei jedem Eintreffen wird der DMA scheinbar ausgeführt, denn NDTR wird decrementiert, nur werden keine Daten in den Buffer kopiert.

Und ich bin mir sicher, dass ich einfach irgendwo ein Flags in der Hardwarekonfiguration übersehen habe.

J. S. schrieb:

Für eine Ringbuffer Implementierung mit DMA gibt es vom Tilen Majerle eine gute Erklärung bzw Code: https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx

Schaue ich mir mal an.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren