队列,环形缓冲区实现与应用:适用于GD32串口编程或嵌入式底层驱动开发

环形缓冲区实现与应用:从基础到实践

在嵌入式系统和实时数据处理场景中,环形缓冲区(Circular Buffer)是一种非常常用的的数据结构,它能有效地管理数据的读写操作,尤其适用于数据流的临时存储与转发。

今天,我们就来深入探讨如何实现一个简单高效的环形缓冲区,并将其应用到模拟的 UART 通信场景中。

这篇文章将带您从零开始构建一个实用的环形缓冲区,并展示其在数据传输中的应用。环形缓冲区基本原理环形缓冲区是一种固定大小的数组结构,通过两个指针(读指针和写指针)来追踪数据的读写位置。当缓冲区的空间被占满时,根据不同的模式可选择覆盖旧数据或者阻塞写入操作。这种数据结构的优势在于无需频繁的内存分配和释放操作,能高效地利用有限的内存资源。

项目结构搭建
为了实现环形缓冲区,我们构建了一个简单的项目结构:

project/├── src/│  ├── main.c              // 主程序│   └── ring_buffer.c       // 环形缓冲区实现        ├── include/│ └── ring_buffer.h       // 环形缓冲区头文件└── Makefile                // 项目的Makefile

这种清晰的项目结构有助于我们更好地组织和管理代码。

环形缓冲区实现头文件
定义在ring_buffer.h中,我们首先定义了环形缓冲区的结构体、错误码、写入模式以及相关的函数声明:

#ifndef RING_BUFFER_H
#define RING_BUFFER_H#include <stdint.h>// 环形缓冲区结构体typedef struct 
{    
uint8_t *buffer;    // 数据存储的缓冲区    
uint32_t in_index;  // 写入指针    
uint32_t out_index; // 读取指针    
uint32_t length;    // 当前缓冲区中的元素个数    
ring_buffer_mode_t mode; // 缓冲区的写入模式} 
ring_buffer_t;// 错误码定义#define RINGBUFF_OK     0    // 成功
#define RINGBUFF_ERR    1    // 错误
#define RINGBUFF_EMPTY  2    // 缓冲区为空
#define RINGBUFF_FULL   3    // 缓冲区满// 写入模式枚举typedef enum {    
RINGBUFF_OVERWRITE,    // 缓冲区满时覆盖最旧数据    
RINGBUFF_NO_OVERWRITE  // 缓冲区满时返回错误
} ring_buffer_mode_t;// 函数声明void ring_buffer_init(ring_buffer_t *buffer, uint8_t *data, ring_buffer_mode_t mode);uint8_t ring_buffer_write(ring_buffer_t *buffer, uint8_t data);uint8_t ring_buffer_read(ring_buffer_t *buffer);int read_data_to_array(ring_buffer_t *buffer, uint8_t *data, uint32_t data_len);#endif // RING_BUFFER_H

源文件实现在ring_buffer.c
我们实现了环形缓冲区的各项功能:

#include "ring_buffer.h"// 初始化环形缓冲区
void ring_buffer_init(ring_buffer_t *buffer, uint8_t *data, ring_buffer_mode_t mode) 
{    
buffer->buffer = data;    
buffer->in_index = 0;    
buffer->out_index = 0;    
buffer->length = 0;    
buffer->mode = mode;
}// 写入数据到环形缓冲区uint8_t ring_buffer_write(ring_buffer_t *buffer, uint8_t data) 
{    
uint32_t next_in_index = (buffer->in_index + 1) % RINGBUFF_LEN;  // 使用数组的固定大小    
if (buffer->length == RINGBUFF_LEN) {        
if (buffer->mode == RINGBUFF_NO_OVERWRITE) {            return RINGBUFF_FULL;  // 缓冲区已满,且不允许覆盖        
} 
else 
{            
// 覆盖最旧的数据            
buffer->out_index = (buffer->out_index + 1) % RINGBUFF_LEN;            
buffer->length--;        
}    
}    buffer->buffer[buffer->in_index] = data;    
buffer->in_index = next_in_index;    
buffer->length++;    
return RINGBUFF_OK;
}// 从环形缓冲区读取数据
uint8_t ring_buffer_read(ring_buffer_t *buffer) 
{    
if (buffer->length == 0) 
{        
return '\0';  // 缓冲区为空,返回 '\0'    
}    uint8_t data = buffer->buffer[buffer->out_index];    
buffer->out_index = (buffer->out_index + 1) % RINGBUFF_LEN;    
buffer->length--;    
return data;
}// 从缓冲区读取数据到数组int read_data_to_array(ring_buffer_t *buffer, uint8_t *data, uint32_t data_len) 
{    
uint32_t index = 0;    while (buffer->length > 0 && index < data_len) 
{        
uint8_t byte = ring_buffer_read(buffer);        if (byte != '\0') 
{            
data[index++] = byte;        
}    
}    
return index;  // 返回实际读取的字节数}

主程序与模拟应用

main.c中,我们模拟了一个简单的 UART 通信场景,展示如何使用环形缓冲区实现数据的接收和读取:

#include "ring_buffer.h"
#include <stdio.h>#define RINGBUFF_LEN 128  // 固定缓冲区长度uint8_t uart_ring_buffer[RINGBUFF_LEN];
ring_buffer_t uart_buffer;// 模拟 UART 中断接收函数,模拟数据写入缓冲区void uart_interrupt_receive(uint8_t data) 
{    
ring_buffer_write(&uart_buffer, data);
}int main() 
{    
// 初始化环形缓冲区,使用覆盖模式    
ring_buffer_init(&uart_buffer, uart_ring_buffer, RINGBUFF_OVERWRITE);    // 模拟 UART 中断接收数据    
uart_interrupt_receive('A');    uart_interrupt_receive('B');    uart_interrupt_receive('C');    uart_interrupt_receive('D');    uart_interrupt_receive('E');    uart_interrupt_receive('F');    // 读取数据并打印    
uint8_t data[10];    
int bytes_read = read_data_to_array(&uart_buffer, data, sizeof(data));    // 打印读取的数据    
printf("Read %d bytes: ", bytes_read);    for (int i = 0; i < bytes_read; i++) 
{        
printf("%c ", data[i]);    
}    return 0;
}

Makefile 构建
为了方便编译和构建项目,我们提供了简单的Makefile

makefile
CC = gccCFLAGS = -Wall -Wextra -std=c99
SRC = src/main.c src/ring_buffer.c
OBJ = $(SRC:.c=.o)
EXEC = ring_buffer_demo
all: $(EXEC)$(EXEC): $(OBJ)    
$(CC) $(OBJ) -o $(EXEC)%.o: %.c    
$(CC) $(CFLAGS) -c $< -o 
$@clean:    rm -f $(OBJ) $(EXEC)

关键点总结

  1. 环形缓冲区:我们定义了一个基于固定大小数组的环形缓冲区结构体,通过in_indexout_index来追踪写入和读取的位置。
  2. 中断模拟:在uart_interrupt_receive函数中,我们模拟了 UART 中断接收数据的过程,并将接收到的数据写入环形缓冲区。
  3. 数据读取:在主函数中,我们使用read_data_to_array函数将环形缓冲区中的数据读取到普通数组中,并通过printf打印输出结果。
  4. 扩展性:如果需要支持多线程场景,可以加入互斥锁来确保缓冲区访问的安全性。
  5. 优化建议:在高频率中断场景下,可以进一步优化数据结构和访问模式以提高性能。总结通过这个简单的环形缓冲区实现,我们了解了其基本原理和应用场景。

这个项目不仅适用于学习目的,还能作为实际项目中的基础模块进行扩展和优化。希望这篇文章能帮助您更好地理解和使用环形缓冲区技术。

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

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

相关文章

WHAT - Expo Go 和 development build

文章目录 1. 什么是 Expo Go?简介作用限制2. 什么是 Development Build(开发构建)?简介功能创建方式3. 它们有什么区别?总结建议怎么从 Expo Go 迁移到开发构建一、什么是“迁移”?二、迁移步骤总览三、详细操作步骤1. 安装 expo-dev-client2. 配置 eas.json(Expo 应用服…

Keepalived 配置 VIP 的核心步骤

Keepalived 配置 VIP 的核心步骤主要涉及安装软件、主备节点配置及服务管理。以下是具体操作指南: 一、安装 Keepalived ‌Ubuntu/Debian 系统‌ sudo apt update sudo apt install keepalived ‌CentOS/RHEL 系统‌ sudo yum install keepalived 注:需确保已配置 EPE…

HarmonyOS 5折叠屏自适应广告位布局方案详解

以下是HarmonyOS 5折叠屏广告位自适应布局的完整技术方案&#xff0c;综合响应式设计、动态交互与元服务融合策略&#xff1a; 一、核心布局技术‌ ‌断点响应式设计‌ 基于屏幕宽度动态调整布局结构&#xff0c;避免简单拉伸&#xff1a; // 定义断点阈值&#xff08;单位&am…

【数据分析十:Classification prediction】分类预测

一、分类的定义 已知&#xff1a;一组数据&#xff08;训练集&#xff09; (X, Y) 例如&#xff1a; x&#xff1a;数据特征/属性&#xff08;如收入&#xff09; y&#xff1a;类别标记&#xff08;是否有借款&#xff09; 任务: 学习一个模型&#xff0c;利用每一条记录…

设计模式-接口隔离原则(Interface Segregation Principle, ISP)

接口隔离原则&#xff08;Interface Segregation Principle, ISP&#xff09; 核心思想&#xff1a;客户端不应被迫依赖它们不使用的接口方法。 目标&#xff1a;通过拆分臃肿的接口为更小、更具体的接口&#xff0c;减少不必要的依赖&#xff0c;提高系统的灵活性和可维护性。…

超融合:系统工程还是软件工程? 从H3C UIS9.0看超融合的技术本质

在数字化转型的浪潮中&#xff0c;超融合基础架构&#xff08;Hyper-Converged Infrastructure, HCI&#xff09;凭借其简化部署、弹性扩展和高效运维的优势&#xff0c;成为企业IT基础设施升级的重要选择。 然而&#xff0c;关于超融合究竟属于系统工程还是软件工程的讨论一直…

青少年编程与数学 01-012 通用应用软件简介 01 Microsoft Office办公软件

青少年编程与数学 01-012 通用应用软件简介 01 Microsoft Office办公软件 **一、Microsoft Office办公软件概述****二、发展过程**&#xff08;一&#xff09;早期起源&#xff08;二&#xff09;技术演进 **三、主要用途或功能**&#xff08;一&#xff09;文字处理&#xff0…

vivado IP综合选项

在 Vivado 中&#xff0c;生成 IP 文件时的 Synthesis Options 提供了两种主要的综合模式&#xff1a;Global 和 Out of Context per IP。这两种模式的主要区别如下&#xff1a; 1. Global Synthesis&#xff08;全局综合&#xff09; 定义&#xff1a;在这种模式下&#xff…

零信任一招解决智慧校园的远程访问、数据防泄露、安全运维难题

随着数字化转型持续深入&#xff0c;“智慧校园”已成为高校发展的必经之路。从统一门户、一卡通到教务系统、选课系统&#xff0c;各类应用极大地便利了师生的工作与学习。 然而&#xff0c;便捷的背后也隐藏着一系列安全挑战。为了满足师生校外访问的需求&#xff0c;许多应…

web布局08

flex-basis 是 Flexbox 布局模块中 flex 属性的另一个子属性&#xff0c;在前面的课程中我们深度剖析了浏览器是如何计算 Flex 项目尺寸的&#xff0c;或者说 Flexbox 是如何工作的。对于众多 Web 开发者而言&#xff0c;在 CSS 中都习惯于使用像 width 、height 、min-* 和 ma…

在 Docker 27.3.1 中安装 PostgreSQL 16 的实践

前言&#xff1a;为什么在 Docker 中部署 PostgreSQL&#xff1f; 在云原生时代&#xff0c;容器化部署已成为生产环境的首选方案。通过 Docker 部署 PostgreSQL 具有以下显著优势&#xff1a; 环境一致性&#xff1a;消除“在我机器上能运行”的问题快速部署&#xff1a;秒级…

日志混乱与数据不一致问题实战排查:工具协同调试记录(含克魔使用点)

日志调试、状态验证和数据一致性排查&#xff0c;是iOS开发中最费时间、最易出错的工作之一。尤其是在模块之间异步通信频繁、本地缓存与远程状态需保持同步时&#xff0c;如果缺乏一套合适的流程与工具&#xff0c;开发人员极容易陷入“盲查状态”。 在一次跨部门联合开发的A…

Redis底层数据结构与内部实现

目录 一、RedisDB结构 1、RedisDB在Redis实例中的位置 2、RedisDB结构与核心组件 二、RedisObject结构 1、核心数据结构 1.1 简单动态字符串 (Simple Dynamic String - SDS) 1.2 字典 (Dict / Hash Table) 1.3 双端链表 (Linked List) 1.4 跳跃表 (Skip List) 1.5 压…

【项目实训】【项目博客#07】HarmonySmartCodingSystem系统前端开发技术详解(5.12-6.15)

【项目实训】【项目博客#07】HarmonySmartCodingSystem系统前端开发技术详解&#xff08;5.12-6.15&#xff09; 一、项目概述与目标 HarmonySmartCodingSystem是一个面向HarmonyOS开发者的智能编码辅助平台&#xff0c;旨在通过自然语言交互简化开发流程&#xff0c;提供智能…

系统性能优化-2 CPU

系统性能优化-2 CPU 其实除了 CPU 的频率&#xff0c;多核架构以及多 CPU 架构对系统运行的性能也是很大影响的&#xff0c;那么该如何充分利用 CPU 呢&#xff1f; CPU 架构 首先介绍一下当前主流的 CPU 架构&#xff0c;现在的系统基本都是多 CPU&#xff0c;一个 CPU 处理…

Docker Pull 相关配置指南

在Docker环境中&#xff0c;docker pull命令用于从Docker镜像仓库拉取镜像。为了确保Docker镜像能够快速、稳定地拉取&#xff0c;配置 docker pull相关的设置是非常重要的。本文将详细介绍如何配置Docker以优化 docker pull操作&#xff0c;涵盖镜像源配置、登录私有仓库、网络…

Python的Matplotlib库:从入门到精通的数据可视化实战指南

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 持续学习&#xff0c;不断…

CentOS查日志

在 CentOS 系统中&#xff0c;查看日志是系统维护和故障排查的重要技能。以下是常用的日志查看方法和工具&#xff1a; 1. 基本日志位置 CentOS 使用systemd管理服务&#xff0c;主要日志存储在&#xff1a; /var/log/messages&#xff1a;系统主日志/var/log/secure&#x…

Linux运维新人自用笔记(用虚拟机Ubuntu部署lamp环境,搭建WordPress博客)

内容全为个人理解和自查资料梳理&#xff0c;欢迎各位大神指点&#xff01; 每天学习较为零散。 day20 一、./configure 脚本命令 ./configure 是 Unix/Linux 系统中用于配置软件源代码的脚本命令&#xff0c;通常用于为后续的 make 和 make install 准备编译环境。 选项作…

JetBrains 2025 全家桶 包含 IDEA、WebStorm、DataGrip、Pycharm、CLion、GoLand、PhpStorm

JetBrains 2025 全家桶 11合1 包含&#xff1a;IDEA、WebStorm、DataSpell、DataGrip、Pycharm、RustRover、CLion、Rider、PhpStorm、RubyMine、GoLand。 原文地址&#xff1a;JetBrains 2025 全家桶 11合1 含 IDEA、PyCharm、DataGrip、WebStrom、GoLand、CLion、PhpStorm、D…