news 2026/4/22 18:08:19

SeanLib系列函数库-MyFIFO

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SeanLib系列函数库-MyFIFO


查看其它库函数说明,请点击此处跳转到SeanLib主页

1. 本篇内容

本篇介绍FIFO库,一个可自定义大小的环形先进先出的数据容器,提供了入队、出队的基本操作方法,以用一些存入字符串、取整行数据等高级的操作方法,适用于串口收发数据、普通消息处理,类似于操作系统中的队列。
本库适用于裸机项目,FreeRTOS中已有队列,不需要使用本库,如果要在FreeRTOS中使用本库,需要修改开关中断的两个函数,将在后面详细说明。

2. 主要应用场景

在不考虑DMA的情况下,使用FIFO来存储串口发送和接收的数据是非常有必要的。

2.1 串口接收数据

首先我们假设有这么一个常规需求:PC发送一系列的命令,单片机进行解析并执行命令。
PC会连续发送不定量的命令,而单片机处理一条命令的时间较长,我们通过串口中断接收数据存放在数组中,收到“\r\n"时表示一条命令结束,开始执行,而此时串口仍在发生接收中断,可能会覆盖我们正在处理的数据,即使在处理之前先将数据复制出来,也仍可能发现数据覆盖的问题,因此使用FIFO存放数据是非常有必要的。
如果使用了FIFO,情况会变为中断中只需要进行入队操作,主程序中通过检测FIFO的计数值进行出队并解析数据,数据安全性可以得到保障。

2.2 串口发送数据

串口发送数据是一个耗时的操作,CPU会一直等待所有数据发完,如果使用了FIFO,就可以很方便地采用中断的方式发送数据,当有要发送的数据时,直接将数据入队,然后使能串口发送中断,在中断中出队再发送。比如在putc函数中入队。

3. 使用方法

本库包含 MyFIFO.h 和 MyFIFO.lib 两个文件。
本库需要使用两个外部函数:关中断和恢复中断,这是为了保护FIFO的数据完整,因为同一个FIFO对象可能既要在中断中使用,也要在主程序中使用,这两个函数请在 SeanLib.c 中实现:

//当使用MyFIFO库时,根据需要实现下面的开、关中断函数#ifdef_MYFIFO_H_//保存中断屏蔽寄存器的值并关中断unsignedintMyFIFO_Disable_Interrupt(void){unsignedintPrimask;__asm{MRS Primask,PRIMASK CPSID I}returnPrimask;}//恢复中断屏蔽寄存器voidMyFIFO_Restore_Interrupt(unsignedintPrimask){__asm{MSR PRIMASK,Primask}}#endif

3.1 FIFO类型介绍

头文件中提供了FIFO类型,结构体如下:

typedefstruct{char*Buffer;//私有,FIFO存储空间,其它程序不要使用unsignedshortSize;//只读,FIFO的存储空间大小unsignedshortCount;//只读,FIFO中元素的个数unsignedcharLines;//只读,FIFO中存在以 NewLine 结尾的数据行数unsignedcharOverFlow;//读写,溢出标志,如果存入时覆盖了未读取的字符,该标志会置1unsignedcharPrimask;//读写,用于暂存中断屏蔽寄存器的值MyFIFO_NewLine_t NewLine;//读写,指定字符串的换行符,默认CRLF,可在FIFO创建后修改unsignedshortReadPst;//私有,读取指针,其它程序不要使用unsignedshortWritePst;//私有,写入指针,其它程序不要使用/******************************************************************************* * 功 能: 释放FIFO占用的内存 * 参 数: 无 * 返回值:无 *******************************************************************************/void(*Dispose)(void);/******************************************************************************* * 功 能: 向FIFO队列尾部存入一个字符 * 参 数: * Character :要存入的字符 * 返回值:无 *******************************************************************************/void(*PushChar)(charCharacter);/******************************************************************************* * 功 能: 向FIFO队列尾部存入一个字符串 * 参 数: * String :要存入的字符串 * 返回值:无 *******************************************************************************/void(*PushString)(char*String);/******************************************************************************* * 功 能: 从FIFO的队列末尾删除一个字符,即最近存入的字符 * 参 数: 无 * 返回值:无 *******************************************************************************/void(*DeleteChar)(void);/******************************************************************************* * 功 能: 从FIFO队列开头取出一个字符 * 参 数: 无 * 返回值:取出的字符,若为'\0'表示Fifo为空 *******************************************************************************/char(*PopChar)(void);/******************************************************************************* * 功 能: 从FIFO队列中取出一行字符,换行符由 NewLine 指定 * 参 数: * String :取出的字符串存放位置,返回字符串不包含换行符 * MaxLen :指定最多检索的数量,防止内存溢出 * 返回值:取出的字符个数,不包含结束符 *******************************************************************************/unsignedshort(*PopLine)(char*String,unsignedshortMaxLen);/******************************************************************************* * 功 能: 取出FIFO队列中所有字符,之后Lines会自动清零 * 参 数: * String :取出的字符串存放位置 * MaxLen :指定最多检索的数量,防止内存溢出 * 返回值:实际取出的字符个数 *******************************************************************************/unsignedshort(*PopALL)(char*String,unsignedshortMaxLen);/******************************************************************************* * 功 能: 清空FIFO的存储空间 * 参 数: 无 * 返回值:无 *******************************************************************************/void(*Clear)(void);}MyFIFO_t;

里面的属性和方法都有注释,解释地比较清楚了。

3.2 示例代码

下面以串口接收数据为例讲解FIFO库的用法
先创建FIFO对象,如下:

MyFIFO_t*PC_RX_FIFO;//定义全局变量,存放从PC收到的数据voidPC_Fifo_Init(void){PC_RX_FIFO=NewMyFIFO(200);if(PC_RX_FIFO==NULL){Error_Handle(0,"Creat RX fifo failed!");}__HAL_UART_ENABLE_IT(UART_PC,UART_IT_RXNE);}MSH_INIT_EXPORT(1,PC_Fifo_Init,"Fifo for PC init");

在串口接收中断中进行入队:

voidUSART_IT_Callback(UART_HandleTypeDef*huart){uint8_ttemp;uint32_tSR=huart->Instance->SR;//读SR寄存器,检查中断标志uint32_tCR1=huart->Instance->CR1;//读CR1寄存器,检查中断使能状态if((SR&USART_SR_RXNE)&&(CR1&USART_CR1_RXNEIE))//发生了接收中断{//接收中断标志在读一次SR和DR后自动清除temp=(uint8_t)huart->Instance->DR;//取出串口收到的数据PC_RX_FIFO->PushChar(temp);//消息入队huart->Instance->DR=temp;}}

在主程序中检测Lines属性,判断是否接收了整行(以指定结束符为标志,默认为\r\n)的数据,若有就取出一行,进行解析和执行:

charbuffer[50];while(1){while(PC_RX_FIFO->Lines>0)//串口接收缓冲区中有整行的数据{uint16_tlen=PC_RX_FIFO->PopLine(buffer,sizeof(buffer));if(len==0)//数据长度为0,表示用户按下了回车键,发送笑脸符号{printf("@_@");}elseif(MyMSH_ExecCmd(buffer)==0)//未识别的命令{printf("NG,%s,Undefined command!\r\n",buffer);}}//......}

4. 其它说明

虽然FIFO结构体中定义的缓存区数据类型为char,但仍然可以使其存储unsigned char类型的数据,这种情况适用于ModbusRTU通讯。
这时仍然可以用PushChar函数存入一个字符,代码与上面相同,因为字符类型也是8位数据。
要出队可以用 PopChar 取一个数据,也可以用 PopALL 取出全部数据(适用于空闲中断触发),不可使用PopLine函数。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 18:01:23

ACadSharp深度解析:现代.NET生态中的CAD数据处理架构揭秘

ACadSharp深度解析:现代.NET生态中的CAD数据处理架构揭秘 【免费下载链接】ACadSharp C# library to read/write cad files like dxf/dwg. 项目地址: https://gitcode.com/gh_mirrors/ac/ACadSharp 在CAD工程数据处理领域,传统解决方案往往面临格…

作者头像 李华
网站建设 2026/4/22 18:00:30

如何快速搭建你的专属Galgame社区:TouchGAL完整指南

如何快速搭建你的专属Galgame社区:TouchGAL完整指南 【免费下载链接】kun-touchgal-next TouchGAL是立足于分享快乐的一站式Galgame文化社区, 为Gal爱好者提供一片净土! 项目地址: https://gitcode.com/gh_mirrors/ku/kun-touchgal-next 你是否一直梦想拥有一…

作者头像 李华
网站建设 2026/4/22 17:59:29

Photon-GAMS光影包技术解析:游戏渲染管线的深度优化方案

Photon-GAMS光影包技术解析:游戏渲染管线的深度优化方案 【免费下载链接】Photon-GAMS Personal fork of Photon shaders 项目地址: https://gitcode.com/gh_mirrors/ph/Photon-GAMS Photon-GAMS是一款基于Photon着色器架构的Minecraft光影包,专注…

作者头像 李华
网站建设 2026/4/22 17:58:49

BilibiliDown:一站式B站视频下载解决方案,轻松保存你喜爱的内容

BilibiliDown:一站式B站视频下载解决方案,轻松保存你喜爱的内容 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitc…

作者头像 李华