AnsweredAssumed Answered

UART Rx with DMA - missing bytes

Question asked by lanham.s on May 1, 2014
Latest reply on Mar 22, 2017 by waclawek.jan
Hello there,


I have an interesting problem. I am writing a hardware abstraction wrapper for UARTs on an STM32F427, and I want this particular implementation to use DMA for receiving data. I am not using DMA to transmit data. Occasionally and unpredictably, the UART DMA request will happen, (I know this because the DMAxStreamy->NDTR register will decrement), but the byte from the UART will not be copied into the buffer. 


As part of my abstraction interface, I need to determine how many valid bytes I have in my buffer. To help determine this, I read the NDTR register. The above error occurs when I read this register approximately when data is coming in the UART. Could this be creating the issue? I have pasted configuration code and the code for reading the data below. Thank you for the help!
#define HAL_DMA_IN_BUFFER_SIZE 400
#define HAL_DMA_OUT_BUFFER_SIZE 400


typedef struct
{
     volatile uint8_t buffer[HAL_DMA_IN_BUFFER_SIZE];
     volatile uint8_t *  nextUnused;
     DMA_Stream_TypeDef * dma_stream;
} HAL_DmaInBufferType;


typedef struct
{
     uint8_t buffer[HAL_DMA_OUT_BUFFER_SIZE];
     uint8_t * nextUnsentByte;
     uint8_t * nextOpenByte;
     uint16_t size;
} HAL_DmaOutBufferType;


static HAL_DmaInBufferType _uartDmaBufferIn = {.nextUnused = NULL, .dma_stream = NULL };


static HAL_DmaOutBufferType _uartDmaBufferOut = {.nextUnsentByte = NULL, .nextOpenByte = NULL, .buffer = {0xA5}, .size = 0 };


int HAL_UartInitializeDevice()
{
     DMA_InitTypeDef  DMA_InitStructure;
     USART_TypeDef *uart_base = UART4;


     ///- enable the UART blocks clock input
     RCC_APB1PeriphClockCmd( RCC_APB1Periph_UART4,  ENABLE );


     ///- choose the correct clock, stream, and channel
     _uartDmaBufferIn.dma_stream = DMA1_Stream2;
     DMA_InitStructure.DMA_Channel = DMA_Channel_4;
     DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&UART4->DR;
     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
     
     ///- Fill buffer with watermarks
     memset((void *)_uartDmaBufferIn.buffer, 0xA5, HAL_DMA_IN_BUFFER_SIZE);


     ///- Deinit the DMA stream
     DMA_DeInit(_uartDmaBufferIn.dma_stream);


     ///- configure the DMA
     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
     DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&_uartDmaBufferIn.buffer[0];
     DMA_InitStructure.DMA_BufferSize = HAL_DMA_IN_BUFFER_SIZE;
     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
     DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
     DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;


     ///- initialize the DMA
     DMA_Init(_uartDmaBufferIn.dma_stream, &DMA_InitStructure);


     ///- enable the DMA RX Stream
     DMA_Cmd(_uartDmaBufferIn.dma_stream, ENABLE);


     ///- enable the USART Rx DMA request
     USART_DMACmd(uart_base, USART_DMAReq_Rx, ENABLE);


     ///- disable DMA Stream Transfer Complete interrupt
     DMA_ITConfig(_uartDmaBufferIn.dma_stream, DMA_IT_TC, DISABLE);


     ///- initialize the buffers
     _uartDmaBufferIn.nextUnused = _uartDmaBufferIn.buffer;
     _uartDmaBufferOut.nextOpenByte = _uartDmaBufferOut.buffer;
     _uartDmaBufferOut.nextUnsentByte = _uartDmaBufferOut.buffer;


     ///- Enable the Tx interrupt
     USART_ITConfig( uart_base, USART_IT_TXE, ENABLE );


     ///- Enable for Tx
     NVIC_InitTypeDef NVIC_InitStructure = {
               .NVIC_IRQChannelPreemptionPriority = 0x01,
               .NVIC_IRQChannelSubPriority = 0x01,
     };
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
     NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;
     NVIC_Init(&NVIC_InitStructure);
     
     ///- Configure UART
     USART_InitTypeDef UART_InitStructure;
     USART_StructInit( &UART_InitStructure );


     UART_InitStructure.USART_BaudRate = baud_rate;
     UART_InitStructure.USART_WordLength = USART_WordLength_8b;
     UART_InitStructure.USART_StopBits = USART_StopBits_1;
     UART_InitStructure.USART_Parity = USART_Parity_No;
     UART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
     UART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;


     // now let's apply it all
     USART_Init( uart_base, &UART_InitStructure );
     USART_Cmd( uart_base, ENABLE );
}


int HAL_UartRecv(char *str, int str_len )
{
     uint32_t nextOpen, arrayBoundary;
     int readCount = 0;


     ///- calculate first invalid address
     arrayBoundary = (uint32_t)_uartDmaBufferIn.buffer + HAL_DMA_IN_BUFFER_SIZE;


     ///- calculate DMA pointer to next open byte
     nextOpen = arrayBoundary - _uartDmaBufferIn.dma_stream->NDTR;


     ///- copy bytes into buffer
     while(readCount < str_len)
     {
          ///- no more unread bytes are available
          if((uint32_t)_uartDmaBufferIn.nextUnused == nextOpen)
          {
               break;
          }


          ///- copy data over
          *str++ = (char) *_uartDmaBufferIn.nextUnused++;
          readCount++;


          ///- check array boundary
          if((uint32_t)_uartDmaBufferIn.nextUnused >= arrayBoundary)
          {
               _uartDmaBufferIn.nextUnused = _uartDmaBufferIn.buffer;
          }
     }


     ///- return the read count
     return readCount;
}

Outcomes