MCU 串口接收环形缓冲区的实现

news2024/11/16 8:26:20

环形缓冲区

1. 环形缓冲区的特性

1.先进先出

2. 当缓冲区被使用完,且又有新的数据需要存储时,丢掉历史最久的数据,保存最新的数据

现实中的存储介质都是线性的,因此我们需要做一下处理,才能在功能上实现环形缓冲区

算法说明:
1、pHead和pTail分别是连续存储介质的首地址和尾地址
2、pTail - pHead 的值是环形缓冲区的总长度
3、pValid 是使用区域的起始指针,取数据时的起点,当取数据时pValid要发生偏移
4、pValidTail 是使用区域的的结尾指针,存数据时的起点,当存数据时,pValidTail要发生偏移
5、现有长度为addLen字节要存入,当pValidTail + addLen > pTail 时(超出了缓冲区,这时就要绕到开头pHead)
int len1 = pTail - pValidTail;
int len2 = addLen - len1;
pValidTail = pHead + len2;//新的使用区的尾指针
6、判断总长度是否变更,即是否有数据覆盖pValid所指向的区域,如果有,要偏移pValid

2. 算法验证代码

ringBuffer.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "ringBuffer.h"
#define BUFFER_SIZE  16   //缓冲区的长度,可以修改

static u32 validLen;//已使用的数据长度
static u8* pHead = NULL;//环形存储区的首地址
static u8* pTail = NULL;//环形存储区的结尾地址
static u8* pValid = NULL;//已使用的缓冲区的首地址
static u8* pValidTail = NULL;//已使用的缓冲区的尾地址

/*
 * 初始化环形缓冲区
 * 环形缓冲区这里可以是malloc申请的内存,也可以是Flash存储介质
 * */
void initRingbuffer(void)
{
	if(pHead == NULL)
	{
		pHead = (u8*) malloc(BUFFER_SIZE);
	}
	pValid = pValidTail = pHead;
	pTail = pHead + BUFFER_SIZE;
	validLen = 0;
}

/*
 * function:向缓冲区中写入数据
 * param:@buffer 写入的数据指针
 * 		 @addLen 写入的数据长度
 * return:-1:写入长度过大
 * 		  -2:缓冲区没有初始化
 * */
int wirteRingbuffer(u8* buffer,u32 addLen)
{
	if(addLen > BUFFER_SIZE) return -2;
	if(pHead==NULL) return -1;
	assert(buffer);

	//将要存入的数据copy到pValidTail处
	if(pValidTail + addLen > pTail)//需要分成两段copy
	{
		int len1 = pTail - pValidTail;
		int len2 = addLen - len1;
		memcpy( pValidTail, buffer, len1);
		memcpy( pHead, buffer + len1, len2);
		pValidTail = pHead + len2;//新的有效数据区结尾指针
	}else
	{
		memcpy( pValidTail, buffer, addLen);
		pValidTail += addLen;//新的有效数据区结尾指针
	}

	//需重新计算已使用区的起始位置
	if(validLen + addLen > BUFFER_SIZE)
	{
		int moveLen = validLen + addLen - BUFFER_SIZE;//有效指针将要移动的长度
		if(pValid + moveLen > pTail)//需要分成两段计算
		{
			int len1 = pTail - pValid;
			int len2 = moveLen - len1;
			pValid = pHead + len2;
		}else
		{
			pValid = pValid + moveLen;
		}
		validLen = BUFFER_SIZE;
	}else
	{
		validLen += addLen;
	}

	return 0;
}

/*
 * function:从缓冲区内取出数据
 * param   :@buffer:接受读取数据的buffer
 *		    @len:将要读取的数据的长度
 * return  :-1:没有初始化
 *	 	    >0:实际读取的长度
 * */
int readRingbuffer(u8* buffer,u32 len)
{
	if(pHead==NULL) return -1;

	assert(buffer);

	if(validLen ==0) return 0;

	if( len > validLen) len = validLen;

	if(pValid + len > pTail)//需要分成两段copy
	{
		int len1 = pTail - pValid;
		int len2 = len - len1;
		memcpy( buffer, pValid, len1);//第一段
		memcpy( buffer+len1, pHead, len2);//第二段,绕到整个存储区的开头
		pValid = pHead + len2;//更新已使用缓冲区的起始
	}else
	{
		memcpy( buffer, pValid, len);
		pValid = pValid +len;//更新已使用缓冲区的起始
	}
	validLen -= len;//更新已使用缓冲区的长度

	return len;
}

/*
 * function:获取已使用缓冲区的长度
 * return  :已使用的buffer长度
 * */
u32 getRingbufferValidLen(void)
{
	return validLen;
}

/*
 * function:释放环形缓冲区
 * */
void releaseRingbuffer(void)
{
	if(pHead!=NULL) free(pHead);
	pHead = NULL;
}

ringBuffer.h

#ifndef RINGBUFFER_H_
#define RINGBUFFER_H_
typedef unsigned char u8;
typedef unsigned int u32;

void initRingbuffer(void);
int wirteRingbuffer(u8* buffer,u32 len);
int readRingbuffer(u8* buffer,u32 len);
u32 getRingbufferValidLen(void);
void releaseRingbuffer(void);

#endif /* RINGBUFFER_H_ */

 测试 main 函数

#include <stdio.h>
#include <stdlib.h>
#include "ringBuffer.h"
// 主函数
int main()
{
	char c;
	int readLen;
	u8 readBuffer[10];
	//setvbuf(stdout,NULL,_IONBF,0); //pinrtf、putchar不能立马输出,打开此注释
	initRingbuffer();

	printf("Please enter a line [blank line to terminate]> ");
	do{
		c=getchar();
		putchar(c);
		switch(c)
		{
			case 'Q':
				goto exit;
			break;
			case 'R':
				readLen = readRingbuffer(readBuffer,10);
				printf("readRingbuffer len:%d\n",readLen);
				if(readLen > 0){
					printf("readRingbuffer:");
					for(int i=0;i<readLen;i++){
						printf("%c ",(char)readBuffer[i]);
					}
					printf("\n");
				}
			break;
			default :
				if(c!='\n') wirteRingbuffer((u8*)&c,1);
			break;
		}
	}while (1);



exit:
	releaseRingbuffer();
	printf("exit.\n");
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1485936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MSMFN

CDFI是彩色多普勒血流成像 辅助信息 作者未提供数据

【C++那些事儿】深入理解C++类与对象:从概念到实践(中)| 默认构造函数 | 拷贝构造函数 | 析构函数 | 运算符重载 | const成员函数

&#x1f4f7; 江池俊&#xff1a; 个人主页 &#x1f525;个人专栏&#xff1a; ✅数据结构冒险记 ✅C那些事儿 &#x1f305; 有航道的人&#xff0c;再渺小也不会迷途。 文章目录 1. 类的6个默认成员函数2. 构造函数2.1 概念2.2 特性 3. 析构函数3.1 概念3.2 特性 4. 拷贝…

SpringMVC 学习(十一)之数据校验

目录 1 数据校验介绍 2 普通校验 3 分组校验 4 参考文档 1 数据校验介绍 在实际的项目中&#xff0c;一般会有两种校验数据的方式&#xff1a;客户端校验和服务端校验 客户端校验&#xff1a;这种校验一般是在前端页面使用 JS 代码进行校验&#xff0c;主要是验证输入数据…

计算机二级Python刷题笔记------基本操作题23、33、35、37(考察字符串)

文章目录 第二十三题&#xff08;字符串替换&#xff1a;replace(old,new)&#xff09;第三十三题&#xff08;字符串遍历&#xff09;第三十五题&#xff08;字符串与列表&#xff09;第三十七题&#xff08;拼接字符串&#xff09; 第二十三题&#xff08;字符串替换&#xf…

Qt开发 显示类控件

Label QLabel 可以用来显示文本和图片 核心属性如下 属性说明textQLabel 中的文本textFormat文本的格式&#xff1a;Qt::PlainText 纯文本Qt::RichText 富文本(支持 html 标签)Qt::MarkdownText markdown 格式Qt::AutoText 根据文本内容自动决定文本格式.pixmapQLabel 内部包…

模拟器抓HTTP/S的包时如何绕过单向证书校验(XP框架)

模拟器抓HTTP/S的包时如何绕过单向证书校验&#xff08;XP框架&#xff09; 逍遥模拟器无法激活XP框架来绕过单向的证书校验&#xff0c;如下图&#xff1a; ​​ 解决办法&#xff1a; 安装JustMePlush.apk安装Just Trust Me.apk安装RE管理器.apk安装Xposedinstaller_逍遥64位…

FRM模型十三:互换定价(二)

定义一个互换&#xff0c;本金为1e7&#xff0c;7年后到期 固定端&#xff1a;利率2.5%,每年付息一次 浮动端&#xff1a;Libor6M,每半年付息一次 import QuantLib as ql from prettytable import PrettyTable# 定义全局时间&#xff1a;当前日期&#xff0c;下一个结算日&…

Redis小白入门教程

Redis入门教程 1. Redis入门1.1 Redis简介1.2 Redis服务启动与停止1.2.1 Redis下载1.2.2 服务启动命令1.2.3 客户端连接命令1.2.4 修改Redis配置文件 2. Redis数据类型2.1 五种常用数据类型介绍2.1.1 字符串操作命令2.1.2 哈希操作命令2.1.3 列表操作命令2.1.4 集合操作命令2.1…

Java | Java中与文件同名的类的构造函数的调用

在Java的学习过程中遇到了这样一段代码&#xff1a; public class Test1 {int a1;public static void main(String []args){System.out.println("java");}public Test1(){System.out.println("构造函数");} }它的运行结果是这样的&#xff0c;构造函数中的…

进程与线程:通过实际生活来解析计算机的基本运作单位

进程与线程 进程与线程&#xff1a;详细解析计算机的基本运作单位1. 进程&#xff1a;独立的执行环境1.1 进程的特点&#xff1a; 2. 线程&#xff1a;轻量级的执行单元2.1 线程的特点&#xff1a; 3. 区别和联系4. 表格 进程与线程&#xff1a;详细解析计算机的基本运作单位 在…

win10安全中心误删文件怎么办?解析恢复与预防策略

在使用Windows 10的过程中&#xff0c;许多用户依赖于其内置的安全中心来保护电脑免受恶意软件的侵害。然而&#xff0c;有时安全中心的误判可能导致重要文件被错误地删除。当面对这种情况时&#xff0c;了解如何恢复误删的文件并掌握预防措施显得尤为重要。本文将为您详细解析…

LabVIEW高精度天线自动测试系统

LabVIEW高精度天线自动测试系统 系统是一个集成了LabVIEW软件的自动化天线测试平台&#xff0c;提高天线性能测试的精度与效率。系统通过远程控制测试仪表&#xff0c;实现了数据采集、方向图绘制、参数计算等功能&#xff0c;特别适用于对天线辐射特性的精确测量。 在天线的…

【C++】类的默认成员函数(上)

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《C》 《Linux》 《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 一、默认成员函数二、构造函数构造函数的概念及特性 三、析构函数析构函数的特性…

介绍平衡准确率(Balance Accuracy)和加权 F1 值(Weighted F1)

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 先复习一下查准率、召回率和 F1 分数&#xff1a; 查准率是对预测结果而言&#xff0c;每个类别模型预测正确的比例。 召回率是对样本标签而言&#xff0c;每个类别中有多少被预测正确了。 F1 分数是…

电机的极数和槽数,机械角度和电角度,霍尔IC,内外转子

什么是电机的极数和槽数&#xff1f; 【第7集】② 正弦波驱动的转矩脉动、正弦电流的时序和相位变化、超前角控制&#xff08;超前角调整&#xff09;、正弦波驱动的各种波形 - 电源设计电子电路基础电源技术信息网站_罗姆电源设计R课堂 (rohm.com.cn) 下面为您介绍表示电机…

算法【线性表的查找-折半查找/二分查找/对分查找】

线性表的查找-折半查找/二分查找/对分查找 折半查找概念查找过程折半查找算法: (非递归算法) 折半查找法的性能分析性能分析&#xff1a;平均查找长度ASL&#xff1a; 复杂度折半查找法的特点&#xff1a; 折半查找概念 折半查找&#xff0c;也称为二分查找&#xff0c;是一种…

【JS】解构赋值注意点,解构赋值报错

报错代码 const 小明 { email: 6, pwd: 66 } const 小刚 { email: 9, pwd: 99 }const { email } 小明 const { email } 小刚 报错图 原因 2个常量重复&#xff0c;重复在同一个作用域内是不能重复的&#xff0c;例如大括号内{const a 1; const a 2} 小伙伴A提问 问&…

从头构建gpt2 基于Transformer

从头构建gpt2 基于Transformer VX关注{晓理紫|小李子}&#xff0c;获取技术推送信息&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持&#xff01;&#xff01; 如果你感觉对你有所帮助&#xff0c;请关注我。 源码获取 VX关注晓理紫并回复“chatgpt…

Unreal触屏和鼠标控制旋转冲突问题

Unreal触屏和鼠标控制旋转冲突问题 鼠标控制摄像机旋转添加Input轴计算旋转角度通过轴事件控制旋转 问题和原因问题原因 解决办法增加触摸控制旋转代码触屏操作下屏蔽鼠标轴响应事件 鼠标控制摄像机旋转 通过Mouse X和Mouse Y控制摄像机旋转。 添加Input轴 计算旋转角度 通过…

10 计算机结构

冯诺依曼体系结构 冯诺依曼体系结构&#xff0c;也被称为普林斯顿结构&#xff0c;是一种计算机架构&#xff0c;其核心特点包括将程序指令存储和数据存储合并在一起的存储器结构&#xff0c;程序指令和数据的宽度相同&#xff0c;通常都是16位或32位 我们常见的计算机,笔记本…