查看其它库函数说明,请点击此处跳转到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}}#endif3.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函数。