stm32halstm32cubeide

STM32F4 Uart data forwarding uses DMA and packet loss occurs


I am trying to develop a serial port forwarding project in STM32F405RGT6 where data is received by Serial Port 2 (baud rate: 460800, DMA circular receive, data stored in RINGBUF), and then the data is transmitted through Serial Port 3 (baud rate: 460800, DMA send, data read from RINGBUF). However, I have encountered a perplexing issue. When I open two serial port tools on my PC for sending and receiving, the size of the received data does not match the size of the sent data. The received data is less than the sent data.

This is rx code:

uint8_t _rx_process(User_UARTRX_T *c)
{
    uint16_t len = 0;
    c->pos = c->rx_size - __HAL_DMA_GET_COUNTER(c->huart->hdmarx);
    c->len = 0;
    if (c->pos != c->oldpos)
    {
        if (c->pos > c->oldpos)
        {
            /* Processing is done in "linear" mode. */
            c->len = c->pos - c->oldpos;
            ringbuffer_put_force(c->ring, &(c->dmabuf[c->oldpos]), c->len);
        }
        else
        {
            /* Processing is done in "overflow" mode. */
            len = c->rx_size - c->oldpos;
            ringbuffer_put_force(c->ring, &(c->dmabuf[c->oldpos]), len);
            c->len = len;
            if (c->pos > 0)
            {
                ringbuffer_put_force(c->ring, &(c->dmabuf[0]), c->pos);
                c->len = c->pos + len;
            }
        }
        c->oldpos = c->pos;
    }

    return (c->len == 0 ? 0 : 1);
}

static void User_UartRx_Callback(UART_HandleTypeDef *huart)
{
        if (huart->Instance == USART2)
    {
        osThreadFlagsSet(User_UartRxHandle, _SIG_UR2);
    }

}

void User_UartRxTask(void *argument)
{
    /* USER CODE BEGIN User_UartRxTask */
    uint8_t ch;
    uint16_t i = 0, len = 0;
    uint32_t signal;

    /* Infinite loop */
    for (;;)
    {
        signal = osThreadFlagsWait(_SIG_RXALL, osFlagsWaitAny, osWaitForever);
        if (signal & _SIG_UR2)
        {
            if (_rx_process(&crx[1]))
            {
                crx[1].stream_cnt += crx[1].len;
                len = ringbuffer_data_len(&uartRxBuf[1]);
                for (i = 0; i < len; ++i)
                {
                    ringbuffer_getchar(&uartRxBuf[1], &ch);
                    ringbuffer_putchar(&uartTxBuf[1], ch);
                    User_SDbuf_WR(0, &ch, 1);
                }
            }

        }

    }
    /* USER CODE END User_UartRxTask */
}

void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
{
    User_UartRx_Callback(huart);
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    User_UartRx_Callback(huart);
}

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    UNUSED(Size);
    User_UartRx_Callback(huart);
}

Function triggered by DMA transmit complete, half transmit, and idle interrupts, to transfer received data from the DMA buffer to the Ringbuffer and save it to the buffer used for serial port forwarding.

This is tx code:

uint8_t dma_OK = 1;
uint32_t rcv_T3 = 0, rcv_dma = 0;
void User_UartTxTask(void *argument)
{
    /* USER CODE BEGIN User_UartRxTask */
    uint16_t len = 0, maxlen = 0;

    /* Infinite loop */
    for (;;)
    {
        len = ringbuffer_data_len(&uartTxBuf[1]);
        if (len > maxlen)
        {
            maxlen = len;
        }
        if (len > 0)
        {
            len = ringbuffer_get(&uartTxBuf[1], dmaTxBuf, _DMA_RXBUFSIZE);
            rcv_T3 += len;
            if (dma_OK == 1 && huart3.gState == HAL_UART_STATE_READY)
            {
                dma_OK = 0;
                HAL_UART_Transmit_DMA(&huart3, dmaTxBuf, len);
            }
        }
        /* USER CODE END User_UartRxTask */
        osDelay(1);
    }
}

static void User_UartTx_Callback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART3)
    {
        dma_OK = 1;
    }

}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    User_UartTx_Callback(huart);
}

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
    ;
}

Create a new thread to wait for data. If it detects that the length of txRingbuf is greater than 0, initiate DMA transmission. Set a flag for DMA in the transmit complete interrupt to prevent restarting DMA transmission if it wasn't successful.

I attempted UART2 (baud rate 119200, receiving) and UART3 (baud rate 460800, forwarding), and the code provided above runs successfully without any packet loss.

I attempted UART2 (baud rate 460800, receiving) and UART3 (baud rate 460800, forwarding), but when running the code, I encountered an issue with data loss. The statistics variables for reception and transmission, rcv_T3 and crx[1].stream_cnt, in the code are correct and match the sent data size of 434724. However, the forwarded data received on the PC is only 358834. enter image description here


Solution

  • If previous transmission did not finish i.e. dmaOK == 0, but there were data received, in User_UartTxTask() you pull those data from the ringbuffer, you even count them up to rcv_T3, but the due to

    if (dma_OK == 1 && huart3.gState == HAL_UART_STATE_READY)
    

    you simply throw those data away.

    In other words, you need to perform that above check for Tx being complete before you pull data from ringbuffer.