Question
UART Rx with DMA - missing bytes
Posted on May 01, 2014 at 23:28
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 400typedef 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;}