Skip to main content
Visitor II
January 31, 2025
Solved

DMA receive, with circular buffer: Incoming data is not copied in buffer on time

  • January 31, 2025
  • 3 replies
  • 1198 views

I have set up a UART as receiver. I use circular buffer to copy the data in. The code pretty much works ok with one exception:

If the last byte of incoming data frame is to be saved in index 0 (UARTRx[0]), the data appears at this address AFTER the buffer counter indicates the data to be ready at that location. the delay could be few hundred microseconds. 

Is it possible that the counter is updated, but the value is still not written yet?

 

 

 

/* Private includes ----------------------------------------------------------*/
#include "BMS.h"

#include "cmsis_os.h"
#include "string.h"


/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/
#define RX_BUF_SIZE 255			// must be power of 2
#define TX_BUF_SIZE 9
#define FR_BUF_SIZE 32

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
// Circular buffer to store received UART data

uint8_t frame[FR_BUF_SIZE];					// Buffer for individual received data frames
uint8_t UARTRx[RX_BUF_SIZE];				// UART Bus receive Circular DMA buffer

 

 

Receiving Task(infinite loop): 

 

 

void BMS_Receive()
{
	if(HAL_UART_Receive_DMA(&huart1, UARTRx, RX_BUF_SIZE) != HAL_OK)						// Start UART reception with DMA
	{
		Error_Handler();
	}
	uint8_t head, checkedhead, tail, y;
	int crc;
	head = checkedhead = tail = 0;											
	while(1)
	{
		if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE) || __HAL_UART_GET_FLAG(&huart1, UART_FLAG_FE) || __HAL_UART_GET_FLAG(&huart1, UART_FLAG_NE)){

			__HAL_UART_CLEAR_OREFLAG(&huart1); 											// The method of clearing all three flags is the same, so only one macro needs to be called
			if(HAL_UART_Receive_DMA(&huart1, UARTRx, RX_BUF_SIZE) != HAL_OK)				// Start UART reception with DMA
			{
				Error_Handler();
			}

			memset(UARTRx, 0, RX_BUF_SIZE);
			osDelay(1);
			Cprintf("Reset\n");
		}

		head = ((RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx)) -1 ) % RX_BUF_SIZE;	
		while (head != checkedhead)
		{

			if (UARTRx[tail] == BMS_UARTHEADER){
				for (y = 0; y < FR_BUF_SIZE; y++)											// Copy data from circular buffer
				{
					checkedhead = (tail + y) % RX_BUF_SIZE;				
					frame[y] = UARTRx[checkedhead]; 					
					if (checkedhead == head)break;											// stop copy if head comes before full frame buffer filled
				}
// do some parsing here
//so in this location i see that the UARTRx[0] value is wrong, by adding a small delay it is updated to correct value
				

					tail = (tail+1) % RX_BUF_SIZE;
				
			} else
			{

				checkedhead = tail;
				tail = (tail+1) % RX_BUF_SIZE;

			}
		}
		osDelay(1);
	}
}

 

 

 

    This topic has been closed for replies.
    Best answer by tarmogr

    To answer my own question, buffer size was set up wrong. Correct is 256 and not 255!

    3 replies

    Super User
    January 31, 2025

    > head = ((RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx)) -1 ) % RX_BUF_SIZE;

    Seems off by one. Consider:

    RX_BUF_SIZE = 8
    NDTR = 8 (no transfers yet)

    head = ((8 - 8) - 1 ) % 8 = -1
    should be 0

    (also note the overflow issue)

    tarmogrAuthor
    Visitor II
    January 31, 2025

    since head is defined as uint8_t, the result would be 7 right?

    Anyway once the receiver is running the NDTR is 0...7, and there is no overflow/underflow. My problem always occurs when receiver has been running for some time.

    tarmogrAuthor
    Visitor II
    January 31, 2025

    The reason I subtract 1 is to get the existing data location and not the next data location that the NDTR register refers to.

    Super User
    January 31, 2025

    Let's be objective here: When no transfers have taken place yet, your code reads characters from UARTRx. That's a bug.

     

    UARTRx should also be declared as volatile. Might not be an issue here, but it's possible.

     

    > since head is defined as uint8_t, the result would be 7 right?

    Yes

     

    > Anyway once the receiver is running the NDTR is 0...7, and there is no overflow/underflow.

    NDTR gets reloaded when it hits 0 in circular mode. Test it.

    tarmogrAuthorAnswer
    Visitor II
    February 4, 2025

    To answer my own question, buffer size was set up wrong. Correct is 256 and not 255!