学习STM32必备的数据结构——环形缓冲区详解【STM32 CubeMX】

文章目录

  • 前言
  • 一、环形缓冲区是什么
  • 二、实现环形缓冲区
  • 实现分析
  • 2.1 环形缓冲区初始化
  • 2.2 写buf
  • 2.3 读buf
  • 2.4 测试
  • 三、代码总况
  • 总结

  • 前言

    在嵌入式系统开发中,经常需要处理数据的缓存和传输,而环形缓冲区是一种常见且有效的数据结构,特别适用于处理实时数据流或者在有限的内存资源下高效地管理数据。在STM32微控制器的开发中,使用CubeMX工具可以方便地配置和生成环形缓冲区的代码,从而加速开发过程并提高代码的可维护性。本文将介绍STM32 CubeMX中环形缓冲区的使用方法以及其在嵌入式系统开发中的重要性。


    一、环形缓冲区是什么

    当我们处理数据时,有时候需要一个地方来临时存储它们,就好像我们用盘子装菜一样。但是,内存有限,如果盘子装满了就得从头开始放菜,这样效率不高。环形缓冲区就像是一个环形的菜盘,当盘子满了,就会从一端开始取走菜,同时从另一端继续放新的菜,这样就能不停地装菜,而且效率很高。

    现在,让我们画一个简单的图来演示一下:

    [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ]
     ^                           ^
     |                           |
    取走                        放入
    

    这里,我们有一个有8个位置的环形缓冲区,用方括号表示每个位置。现在,我们从左边取走了一个数据,然后从右边放入了一个新的数据。这样,缓冲区就像一个环,数据可以不停地在其中循环。

    这种数据结构适合一方读buf,一方写buf的情节

    二、实现环形缓冲区

    实现分析

    我们需要定义存储区buf

    char buf[100];
    

    我们还需要定义读位置为0,写位置为0
    后面我们通过这两个变量来操作读和写

    int r,w = 0;
    

    那么怎么写入数据和读数据呢?
    读:
    首先肯定要有数据我们才能读吧,那么怎么判断有没有数据呢?
    当我们的r = w时,他是空的,如果是空,我们肯定不能读,如果是有数据,直接返回对应下标即可

    写:
    首先我们要知道,如果buf满了,肯定是不能把原来的数据给干掉的,那么我们怎么判断他有没有满呢,如果直接使用r = w来判断,那就和判断空是一样的了,我们可以使用w+1 = r来判断,因为如果没满,我写一个进去,他肯定不等于r,如果满了我写一个进去他等于r就是满了,我们使用w+1来模拟写入后的w状态即可

    如果读和写超过了这个buf的原始大小,因为他是环形缓冲区,所以需要把下标取余个buf的size

    2.1 环形缓冲区初始化

    首先声明一个结构体,这个结构体存储着缓冲区buf,读r和写w

    typedef struct
    {
    	int *buf;
    	int len;
    	int r;
    	int w;
    }CircleBuf,*p_CircleBuf;
    

    接下来声明一个函数进行init操作

    void CircleBufInit(p_CircleBuf pbuf,int* buf ,int len)
    {
    	pbuf->r = pbuf->w = 0;
    	pbuf->buf = buf;
    	pbuf->len = len;
    }
    

    对于这个函数,你可以从外部传入buf进行缓冲区的初始化,也可以使用malloc进行内存的分配初始化

    2.2 写buf

    我们需要实现下面这个函数来进行写buf

    void CircleBufWrite(p_CircleBuf pCircleBuf, int val);
    

    首先我们需要先定义一个变量存储w的下一个下标,并且如果下一个下标超出len,需要变成0,实现环形

    int nextW = pCircleBuf->w + 1;
    nextW %= pCircleBuf->len;
    

    接下来,我们需要去判断nextW是否等于r,如果不等于,代码没有满

    if (pCircleBuf->r != nextW)
    

    如果没有满,我们需要把对应的w下标赋值成val
    然后w下标++,再对w进行取余len,如果下标超出len,需要变成0,实现环形

    pCircleBuf->buf[pCircleBuf->w] = val;
    pCircleBuf->w++;
    pCircleBuf->w %= pCircleBuf->len;
    

    2.3 读buf

    我们需要实现下面这个函数来进行读buf

    void CircleBufRead(p_CircleBuf pCircleBuf, int* val);
    

    首先我们先要判断r !=w才能进行读操作

    if (pCircleBuf->r != pCircleBuf->w)
    

    如果不等于
    我们可以把r对应的下标给val
    然后r读下标++
    因为可能存在读完的情况所以我们需要进行取余操作

    *val = pCircleBuf->buf[pCircleBuf->r];
    pCircleBuf->r++;
    pCircleBuf->r %= pCircleBuf->len;
    

    如果等于,我们可以把val的值变成NULL

    else
    {
    	val = NULL;
    }
    

    2.4 测试

    我们可以先写入一部分数据,然后,我们可以使用CircleBufRead读出来,读出来之后立马进行写,我们可以通过下面这种方法进行缓冲区的测试:

    CircleBuf cBuf;
    int buf[100] = { 0 };
    CircleBufInit(&cBuf,&buf,100);
    
    for (int i = 0; i < 100; i++)
    {
    	CircleBufWrite(&cBuf, i);
    }
    
    int i = 100;
    while (1)
    {
    	int temp = -1;
    	CircleBufRead(&cBuf, &temp);
    	printf("%d ", temp);
    
    	CircleBufWrite(&cBuf, i);
    	i++;
    
    	Sleep(10);
    }
    

    三、代码总况

    //Circle.c
    #include "circleBuf.h"
    #include <memory.h>
    
    void CircleBufInit(p_CircleBuf pbuf,int* buf ,int len)
    {
    	pbuf->r = pbuf->w = 0;
    	pbuf->buf = buf;
    	pbuf->len = len;
    }
    
    void CircleBufRead(p_CircleBuf pCircleBuf, int* val)
    {
    	if (pCircleBuf->r != pCircleBuf->w)
    	{
    		*val = pCircleBuf->buf[pCircleBuf->r];
    		pCircleBuf->r++;
    		pCircleBuf->r %= pCircleBuf->len;
    	}
    	else
    	{
    		val = NULL;
    	}
    }
    
    void CircleBufWrite(p_CircleBuf pCircleBuf, int val)
    {
    	int nextW = pCircleBuf->w + 1;
    	nextW %= pCircleBuf->len;
    	if (pCircleBuf->r != nextW)
    	{
    		pCircleBuf->buf[pCircleBuf->w] = val;
    		pCircleBuf->w++;
    		pCircleBuf->w %= pCircleBuf->len;
    	}
    }
    
    //Circle.h
    #pragma once
    
    typedef struct
    {
    	int *buf;
    	int len;
    	int r;
    	int w;
    }CircleBuf,*p_CircleBuf;
    
    void CircleBufInit(p_CircleBuf pbuf, int*buf ,int len);
    
    void CircleBufRead(p_CircleBuf pCircleBuf, int* val);
    
    void CircleBufWrite(p_CircleBuf pCircleBuf, int val);
    
    //main.c
    #include <stdio.h>
    #include <stdlib.h>
    #include "circleBuf.h"
    #include <Windows.h>
    
    int main()
    {
    	CircleBuf cBuf;
    	int buf[100] = { 0 };
    	CircleBufInit(&cBuf,&buf,100);
    
    	for (int i = 0; i < 100; i++)
    	{
    		CircleBufWrite(&cBuf, i);
    	}
    
    	int i = 100;
    	while (1)
    	{
    		int temp = -1;
    		CircleBufRead(&cBuf, &temp);
    		printf("%d ", temp);
    
    		CircleBufWrite(&cBuf, i);
    		i++;
    
    		Sleep(10);
    	}
    
    	system("pause>0");
    	return 0;
    }
    

    总结

    环形缓冲区是嵌入式系统开发中常用的数据结构之一,具有高效、可靠的特性。通过STM32 CubeMX工具,我们可以轻松地配置和生成环形缓冲区的代码,从而简化开发流程并提高代码的可维护性。掌握环形缓冲区的原理和使用方法,对于STM32微控制器的开发者来说是必不可少的技能。希望本文能够帮助读者更好地理解和应用环形缓冲区,从而更加高效地开发嵌入式系统。

    作者:人才程序员

    物联沃分享整理
    物联沃-IOTWORD物联网 » 学习STM32必备的数据结构——环形缓冲区详解【STM32 CubeMX】

    发表评论