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!

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.