Skip to main content
JayDev
Senior
March 8, 2022
Solved

Circular buffer resetting start point?

I'm trying to use DMA to handle my incoming Rx data and, ideally, I'd like to just increment the index to keep track of the incoming data, essentially like this:

#define uartsize	2000
#define dma_buffer_interrupt_size	1
 
uint8_t		UART_in_Buffer[uartsize];
 
HAL_UART_Receive_DMA(&huart3, &UART_in_Buffer[head_index], dma_buffer_interrupt_size);
 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	head_index = (head_index + dma_buffer_interrupt_size) % uartsize;
}

The problem I'm having is that when I setup a circular buffer, once it enters HAL_UART_RxCpltCallback(), it resets the index to 0. So if I have the dma_buffer_interrupt_size set to 3, for instance, it will enter that interrupt routine every 3 bytes, increment the count, and then if I send another 3 bytes, it will write over the first 3 bytes of the array.

I can get around that by doing the following:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	head_index = (head_index + dma_buffer_interrupt_size) % uartsize;
	HAL_UART_DMAStop(&huart3);
	HAL_UART_Receive_DMA(&huart3, &UART_in_Buffer[head_index], dma_buffer_interrupt_size);
}

This will change the starting point to the head location, but it uses a lot of code to do this and rules out any advtantages of doing this over a normal buffer (in fact, it might essentially just have recreated the functionality of a normal buffer over a circular buffer).

Is there a way to setup a circular buffer so it's incrementing "head_index" after every byte but not resetting the index back to 0 every time it enters the interrupt? There's not really a way to manage the buffer at this point otherwise.

Currently, I can either count how many bytes I've received (but write over the previous ones) or I can write the data to the DMA buffer but not be able to track how many bytes have come in.

This topic has been closed for replies.
JayDev
JayDevBest answer
Senior
March 8, 2022

That's actually pretty perfect and seems to greatly simplify the code. I ended up moving to this:

#define uartsize	2000
uint8_t UART_in_Buffer[uartsize];
 
HAL_UART_Receive_DMA(&pc_uart, UART_in_Buffer, uartsize);
 
while (1)
 {
	 head_index = (uartsize - DMA1_Stream0->NDTR) % uartsize;
 }

Seems to work pretty well. Thanks for the help!

2 replies

TDK
March 8, 2022

Set up circular DMA which is always running and use the NDTR register to track the head index. You will still get half- and full-complete callbacks if you want them, but you could also just poll NDTR for new bytes.

Edit: there is also HAL_UARTEx_ReceiveToIdle_DMA which you can use in circular mode and will generate a callback at idle conditions as well as half- and full-complete flags.

"If you feel a post has answered your question, please click ""Accept as Solution""."
JayDev
JayDevAuthorBest answer
Senior
March 8, 2022

That's actually pretty perfect and seems to greatly simplify the code. I ended up moving to this:

#define uartsize	2000
uint8_t UART_in_Buffer[uartsize];
 
HAL_UART_Receive_DMA(&pc_uart, UART_in_Buffer, uartsize);
 
while (1)
 {
	 head_index = (uartsize - DMA1_Stream0->NDTR) % uartsize;
 }

Seems to work pretty well. Thanks for the help!