目录
1、FIFO概述
1.1什么是FIFO
1.2什么时候使用FIFO
1.3FIFO最基本元素
2.FIFO的实现及相关函数
2.1FIFO结构体定义
2.2创建FIFO
2.3删除FIFO
2.4向FIFO中写入元素
2.5从FIFO中读取元素
2.6从FIFO中拷贝元素
2.7从FIFO中删除元素
2.8比较FIFO中的内容
2.9从FIFO接收内容中查找1
2.10从FIFO接收内容中查找2
1、FIFO概述
1.1什么是FIFO
? ? ? ? FIFO(fist in first out)中文直译先进先出,对应的有LIFO(last?in?first?out)。
1.2什么时候使用FIFO
? ? ? ? FIFO主要用来解决速率不匹配的问题而设计。以STM32中串口应用为例,串口以115200的波特率进行数据接收,接收一个字节大概需要87us,接收一帧数据100字节,需要耗时为870us(约等于1kHz)。以SMT32F407为例,单片机运行主频可达160MHz。单片机处理速度远高于串口的接收速度。所以此时就需要将数据接收到FIFO中进行暂存。
1.3FIFO最基本元素
? ? ? ? 该文所讲述的FIFO主要为后续的STM32串口所使用,所以涉及的数据宽度均为8位。一个FIFO需要的最基本元素如下。
????????查看有一些关于C语言FIFO实现的文章中没有进行使用量(满标志)的定义,而是使用了写位置和读位置来进行FIFO使用量的计算。但是存在一个问题,当读取位置等于写入位置时,无法实现使用量的计算。因为FIFO为空时和FIFO为满时读取位置均等于写入位置。
? ? ? ? 考虑到FIFO使用的安全性还需要增加一个FIFO进行了初始化的标志位。因为后续文章中的FIFO缓存地址及FIFO地址均使用malloc申请内存进行处理,若FIFO没初始化,程序容易指向不确定位置造成错误。
2.FIFO的实现及相关函数
? ? ? ? 该文章所描述的FIFO主要用来后续STM32的串口通信使用,所以FIFO实现为数据可覆盖模式即当FIFO写入满时,再次进行写入对原有数据会进行覆盖。
2.1FIFO结构体定义
? ? ? ? 我们使用结构体来表示FIFO,具体定义如下
typedef struct
{
INT16U Depth; //number of element can be stored
INT8U* Buf; //address of the buffer
INT8U InitFlag; //flag of initialization
//use volatile to limit,prevent errors when reading and writing at the same time
volatile INT16U Push; //position of the push position
volatile INT16U Pull; //position of the pull position
volatile INT16U CntUsed; //number of the used element
}FifoTypeDef;
2.2创建FIFO
/**
***************************************************
* @brief func_fifo_Create
* @note 创建FIFO,动态申请内存
* @param depth FIFO可存放元素的个数
* @retval 0: 申请内存失败
* else: FIFO的地址
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* 新建
***************************************************
**/
FifoTypeDef* func_fifo_Create(const INT16U depth)
{
FifoTypeDef* p = malloc(sizeof(FifoTypeDef));
if(p == NULL)
return NULL;
p->Buf = malloc(depth);
if(p->Buf == NULL)
{
free(p);
return NULL;
}
p->Depth = depth;
p->CntUsed = 0;
p->Push = 0;
p->Pull = 0;
p->InitFlag = FIFO_INITIAL;
return p;
}
2.3删除FIFO
? ? ? ? FIFO删除前先进行FIFO初始化判断,若FIFO未初始化意味着FIFO创建失败或者未创建,指针不明确不进行操作。
/**
***************************************************
* @brief func_fifo_Free
* @note 删除FIFO
* @param p 指向待操作的FIFO
* @retval EnumFifoErr
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
FIFO_ERR_FAILED func_fifo_Free(FifoTypeDef* const p)
{
if(p->InitFlag == FIFO_INITIAL)
{
free(p->Buf);
free(p);
return FIFO_ERR_OK;
}
return FIFO_ERR_FAILED;
}
2.4向FIFO中写入元素
? ? ?此函数为FIFO常规写入时调用。需要注意以下几个问题
- 写入到缓存末尾时需要重头进行写入
- 写入数据量大于FIFO深度时,只写入待写入数据的最后FIFO深度个数据
/**
***************************************************
* @brief func_fifo_Push
* @note 向FIFO中写入数据
* @param p 指向待操作FIFO
* num 写入的字节数
* data 指向写入数据的内存地址
* @retval EnumFifoErr
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
FIFO_ERR_FAILED func_fifo_Push(FifoTypeDef* const p,const INT16U num,const INT8U* const data)
{
INT16U offset = 0;
INT16U len2end = 0;
if(p->InitFlag != FIFO_INITIAL)
return FIFO_ERR_FAILED;
//写入数据量大于FIFO的容量,存在多次覆盖的情况,仅写入最后的FIFO大小个元素
if(num > p->Depth)
{
//计算写入数据的偏移地址
offset = num - p->Depth;
//计算写入位置
p->Push = (p->Push + offset) % p->Depth;
//若写入位置刚好位于buf的开始,进行fifo大小数据量的写入
if(p->Push == 0)
memcpy(p->Buf , data+offset , p->Depth);
//写入位置不在头部
else
{
//计算写入到末尾可写入的数量
len2end = p->Depth - p->Push;
//写入到buf末尾
memcpy(p->Buf + p->Push , data+offset , len2end);
//从头继续写入
memcpy(p->Buf , data + offset + len2end , p->Depth - len2end);
}
//FIFO数据发生了覆盖
p->Pull = p->Push;
p->CntUsed = p->Depth;
}
//写入数据量不大于FIFO容量,全部数据可以写入到FIFO中。
else
{
len2end = p->Depth - p->Push;
//从PUSH位置到BUFF末尾可容纳写入数据
if(len2end >= num)
memcpy(p->Buf + p->Push , data , num);
//不能容纳,需要从头写入
else
{
memcpy(p->Buf + p->Push , data , len2end);
memcpy(p->Buf , data + len2end , num - len2end);
}
p->Push = (p->Push + num) % p->Depth;
p->CntUsed += num;
//FIFO被写满,发生覆盖现象。
if(p->CntUsed >= p->Depth)
{
p->Pull = p->Push;
p->CntUsed = p->Depth;
}
}
return FIFO_ERR_OK;
}
下面的写入函数用于STM32的串口使用DMA模式接收时,将DMA的缓冲器地址直接指向FIFO地址,数据会自动进行写入,但是FIFO的PULL,PUSH等相关变量不会自动更新。需要在接收数据完成后记性相关变量的操作。
/**
***************************************************
* @brief func_fifo_push_nodata
* @note 不进行数据的写入,只操作fifo相关参数,用于DMA直接写入FIFO数据时,中断中处理FIFO参数
* @param p 指向待操作FIFO
* num 写入数据量
* @retval NONE
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
void func_fifo_PushNoData(FifoTypeDef* const p,const INT16U num)
{
p->Push = (p->Push + num) % p->Depth;
p->CntUsed += num;
if(p->CntUsed >= p->Depth)
{
p->Pull = p->Push;
p->CntUsed = p->Depth;
}
}
2.5从FIFO中读取元素
/**
***************************************************
* @brief func_fifo_pull
* @note 从FIFO中读取数据
* @param p 指向待操作FIFO
* num 读取数据的长度
* buff 读取数据的缓冲区
* @retval EnumFifoErr
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
EnumFifoErr func_fifo_Pull(FifoTypeDef* const p,const INT16U num,const void* const buff)
{
//读取大于使用量或者未初始化
if(num > p->CntUsed || p->InitFlag != FIFO_INITIAL)
return FIFO_ERR_FAILED;
//计算到末尾可读取数量
INT8U len2end = p->Depth - p->Pull;
//读取到buff结尾满足num数量
if(len2end > num)
{
memcpy(buff , p->Buf + p->Pull , num);
memset(p->Buf + p->Pull , 0 , num);
}
//需要从头读取
else
{
memcpy(buff , p->Buf + p->Pull , len2end);
memset(p->Buf + p->Pull , 0 , len2end);
memcpy(buff + len2end , p->Buf , num-len2end);
memset(p->Buf , 0 , num-len2end);
}
p->Pull = (p->Pull + num) % p->Depth;
p->CntUsed -= num;
return FIFO_ERR_OK;
}
2.6从FIFO中拷贝元素
? ? ? ? 不同于上面的读取函数,改函数只进行元素的拷贝,不对FIFO的参数进行修改。?
/**
***************************************************
* @brief func_fifo_Copy
* @note 拷贝FIFO中PULL位置后的第ind开始的len个数据到data
* @param p 指向待操作FIFO
* ind pull后面的第ind个元素
* len 数据长度
* data 指向存储较数据
* @retval FIFO_ERR_OK 成功
* FIFO_ERR_FAILED 失败
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
EnumFifoErr func_fifo_Copy(FifoTypeDef* const p,const INT16U ind, const INT16U len, const INT8U* data)
{
if(ind + len > p->CntUsed || p->InitFlag != FIFO_INITIAL)
return FIFO_ERR_FAILED;
INT8U* point = p->Buf + p->Pull + ind;
INT8U* end_position = p->Buf + p->Depth - 1;
if(point > end_position)
{
point = point - p->Depth;
memcpy(data,point,len);
return FIFO_ERR_OK;
}
else
{
INT16U len2end = end_position - point + 1;
if(len2end >= len)
{
memcpy(data,point,len);
return FIFO_ERR_OK;
}
else
{
memcpy(data,point,len2end);
memcmp(data+len2end,p->Buf,len-len2end);
return FIFO_ERR_OK;
}
}
}
2.7从FIFO中删除元素
? ? ? ? 功能类似于上面的读取元素,但是读取元素时需要指明读取元素的存放地址。该函数不需要。直接操作FIFO,删除num个元素。同样需要注意指针回头问题。
/**
***************************************************
* @brief func_fifo_DeleteElement
* @note 删除FIFO中的元素,对FIFO结构体进行相应操作
* @param p 指向待操作FIFO
* num 删除的数量
* @retval EnumFifoErr
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
EnumFifoErr func_fifo_DeleteElement(FifoTypeDef* const p,const INT16U num)
{
if(num > p->CntUsed || p->InitFlag != FIFO_INITIAL)
return FIFO_ERR_FAILED;
INT8U len2end = p->Depth - p->Pull;
if(len2end > num)
memset(p->Buf + p->Pull , 0 , num);
else
{
memset(p->Buf + p->Pull , 0 , len2end);
memset(p->Buf , 0 , num - len2end);
}
p->CntUsed -= num;
p->Pull = (p->Pull+num) % p->Depth;
return FIFO_ERR_OK;
}
2.8比较FIFO中的内容
? ? ? ? 从FIFO中PULL位置开始的第ind个元素长度为len的数据是否与data相等。
/**
***************************************************
* @brief func_fifo_Cmp
* @note 从FIFO的PULL开始的第ind个数据与len长度的data进行比较
* @param p 指向待操作FIFO
* ind pull后面的第ind个元素
* len 比较的数据长度
* data 指向待比较数据
* @retval EnumFifoErr
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
EnumFifoErr func_fifo_Cmp(FifoTypeDef* const p,const INT16U ind, const INT16U len, const INT8U* data)
{
if(ind + len > p->CntUsed || p->InitFlag != FIFO_INITIAL)
return FIFO_ERR_FAILED;
INT8U* point = p->Buf + p->Pull + ind;
INT8U* end_position = p->Buf + p->Depth - 1;
if(point > end_position)
{
point = point - p->Depth;
if(memcmp(data,point,len) == 0)
return FIFO_ERR_OK;
else
return FIFO_ERR_FAILED;
}
else
{
INT16U len2end = end_position - point + 1;
if(len2end >= len)
{
if(memcmp(data,point,len) == 0)
return FIFO_ERR_OK;
else
return FIFO_ERR_FAILED;
}
else
{
if(memcmp(data,point,len2end) == 0 && memcmp(data+len2end,p->Buf,len-len2end) == 0 )
return FIFO_ERR_OK;
else
return FIFO_ERR_FAILED;
}
}
}
2.9从FIFO接收内容中查找1
? ? ? ? 从FIFO中查找长度为len的data数据,对不符合的内容进行删除。
/**
***************************************************
* @brief func_fifo_FindDataDelete
* @note 从FIFO中查找长度为len的data数据对不符合数据进行删除
* @param p 指向待操作FIFO
* len 查找数据的长度
* data 指向查找数据
* @retval EnumFifoErr
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
EnumFifoErr func_fifo_FindDataDelete(FifoTypeDef* const p, const INT16U len, const INT8U* data)
{
while(p->CntUsed >= len || p->InitFlag != FIFO_INITIAL)
{
if(func_fifo_Cmp(p,0,len,data) == FIFO_ERR_OK)
return FIFO_ERR_OK;
else
func_fifo_DeleteElement(p,1);
}
return FIFO_ERR_FAILED;
}
2.10从FIFO接收内容中查找2
/**
***************************************************
* @brief func_fifo_FindData
* @note 从FIFO中查找长度为len的data数据对,将查找到的序号赋值给pos
* @param p 指向待操作FIFO
* ind pull后面的第几个
* len 查找数据的长度
* data 指向查找数据
* pos 查找到的位置
* @retval FIFO_ERR_OK 成功
* FIFO_ERR_FAILED 失败
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
EnumFifoErr func_fifo_FindData(FifoTypeDef* const p, const INT16U ind, const INT8U* data,INT16U len, INT16U* pos)
{
INT16U times = 0;
INT16U cycle = 0;
if(p->CntUsed >= ind + len || p->InitFlag == FIFO_INITIAL)
{
times = p->CntUsed - ind - len + 1;
while(cycle < times)
{
if(func_fifo_Cmp(p,ind+cycle,len,data) == FIFO_ERR_OK)
{
*pos = cycle;
return FIFO_ERR_OK;
}
cycle++;
}
return FIFO_ERR_FAILED;
}
return FIFO_ERR_FAILED;
}
|