2024-07-15 09:53 PM - edited 2024-07-15 11:15 PM
Hello,
I am implementing UART RX reception using DMA, but I am encountering an issue where the data is not correctly updated in the memory buffer connected to the DMA.
Below is the code I configured.
#define DMA_BUFFER_SIZE 10
uint8_t dma_buffer[DMA_BUFFER_SIZE];
void USART_Initialize(USART_TypeDef* USARTx, LL_USART_InitTypeDef* p_init)
{
UsartRxBuffer = xStreamBufferCreate(DMA_BUFFER_SIZE * 3, 1);
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (u8*)dma_buffer, DMA_BUFFER_SIZE);
}
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA2_CLK_ENABLE();
/* DMA2_Stream2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0x06, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
}
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(huart->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspInit 0 */
/* USER CODE END USART1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PB6 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART1 DMA Init */
/* USART1_RX Init */
hdma_usart1_rx.Instance = DMA2_Stream2;
hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;
hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart1_rx.Init.MemInc = DMA_MINC_DISABLE;
hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(huart,hdmarx,hdma_usart1_rx);
/* USART1 interrupt Init */
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspInit 1 */
/* USER CODE END USART1_MspInit 1 */
}
}
// redefined HAL_UARTEx_RxEventCallback
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
u32 recvLength = 0;
u32 dmaNewPoint = Size;
static u32 dmaOldPoint = 0;
u32 i = 0;
// for (i = 0; i < DMA_BUFFER_SIZE; i++)
// {
// printf("%02X ", dma_buffer[i]);
// }
// printf("\n");
if (dmaNewPoint == dmaOldPoint)
{
return;
}
else if (dmaNewPoint > dmaOldPoint)
{
recvLength = dmaNewPoint - dmaOldPoint;
xStreamBufferSendFromISR(UsartRxBuffer, (u8*)dma_buffer + dmaOldPoint, recvLength, &xHigherPriorityTaskWoken);
}
else if (dmaNewPoint < dmaOldPoint)
{
recvLength = DMA_BUFFER_SIZE - dmaOldPoint;
if (recvLength != 0)
{
xStreamBufferSendFromISR(UsartRxBuffer, (u8*)dma_buffer + dmaOldPoint, recvLength, &xHigherPriorityTaskWoken);
}
if (dmaNewPoint > 0)
{
recvLength = dmaNewPoint;
xStreamBufferSendFromISR(UsartRxBuffer, (u8*)dma_buffer, recvLength, &xHigherPriorityTaskWoken);
}
}
dmaOldPoint = dmaNewPoint;
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
// interrupt handler
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
}
void DMA2_Stream2_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_usart1_rx);
}
The MCU I am using is the STM32F469NI. DMA is set to circular mode, and the UART IDLE interrupt is enabled to receive variable-length UART packets.
Previously, I handled UART reception by processing received bytes with interrupts rather than using DMA. However, I am transitioning to using DMA for UART data reception to improve performance. Therefore, I believe there is no hardware issue.
I referred to the 'UART Reception To IDLE' example code provided by STM. I have reviewed my code multiple times against that example, but it seems fine.
For testing, I connected the UART to a PC terminal program and transmitted character data from the terminal to the MCU. Transmitting a single byte from the keyboard works correctly. The data updates at index 0 of the memory buffer dma_buffer and triggers the IDLE interrupt properly.
However, when entering more than 2 bytes of continuous data from the terminal, only the last byte is stored at index 0 of the buffer, and the rest are not updated and get lost.
For example, if DMA_BUFFER_SIZE is set to 10 and 'abcdefghijklmnop' is entered into the terminal (according to the PC's UART monitor program, the characters are transmitted continuously), the UART_DMARxHalfCplt, UART_DMAReceiveCplt, and HAL_UART_IRQHandler are called in order. However, when printing the data in dma_buffer using printf during each interrupt handler, only the character 'p' is stored at index 0, and the rest are stored as 0.
As I understand it, in DMA circular mode, when RX data is received, it should be stored in the DMA buffer with the index incrementing circularly. It is strange that data is only stored at index 0. Additionally, the fact that UART_DMARxHalfCplt and UART_DMAReceiveCplt were called indicates that the data of length 'abcdefghijklmnop' was recognized. I don't understand why all data is not stored in the buffer, and only the last character remains in the buffer.
If anyone knows why this is happening, please let me know.
Solved! Go to Solution.
2024-07-15 11:15 PM
Hi,
hdma_usart1_rx.Init.MemInc = DMA_MINC_DISABLE;
is wrong. It says that the memory address will *not* be incremented after each DMA transfer. This is exactly what you're observing. It should be enabled for using a memory buffer, possibly in STM32CubeMX.
hth
KnarfB
2024-07-15 11:15 PM
Hi,
hdma_usart1_rx.Init.MemInc = DMA_MINC_DISABLE;
is wrong. It says that the memory address will *not* be incremented after each DMA transfer. This is exactly what you're observing. It should be enabled for using a memory buffer, possibly in STM32CubeMX.
hth
KnarfB
2024-08-15 10:43 PM
You were right. It works well after making that change. Thank you!