DMA receive, with circular buffer: Incoming data is not copied in buffer on time
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);
}
}
