cancel
Showing results for 
Search instead for 
Did you mean: 

Circular buffer resetting start point?

JayDev
Senior II

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.

1 ACCEPTED SOLUTION

Accepted Solutions

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!

View solution in original post

2 REPLIES 2
TDK
Guru

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".

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!