FIFO buffer在嵌入式开发中使用是比较频繁的,常用用于数据收发处理,本文用串口数据接收与处理举例
先定义结构体,定义串口数据的buffer与长度
typedef struct USART_Buffer
{
/* @brief Receive buffer. */
volatile uint8_t RX[UART_BUFSIZE];
/* @brief Transmit buffer. */
volatile uint8_t TX[2];
/* @brief Receive buffer head. */
volatile uint16_t RX_Head;
/* @brief Receive buffer tail. */
volatile uint16_t RX_Tail;
/* @brief Transmit buffer head. */
volatile uint16_t TX_Head;
/* @brief Transmit buffer tail. */
volatile uint16_t TX_Tail;
/* @brief Receive buffer full flag */
volatile uint8_t RX_Full;
/* @brief Receive complete flag (new) */
volatile uint8_t RX_Complete;
/* @brief Last received time stamp (new) */
volatile uint32_t LastRxTime;
/* @brief received data len (new) */
volatile uint16_t RX_len;
} USART_Buffer_t;
//每次串口接收数据内存大小
#define GM_UART_RCV_BUFF_LEN 1024
//串口缓冲区内存大小
#define GM_UART_RCV_FIFO_LEN 2048
定义FIFO操作相关函数
GM_ERRCODE fifo_init(FifoType *fifo, u32 size)
{
if (fifo == NULL || size == 0)
{
return GM_PARAM_ERROR;
}
/* allocate fifo space. */
fifo->base_addr = (u8 *)malloc(size * sizeof(u8));
if (fifo->base_addr == NULL)
{
return GM_MEM_NOT_ENOUGH;
}
memset((u8 *)fifo->base_addr, 0x00, sizeof(u8) * size);
fifo->read_idx = 0;
fifo->write_idx = 0;
fifo->size = size;
return GM_SUCCESS;
}
/************************************************************************
Function :
Description : Reset the fifo structure.
fifo : reference to fifo struct.
Change history:
Note:
Author:
Date:
************************************************************************/
GM_ERRCODE fifo_reset(FifoType *fifo)
{
if (fifo == NULL || fifo->base_addr == NULL)
{
return GM_PARAM_ERROR;
}
/* optional (clear memory region) */
memset((u8 *)fifo->base_addr, 0x00, fifo->size);
fifo->read_idx = 0;
fifo->write_idx = 0;
return GM_SUCCESS;
}
/************************************************************************
Function :
Description : delete the fifo structure.
fifo : reference to fifo struct.
Change history:
Note:
Author:
Date:
************************************************************************/
GM_ERRCODE fifo_delete(FifoType *fifo)
{
if (fifo == NULL)
{
return GM_PARAM_ERROR;
}
/* delete fifo space. */
if (fifo->base_addr == NULL)
{
return GM_SUCCESS;
}
free(fifo->base_addr);
fifo->base_addr = 0;
fifo->read_idx = 0;
fifo->write_idx = 0;
fifo->size = 0;
return GM_SUCCESS;
}
/************************************************************************
Function :
Description : returns msg count in the fifo
fifo : reference to fifo struct.
Change history:
Note:
Author:
Date:
************************************************************************/
u32 fifo_get_msg_length(FifoType *fifo)
{
int read_point = 0;
int write_point = 0;
int left_data_size = 0;
if (fifo == NULL)
{
return 0;
}
read_point = fifo->read_idx;
write_point = fifo->write_idx;
left_data_size = write_point - read_point;
if (left_data_size < 0)
{
left_data_size += fifo->size;
}
return left_data_size;
}
/************************************************************************
Function :
Description : returns left space of the fifo
fifo : reference to fifo struct.
Change history:
Note:
Author:
Date:
************************************************************************/
u32 fifo_get_left_space(FifoType *fifo)
{
int read_point = 0;
int write_point = 0;
int left_data_size = 0;
if (fifo == NULL)
{
return 0;
}
read_point = fifo->read_idx;
write_point = fifo->write_idx;
left_data_size = write_point - read_point;
if (left_data_size < 0)
{
left_data_size += fifo->size;
}
return (fifo->size - left_data_size - 1);
}
/************************************************************************
Function :
Description : Insert msg to the fifo.
fifo : reference to fifo struct.
data : reference to msg buffer.
len : msg length.
Change history:
Note:
Author:
Date:
************************************************************************/
GM_ERRCODE fifo_insert(FifoType *fifo, u8 *data, u32 len)
{
int read_point;
int write_point;
int left_size;
int to_end;
if (len > MAX_FIFO_MSG_LEN)
{
return GM_PARAM_ERROR;
}
read_point = fifo->read_idx;
write_point = fifo->write_idx;
left_size = read_point - write_point;
if (left_size <= 0)
{
left_size += fifo->size;
}
if ((u32)left_size <= (len))
{
return GM_MEM_NOT_ENOUGH;
}
to_end = fifo->size - write_point;
if ((u32)to_end >= len)
{
memcpy(fifo->base_addr + write_point, data, len);
write_point += len;
write_point %= fifo->size;
}
else
{
memcpy(fifo->base_addr + write_point, data, to_end);
memcpy(fifo->base_addr, data + to_end, len - to_end);
write_point = len - to_end;
}
fifo->write_idx = write_point;
return GM_SUCCESS;
}
/************************************************************************
Function :
Description : Retrieve one msg from the fifo.
fifo : reference to fifo struct.
data : reference to msg data buffer.
len : data buffer length.
Change history:
Note:
Author:
************************************************************************/
GM_ERRCODE fifo_peek(FifoType *fifo, u8 *data, u32 len)
{
int read_point = 0;
int write_point = 0;
int left_data_size = 0;
int to_end = 0;
read_point = fifo->read_idx;
write_point = fifo->write_idx;
left_data_size = write_point - read_point;
if (left_data_size < 0)
{
left_data_size += fifo->size;
}
if ((u32)left_data_size < len)
{
return GM_EMPTY_BUF;
}
to_end = fifo->size - read_point;
if ((u32)to_end >= len)
{
memcpy(data, fifo->base_addr + read_point, len);
read_point += len;
if (read_point >= fifo->size)
{
read_point = 0;
}
}
else
{
memcpy(data, fifo->base_addr + read_point, to_end);
memcpy(data + to_end, fifo->base_addr, len - to_end);
read_point = len - to_end;
}
return GM_SUCCESS;
}
/************************************************************************
Function :
Description : Retrieve one msg from the fifo.
fifo : reference to fifo struct.
data : reference to msg data buffer.
len : data buffer length. it is input and output varible
Change history:
Note:
Author:
************************************************************************/
GM_ERRCODE fifo_peek_and_get_len(FifoType *fifo, u8 *data, u32 *len_p)
{
int read_point = 0;
int write_point = 0;
int left_data_size = 0;
int to_end = 0;
read_point = fifo->read_idx;
write_point = fifo->write_idx;
left_data_size = write_point - read_point;
if (left_data_size < 0)
{
left_data_size += fifo->size;
}
if ((u32)left_data_size < (*len_p))
{
(*len_p) = (u32)left_data_size;
}
to_end = fifo->size - read_point;
if ((u32)to_end >= (*len_p))
{
memcpy(data, fifo->base_addr + read_point, (*len_p));
read_point += (*len_p);
if (read_point >= fifo->size)
{
read_point = 0;
}
}
else
{
memcpy(data, fifo->base_addr + read_point, to_end);
memcpy(data + to_end, fifo->base_addr, (*len_p) - to_end);
read_point = (*len_p) - to_end;
}
return GM_SUCCESS;
}
GM_ERRCODE fifo_pop_len(FifoType *fifo, u32 len)
{
int read_point = 0;
int write_point = 0;
int left_data_size = 0;
read_point = fifo->read_idx;
write_point = fifo->write_idx;
left_data_size = write_point - read_point;
if (left_data_size < 0)
{
left_data_size += fifo->size;
}
if ((u32)left_data_size < len)
{
return GM_PARAM_ERROR;
}
read_point += len;
if (read_point >= fifo->size)
{
read_point -= fifo->size;
}
fifo->read_idx = read_point;
return GM_SUCCESS;
}
GM_ERRCODE fifo_peek_until(FifoType *fifo, u8 *data, u16 *len_p, const u8 until_char)
{
U32 read_point = fifo->read_idx;
U32 write_point = fifo->write_idx;
S32 data_size = write_point - read_point;
U32 index = 0;
bool has_until_char = false;
if (NULL == fifo || NULL == data || NULL == len_p)
{
return GM_PARAM_ERROR;
}
if (data_size < 0)
{
data_size += fifo->size;
}
// 取分隔符前面的所有字符
for (index = 0; index < data_size && (fifo->base_addr[read_point] != until_char) && index < *len_p - 1; index++)
{
data[index] = fifo->base_addr[read_point];
read_point++;
read_point %= fifo->size;
}
// 取分隔符后面的所有分隔符,包括'\r'
for (; index < data_size && index < *len_p - 1 && ((fifo->base_addr[read_point] == until_char) || (fifo->base_addr[read_point] == '\r')); index++)
{
data[index] = fifo->base_addr[read_point];
read_point++;
read_point %= fifo->size;
has_until_char = true;
}
*len_p = index;
data[index] = '\0';
if (has_until_char)
{
return GM_SUCCESS;
}
else
{
return GM_EMPTY_BUF;
}
}
bool fifo_empty(FifoType *fifo)
{
return (fifo->read_idx == fifo->write_idx);
// return (fifo_get_left_space(fifo) == fifo->size - 1);
}
bool fifo_full(FifoType *fifo)
{
return (fifo_get_msg_length(fifo) == fifo->size - 1);
}
下面简单举例下实际使用
1 初始化对应的串口与buffer,初始化FIFO buffer调用fifo_init
void uart_init(uint8_t port)
{
GM_ERRCODE ret = 0;
/* Turn on USART*/
switch (port)
{
case DEBUG_UART_PORT:
break;
case COMM_UART_PORT:
/* Init fifo Buffer */
g_uart.UARTParas[GM_UART_COMM].is_Idle = true;
ret = fifo_init(&g_uart.UARTParas[GM_UART_COMM].rcv_fifo, GM_UART_RCV_FIFO_LEN);
printf("GM_UART_COMM fifo_init:%d\n",ret);
/* Init Ring Buffer */
comm_Uart_Buf.RX_Tail = 0;
comm_Uart_Buf.RX_Head = 0;
comm_Uart_Buf.TX_Tail = 0;
comm_Uart_Buf.RX_Full = 0;
comm_Uart_Buf.LastRxTime = 0;
memset(&comm_Uart_Buf, 0, sizeof(USART_Buffer_t));
/* Enable RXNE interrupt */
__HAL_UART_ENABLE_IT(&UartHandle_Module, UART_IT_RXNE);
__HAL_UART_ENABLE_IT(&UartHandle_Module, UART_IT_IDLE);
break;
default:
break;
}
}
2 串口有数据产生中断,往FIFO buffer丢数据,判断数据是否接收完毕,是否接收超时。
/*!
@brief is_comm_uart_frame_complete.
*/
bool is_comm_uart_frame_complete(uint32_t inter_byte_timeout) {
USART_Buffer_t *pUSART_Buf = &comm_Uart_Buf;
/* buffer has data and timeout regard as tranfer completed*/
if (pUSART_Buf->RX_len> 0 &&
(HAL_GetTick() - pUSART_Buf->LastRxTime > inter_byte_timeout)) {
return true;
}
return false;
}
/*!
@briefOne frame receive completed or timeout ,add data to fifo
*/
void comm_add_data_to_fifo(void)
{
GM_ERRCODE ret = 0;
USART_Buffer_t *pUSART_Buf = &comm_Uart_Buf;
/* caculate availabe data from ring buffer */
if (pUSART_Buf->RX_len > 0) {
__HAL_UART_DISABLE_IT(&UartHandle_Module, UART_IT_RXNE);
g_uart.UARTParas[GM_UART_COMM].is_Idle = false;
/* read ring buffer data to FIFO */
ret = fifo_insert(&g_uart.UARTParas[GM_UART_COMM].rcv_fifo, &pUSART_Buf->RX[0],pUSART_Buf->RX_len);
//printf("module_add_data_to_fifo ret:%d,data:%s,len:%d,fifolen:%d\r\n",ret,pUSART_Buf->RX,pUSART_Buf->RX_len,fifo_get_msg_length(&g_uart.UARTParas[GM_UART_COMM].rcv_fifo));
pUSART_Buf->RX_len = 0;
/* update Tail */
g_uart.UARTParas[GM_UART_COMM].is_Idle = true;
__HAL_UART_ENABLE_IT(&UartHandle_Module, UART_IT_RXNE);
}
}
/*!
@briefOne module data recieve IRQ
*/
void Comm_IRQHandler(void) {
USART_Buffer_t *pUSART_Buf = &comm_Uart_Buf;
/* receieve data*/
if ((__HAL_UART_GET_FLAG(&UartHandle_Module, UART_FLAG_RXNE) != RESET) && \
(__HAL_UART_GET_IT_SOURCE(&UartHandle_Module, UART_IT_RXNE) != RESET))
{
pUSART_Buf->RX[pUSART_Buf->RX_len] = (uint8_t)(UartHandle_Module.Instance->DR & (uint8_t)0x00FF);
pUSART_Buf->RX_len++;
if(pUSART_Buf->RX_len > (RX_MAX_LEN - 1))
{
pUSART_Buf->RX_len = 0;
}
pUSART_Buf->LastRxTime = HAL_GetTick();
__HAL_UART_CLEAR_FLAG(&UartHandle_Module, UART_FLAG_RXNE);
/* Clear ORE flag if set */
if (__HAL_UART_GET_FLAG(&UartHandle_Module, UART_FLAG_ORE)) {
__HAL_UART_CLEAR_FLAG(&UartHandle_Module, UART_FLAG_ORE);
}
}
/* Check for idle line detection (optional, if your protocol supports it) */
if (__HAL_UART_GET_FLAG(&UartHandle_Module, UART_FLAG_IDLE)) {
__HAL_UART_CLEAR_FLAG(&UartHandle_Module, UART_FLAG_IDLE);
__HAL_UART_CLEAR_IDLEFLAG(&UartHandle_Module);
//printf("Module pUSART_Buf->RX:%s,%d\r\n",pUSART_Buf->RX,pUSART_Buf->RX_len);
comm_add_data_to_fifo();
}
}
2 另外一任务或循环中取FIFO buffer的数据并做相应处理,取出fifo buffer数据调用fifo_peek。
void process_module_Rxfifo_buffer(void) {
GM_ERRCODE ret = GM_SUCCESS;
u16 contentLen, actualLen;
if (g_uart.UARTParas[GM_UART_COMM].is_Idle == false) return;
if(is_comm_uart_frame_complete(COMM_UART_TIMEOUT))
{
comm_add_data_to_fifo();
}
contentLen = fifo_get_msg_length(&g_uart.UARTParas[GM_UART_COMM].rcv_fifo);
if (contentLen > 2) { 根据实际情况修改或添加判断条件
u8 tempBuf[GM_UART_RCV_BUFF_LEN] = {0};
ret = fifo_peek(&g_uart.UARTParas[GM_UART_COMM].rcv_fifo, tempBuf, contentLen);
if (GM_SUCCESS != ret {
fifo_reset(&g_uart.UARTParas[GM_UART_COMM].rcv_fifo);
printf("Receive invalid module data,reset fifo!,ret=%d\n", ret);
return;
}
(数据处理)
......
}
fifo_pop_len(&g_uart.UARTParas[GM_UART_COMM].rcv_fifo, contentLen);
}
}