cancel
Showing results for 
Search instead for 
Did you mean: 

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

tarmogr
Associate III

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);
	}
}

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
tarmogr
Associate III

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

View solution in original post

5 REPLIES 5
TDK
Guru

> 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)

If you feel a post has answered your question, please click "Accept as Solution".
tarmogr
Associate III

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.

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

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.

If you feel a post has answered your question, please click "Accept as Solution".
tarmogr
Associate III

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