用C代码实现环形缓冲区(ring buf)

news2024/12/23 7:13:45

用C代码实现环形缓冲区(ring buf)

  • 概述
  • 环境介绍
    • launch.json(没改)
    • tasks.json
    • 注意
  • 代码
    • ringbuf.c
    • ringbuf.h
    • main.c
  • 测试说明
    • 工程代码下载

概述

因嵌入式项目需要,串口接收的数据有很高的周期性发送频率,原方式通过查询接收标志再接收会导致数据丢失和硬件buf溢出等问题,因此考虑开足够大的环形缓冲区(ring buf)实现,接收中断中仅实时的进行buf写操作,应用程序进行循环查询处理,将数据处理和数据接收解耦合,本文采用VS Code + Gcc编译器编写了测试验证程序,对ringbuf进行了测试,参考文章链接,并非照抄,而是在原文基础上进行了改进,并封装成单独的.c文件。

环境介绍

本文测试代码时在VS Code环境下进行了编写和调试,编译工具用的mingw64,详细配置方法就不做介绍了,主要介绍我遇到的一个问题:单个C程序可正常编译,但多个C程序编译报错(未定义的函数名),主要问题是launch.json 和 tasks.json 不对,参考文章,我的配置如下:

launch.json(没改)

 {
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": []
}

tasks.json

该文件主要修改了 “ f i l e " 参数为: " {file}" 参数为:" file"参数为:"{cwd}\*.c”,以编译当前工程目录下的所有C文件

{
	"version": "2.0.0",
	"tasks": [
		{
			"type": "cppbuild",
			"label": "C/C++: gcc.exe 生成活动文件",
			"command": "C:\\mingw64\\bin\\gcc.exe",
			"args": [
				"-fdiagnostics-color=always",
				"-g",
				//"${file}", //默认仅编译当前打开的单个文件
				"${cwd}\\*.c",//编译当前目录下的所有C文件
				"-o",
				"${fileDirname}\\${fileBasenameNoExtension}.exe"
			],
			"options": {
				"cwd": "${fileDirname}"
			},
			"problemMatcher": [
				"$gcc"
			],
			"group": "build",
			"detail": "编译器: C:\\mingw64\\bin\\gcc.exe"
		}
	]
}

注意

这2个文件不是自己编写的,生成方法如下:
打开项目文件夹->终端->配置默认生成任务->选择gcc,之后会自动生成,我们只需要修改部分内容即可

代码

三个文件,main.c为主程序,ringbuf.c和ringbuf.h

ringbuf.c


#include "ringbuf.h"

/* ******************************************
* 环形缓冲区初始化函数 InitRingBuff函数参数说明:
*   参数1 buf :指向ringbuf的结构体指针
*   参数2 size :buf大小,单位字节
*   参数3 over_mode :写入覆盖模式
*        0-   当buf满时阻塞写,并丢弃最新数据
*        1-   当buf满时覆盖写,并丢弃最早的数据
***************************************** */
void InitRingBuff(ringfifo *buf, unsigned int size,int over_mode)
{
    buf->pHead = (char *)malloc(size * sizeof(char));
    memset(buf->pHead, 0, size);
    buf->pValidRead = buf->pHead;
    buf->pValidWrite = buf->pHead;
    buf->pTail = buf->pHead + size;
    buf->used_space = 0;
    buf->free_space = size;
    buf->overwrite = over_mode; 
    printf("pread is %d \t pwrite is %d \t used_space is %d \n",buf->pValidRead,buf->pValidWrite,buf->used_space);
}

// 向缓冲区写数据
int WriteRingBuff(ringfifo *buf,char *pdata, unsigned int len)
{
    if (buf->pHead == NULL )
    {
        printf("WriteRingBuff: RingBuff is not Init!\n");
        return -2;
    }
    if (len > buf->pTail - buf->pHead)
    {
        printf("WriteRingBuff: ring buf is too small\n");//每次写入数据不能超过buf总长度,否则认为buf开的不够
        return -1;
    }
    if ((len > buf->free_space) && (buf->overwrite != 1))
    {
        printf("WriteRingBuff: New add data is too long\n");//只有在覆盖模式才可写入超过剩余空间的数据,否则无法写入
        return -1;
    }
    // 若新增的数据长度大于写指针和尾指针之间的长度
    if (buf->pValidWrite + len > buf->pTail)
    {
        int PreLen = buf->pTail - buf->pValidWrite;
        int LastLen = len - PreLen;
        memcpy(buf->pValidWrite, pdata, PreLen);
        memcpy(buf->pHead, pdata + PreLen, LastLen);
        buf->pValidWrite = buf->pHead + LastLen; // 新环形缓冲区尾地址
    }
    else
    {
        memcpy(buf->pValidWrite, pdata, len); // 将新数据内容添加到缓冲区
        buf->pValidWrite += len;              // 新的有效数据尾地址
    }
    if ((len > buf->free_space) && (buf->overwrite == 1))//覆盖写
    {
        buf->pValidRead = buf->pValidWrite;
        buf->used_space = buf->pTail - buf->pHead; 
        buf->free_space = 0;
    }
    else{//未覆盖
        buf->used_space += len; 
        buf->free_space -= len;
    }
    return 1;
}

// 从缓冲区读数据
int ReadRingBuff(ringfifo *buf, char *pdata, unsigned int len)
{
    if (buf->pHead == NULL )
    {
        printf("ReadRingBuff: RingBuff is not Init!\n");
        return -2;
    }
    if (len > buf->used_space)
    {
        printf("ReadRingBuff: Read data is too long\n");
        return -1;
    }
    if (len == 0)
    {
        return 1;
    }
    if (buf->pValidRead + len > buf->pTail)
    {
        int PreLen = buf->pTail - buf->pValidRead;
        int LastLen = len - PreLen;
        memcpy(pdata, buf->pValidRead, PreLen);
        memcpy(pdata + PreLen, buf->pHead, LastLen);
        buf->pValidRead = buf->pHead + LastLen;
    }
    else
    {
        memcpy(pdata, buf->pValidRead, len);
        buf->pValidRead += len;
    }
    buf->used_space -= len;
    buf->free_space +=len;
    return len;
}

ringbuf.h

#ifndef __RINGBUF_H_
#define __RINGBUF_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

typedef struct
{
    char *pHead;       // 环形缓冲区首地址
    char *pValidRead;  // 已使用环形缓冲区首地址
    char *pValidWrite; // 已使用环形缓冲区尾地址
    char *pTail;       // 环形缓冲区尾地址
    unsigned int used_space;  //环形缓冲区有效数据个数
    unsigned int free_space; //环形缓冲区剩余空间
    int overwrite;// 1- overwrite
} ringfifo;

void InitRingBuff(ringfifo *buf, unsigned int size,int over_mode);
int WriteRingBuff(ringfifo *buf,char *pdata, unsigned int len);
int ReadRingBuff(ringfifo *buf, char *pdata, unsigned int len);

#endif

main.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "ringbuf.h"

int main()
{
    char c;
    int len;
    int readLen = 1;
    char readBuffer[10];
    int i = 0;
    ringfifo buf1;
    int r;

	char* strbuff = (char*)malloc(100 * sizeof(char));
    buf1.overwrite = 1;
    InitRingBuff(&buf1,10,1);
    printf("Please enter a character,VER 0.4\n");

    while (1)
    {
        fgets(strbuff,100,stdin);
        printf("strlen is %d\n",strlen(strbuff)-1);
        if(strlen(strbuff)>1)
        {
            switch (*strbuff)
            {
            case 'R':
                r = ReadRingBuff(&buf1,readBuffer, readLen);
                if (r > 0)
                {
                    for (i = 0; i < readLen; i++)
                    {
                        printf("%c ", (char)readBuffer[i]);
                    }
                    printf("\n");
                    printf("pread is %d \t pwrite is %d \t used_space is %d free space is %d BUF:",buf1.pValidRead,buf1.pValidWrite,buf1.used_space,buf1.free_space);
                    for (i = 0; i < buf1.pTail - buf1.pHead; i++)
                    {
                        printf("%c ", (char)*(buf1.pHead+i));
                    }
                    printf("\n");
                }
                break;

            default:
                WriteRingBuff(&buf1,strbuff, strlen(strbuff)-1);
                printf("pread is %d \t pwrite is %d \t used_space is %d free space is %d BUF:",buf1.pValidRead,buf1.pValidWrite,buf1.used_space,buf1.free_space);
                for (i = 0; i < buf1.pTail - buf1.pHead; i++)
                    {
                        printf("%c ", (char)*(buf1.pHead+i));
                    }
                printf("\n");
                break;
            }
        }
    };


    return 0;
}

测试说明

程序运行后输入除了”R“以外的任意字符,按回车后写入buf,输入R时从buf读取1个字节,初始化buf时需设置覆盖/阻塞模式,测试结果如下:
在这里插入图片描述

工程代码下载

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

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

相关文章

小程序项目思路分享爬虫

小程序项目思路分享爬虫 具体需求&#xff1a; 有这几个就行&#xff0c;门店名称门店地址门店类型&#xff0c;再加上省、市、县/区门店名称&#xff1a;storeName 门店地址&#xff1a;storeAddress 程序运行&#xff1a; honor_spider获取经纬度信息。 经纬度——>详…

Mybatis plus 使用通用枚举

说明&#xff1a;mybatis plus 使用枚举可实现数据库存入时指定值保存&#xff0c; 读取时指定值展示&#xff08;返给前端&#xff09; 可通过继承IEnum<T>、 EnumValue实现 1、引包 <dependency><groupId>mysql</groupId><artifactId>mysql-…

esxi上给centos7虚拟机扩容硬盘

原来centos7硬盘分配的空间只有40GB 需要直接扩容到200GB 扩容前 扩容后 扩容步骤&#xff1a; 1 .在esxi平台上关机虚拟机&#xff0c;将硬盘调整为200GB&#xff0c;然后开机 2.请出chatGPT 1. 创建新分区 使用剩余的磁盘空间创建一个新的分区。你可以使用fdisk&#xf…

归档数据shell脚本

系统中数据很重要&#xff0c;为确保数据不会丢失&#xff0c;定时备份数据是一个必要的习惯。制定一个存储重要文件的数据备份计划也绝非易事&#xff0c;而shell脚本可以祝我们一臂之力。 1.创建需要备份配置文件的路径文件。(BACKUP_FILE) 2.以日期为备份文件的后缀。 3.判断…

LeetCode 热题 100 | 多维动态规划(二)

目录 1 5. 最长回文子串 2 1143. 最长公共子序列 菜鸟做题&#xff0c;语言是 C 1 5. 最长回文子串 核心思想&#xff1a;把总问题拆解为若干子问题。 总问题&#xff1a;从第 i 个字母到第 j 个字母是回文串子问题&#xff1a;从第 i 1 个字母到第 j - 1 个字母是回文…

【就近接入,智能DNS-Geo DNS ,大揭秘!】

做过后端服务或者网络加速的小伙伴&#xff0c;可能或多或少都听说过&#xff0c;智能DNS或者Geo DNS&#xff0c;就是根据用户的位置&#xff0c;返回离用户最近的服务节点&#xff0c;就近接入&#xff0c;以达到服务提速的效果。 那么大家有没想过&#xff0c;这个背后的原理…

ETLCloud结合kafka的数据集成

一、ETLCloud中实时数据集成的使用 在ETLCloud中数据集成有两种方式&#xff0c;一种是离线数据集成&#xff0c;另一种便是我们今天所要介绍的实时数据集成了&#xff0c;两者的区别从名字便可以得知&#xff0c;前者处理的数据是离线的没有时效性的&#xff0c;后者的数据是…

【Keil5-Boot和APP配置】

Keil5-Boot和App配置 ■ Keil5-Boot和APP配置■ 一&#xff1a;sct文件 sct文件配置■ 二&#xff1a;发布版本不需要在 C/C&#xff0c;Asm&#xff0c;Linker&#xff0c;中添加 CMDDEBUG 宏定义。■ 三&#xff1a;Debug版本需要在Linker添加 --pd"-DCMDDEBUG" 才…

4.进程相关 2

8.内存映射 8.1 内存映射相关定义 创建一个文件&#xff0c;将保存在磁盘中的文件映射到内存中&#xff0c;后期两个进程之间对内存中的数据进行操作&#xff0c;大大减少了访问磁盘的时间&#xff0c;也是一种最快的 IPC &#xff0c;因为进程之间可以直接对内存进行存取 8.…

视觉大模型--DeformableDETR

原理大家可以参考这篇文章&#xff0c;我这边主要介绍几个公式和整体源码理解。 提出了多尺度可变形注意力(Multi-scale Deformable Attention, MSDA).基于此设计了 DETR 特有的利用多尺度特征检测的流程&#xff0c;对之后的很多工作有指导意义。提出了两阶段 DETR 的思路&…

OpenHarmony应用开发引入开源C/C++库---之Har包里的NDK

Har 包 HAR&#xff08;Harmony Archive&#xff09;是静态共享包&#xff0c;可以包含代码、C 库、资源和配置文件。通过 HAR 可以实现多个模块或多个工程共享 ArkUI 组件、资源等相关代码。HAR 不同于 HAP&#xff0c;不能独立安装运行在设备上&#xff0c;只能作为应用模块…

pandas常用的一些操作

EXCLE操作 读取Excel data1 pd.read_excel(excle_dir) 读Excel取跳过前几行&#xff1a; data1 pd.read_excel(excle_dir,skiprows1) 获取总行数 data1.shape[0] 获取总列数 data1.shape[1] 指定某列数据类型 data1 pd.read_excel("C:数据导入.xlsx",dtype…

设计模式——装饰器模式09

装饰器模式&#xff1a;是在原有基础上进行装饰&#xff08;无修改原来代码&#xff09;&#xff0c;来添加新的功能。 例如下面对普通耳机进行装饰。 设计模式&#xff0c;一定要敲代码理解 修饰对象&#xff08;抽象&#xff09; /*** author ggbond* date 2024年04月07日…

Unity核心学习

目录 认识模型的制作流程模型的制作过程 2D相关图片导入设置图片导入概述纹理类型设置纹理形状设置纹理高级设置纹理平铺拉伸设置纹理平台打包相关设置 SpriteSprite Editor——Single图片编辑Sprite Editor——Multiple图片编辑Sprite Editor——Polygon图片编辑SpriteRendere…

【汇编语言实战】统计个数(创新版)

内存中有10个分布在0至100内的正整数&#xff0c; 求小于60的数的个数num1&#xff0c;大于或等于60且小于80的数的个数num2&#xff0c;大于或等于80且小于100的数的个数num3 C语言描述该程序流程&#xff1a; #include <stdio.h> int main() {int a[]{1, 20, 95, 32,…

Python | 超前滞后分析

Nino SST Indices (Nino 12, 3, 3.4, 4; ONI and TNI) 有几个指标用于监测热带太平洋&#xff0c;所有这些指标都是基于海表温度(SST)异常在一个给定的区域的平均值。通常&#xff0c;异常是相对于30年的周期来计算的。厄尔尼诺3.4指数(Nio 3.4 index)和海洋厄尔尼诺指数(Ocea…

【Ubuntu】update-alternatives 命令详解

1、查看所有候选项 ​​​​​​​sudo update-alternatives --list java 2、​​​​​​​更换候选项 sudo update-alternatives --config java 3、自动选择优先级最高的作为默认项 sudo update-alternatives --auto java 4、删除候选项 sudo update-alternatives --rem…

MATLAB技巧:箱型图绘制

箱型图/箱线图 箱型图&#xff08;Box Plot&#xff09;&#xff0c;也称为盒须图或箱线图&#xff0c;是一种用于展示数据分布情况的统计图表。它通过展示数据的中位数、上下四分位数、最大值和最小值&#xff0c;可以直观地显示出数据的离散程度、偏态和异常值等信息。 箱型…

Python实现读取dxf文件的所有字符

Python实现读取dxf文件的所有字符 import ezdxfdef read_dxf_and_print_text(filename):# 加载DXF文件doc ezdxf.readfile(filename)# 遍历所有的实体for entity in doc.entities:# 检查实体是否是TEXT、MTEXT或DIMENSIONif isinstance(entity, ezdxf.entities.Text):print(f…

从头训练、采用预训练模型这两种方法在图像分类上的实践

参考书籍《Python深度学习》Chapter 5《深度学习用于计算机视觉》 演示数据&#xff1a;Dogs vs. Cats | Kaggle 1. 从头训练模型 1.1 不使用数据增强 1.2 使用数据增强 2. 使用预训练模型 去掉已有模型的最后的分类层。 2.1 直接使用已有模型的结果&#xff0c;再输入到新建…