cancel
Showing results for 
Search instead for 
Did you mean: 

DMA USART receive data is returning DMA USART transmit data.

ChrisFyberLabs
Associate II

Hi, I am having the weirdest situation. I setup LL DMA transfers for both transmit and receive on USART2. I let stm32cubemx do its partial init, which seems to work:

void radioUART_init(void) {
    //Turn off USART while we finish non-templated options
    LL_USART_Disable(RADIO_USART);
    LL_USART_DisableDMAReq_RX(RADIO_USART); //RX DMA int off
    LL_USART_DisableDMAReq_TX(RADIO_USART); //TX DMA int of
    LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_6); //TX off
    LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_5); //RX off
 
    //TX setup
    LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_6, (uint32_t)&RADIO_USART->DR);
    LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_6, (uint32_t)dma_tx_buf);
    LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_6, strlen(tx_buffer));
 
    LL_DMA_EnableIT_TE(DMA1, LL_DMA_STREAM_6);//TX enable transfer error int
    LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_6);//TX enable transfer complete int
    //LL_DMA_EnableIT_FE(DMA1, LL_DMA_STREAM_6);//TX enable fifo error int
    LL_DMA_EnableIT_DME(DMA1, LL_DMA_STREAM_6);//TX enable direct mode error int
    LL_DMA_DisableIT_HT(DMA1, LL_DMA_STREAM_6);
    LL_DMA_DisableIT_FE(DMA1, LL_DMA_STREAM_6);
 
    LL_DMA_ClearFlag_TC6(DMA1); //TX Transfer complete clear
    LL_DMA_ClearFlag_TE6(DMA1); //TX Transfer error clear
    LL_DMA_ClearFlag_FE6(DMA1); //TX Fifo error clear
    LL_DMA_ClearFlag_HT6(DMA1);
    LL_DMA_ClearFlag_DME6(DMA1);
 
    //RX setup
    //To start a RX USARTx_CR1_RE must be set
    LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_5, (uint32_t)&RADIO_USART->DR);
    LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_5, (uint32_t)rx_buffer);
    LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_5, RX_DMA_BUF_SIZE);
 
    LL_DMA_EnableIT_HT(DMA1, LL_DMA_STREAM_5);//RX enable half transfer int
    LL_DMA_EnableIT_TE(DMA1, LL_DMA_STREAM_5);//RX enable transfer error int
    LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_5);//RX enable transfer complete int
    LL_DMA_EnableIT_FE(DMA1, LL_DMA_STREAM_5);//RX enable fifo error int
    LL_DMA_EnableIT_DME(DMA1, LL_DMA_STREAM_5);//RX enable direct mode error int
 
    LL_DMA_DisableIT_HT(DMA1, LL_DMA_STREAM_5);//RX disable half transfer int
    LL_DMA_DisableIT_FE(DMA1, LL_DMA_STREAM_5);//RX disable fifo error int
 
    LL_DMA_ClearFlag_TC5(DMA1); //RX Transfer complete clear
    LL_DMA_ClearFlag_TE5(DMA1); //RX Transfer error clear
    LL_DMA_ClearFlag_FE5(DMA1); //RX Fifo error clear
    LL_DMA_ClearFlag_HT5(DMA1);
    LL_DMA_ClearFlag_DME5(DMA1);
 
 
    LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_6); //TX DMA channel on
    LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_5); //RX DMA channel on
    LL_USART_Enable(USART2);
    LL_USART_EnableDMAReq_TX(USART2); //TX DMA int on
    LL_USART_EnableDMAReq_RX(USART2); //RX DMA int on
    LL_USART_EnableIT_IDLE(USART2);   //Detect TX(and RX?) IDLEs
    radio_receive = true;
    //RX DMA should be up and listening now
}

I then transmit state on start up, but the data returns back on the receiver buffer. Also, the transmit is throwing DMA FIFO errors.

Transmit function, I assume the DMA is disabled as this can't be called again in its wrapper function unless any current transmit has succeeded with a TC.

void radioUART_Transmit(void) {
    LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_6, (uint32_t)&RADIO_USART->DR);
    LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_6, (uint32_t)dma_tx_buf);
    LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_6, strlen(tx_buffer));
    LL_DMA_ClearFlag_TC6(DMA1); //TX Transfer complete clear
    LL_DMA_ClearFlag_TE6(DMA1); //TX Transfer error clear
    LL_DMA_ClearFlag_FE6(DMA1); //TX Fifo error clear
    LL_DMA_ClearFlag_HT6(DMA1);
    LL_DMA_ClearFlag_DME6(DMA1);
 
    LL_DMA_DisableIT_HT(DMA1, LL_DMA_STREAM_6);
    LL_DMA_DisableIT_FE(DMA1, LL_DMA_STREAM_6);
 
    LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_6); //TX DMA channel on
 
    LL_USART_Enable(USART2);
    LL_USART_EnableDMAReq_TX(USART2); //TX DMA int on
}

Receive is a mix of two DMA with IDLE examples online with a systick based timer(dma_uart_rx struct) after IDLE. The interrupt passes data to a message parser/builder. At some point I would like to implement a HT call to pass to the parser, but I figured small things like buffer corruption should be fixed first.

void radioUART_Receive_IRQHandler(void) {
    if(LL_DMA_IsActiveFlag_TE5(DMA1)) //TX Transfer error set)
    {
        LL_DMA_ClearFlag_TE5(DMA1); //TX Transfer error clear
        Log(LOG_ERROR, "RX DMA Transfer Error");
    }
 
    if(LL_DMA_IsActiveFlag_FE5(DMA1)) //TX FIFO error set)
    {
        LL_DMA_ClearFlag_FE5(DMA1); //TX FIFO error clear
        Log(LOG_ERROR, "RX DMA FIFO or init Error");
    }
 
    if(LL_DMA_IsActiveFlag_DME5(DMA1)) //TX DME error set)
    {
        LL_DMA_ClearFlag_DME5(DMA1); //TX DME error clear
        Log(LOG_ERROR, "RX DMA Direct Mode Error");
    }
 
    if(dma_uart_rx.flag)    /* Timeout event */
    {
        volatile uint16_t currCNDTR = LL_DMA_GetDataLength(DMA1, LL_DMA_STREAM_5);
        if(currCNDTR == RX_DMA_BUF_SIZE)
        { 
            dma_uart_rx.flag = 0;
            return;
        }
        else {
            radioBufferReceive(rx_buffer, 0, RX_DMA_BUF_SIZE-currCNDTR);
        }
        dma_uart_rx.prevCNDTR = RX_DMA_BUF_SIZE;
        dma_uart_rx.flag = 0;
 
        LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_5, (uint32_t)&RADIO_USART->DR);
        LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_5, (uint32_t)rx_buffer);
        LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_5, RX_DMA_BUF_SIZE);
        LL_DMA_ClearFlag_TC5(DMA1); //TX Transfer complete clear
        LL_DMA_ClearFlag_HT5(DMA1);
        volatile uint32_t tmp;                  /* Must be volatile to prevent optimizations */
        tmp = RADIO_USART->SR;                       /* Read status register */
        tmp = RADIO_USART->DR;                       /* Read data register */
        (void)tmp;                              /* Prevent compiler warnings */
        radio_receive = true;
        LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_5); //TX DMA channel on;
    } else {
 
        if(LL_DMA_IsActiveFlag_HT5(DMA1)) {
            LL_DMA_ClearFlag_HT5(DMA1);
        }
 
        if (LL_DMA_IsActiveFlag_TC5(DMA1)) {
            LL_DMA_ClearFlag_TC5(DMA1);
            dma_uart_rx.prevCNDTR = RX_DMA_BUF_SIZE;
            radioBufferReceive(rx_buffer, 0, RX_DMA_BUF_SIZE); 
 
            LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_5, (uint32_t)&RADIO_USART->DR);
            LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_5, (uint32_t)rx_buffer);
            LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_5, RX_DMA_BUF_SIZE);
            LL_DMA_ClearFlag_TC5(DMA1); //TX Transfer complete clear
 
            volatile uint32_t tmp;                  /* Must be volatile to prevent optimizations */
            tmp = RADIO_USART->SR;                       /* Read status register */
            tmp = RADIO_USART->DR;                       /* Read data register */
            (void)tmp;                              /* Prevent compiler warnings */
            LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_5); //RX DMA channel on;
        }
    }
}

Has anyone seen transmit appear on the receive buffer before? I am not sure if it is some weird effect of running it under Atollic's debug or ? As you can see, RX_DMA_BUF_SIZE is the only data length ever set before the receive DMA is enabled. So there is no weird memory violation.

Thanks for any help!

This discussion is locked. Please start a new topic to ask your question.
1 REPLY 1
ChrisFyberLabs
Associate II

The problem was with the ****** RPI defaults for uarts. The TX DMA error was from not disabling the DMA request and then enabling it after setting up transmit again.