环形缓冲区例子

news2024/11/17 1:53:26

即使使用中断函数或者定时器函数记录按键,如果只能记录一个键值的话,如果不能
及时读走出来,再次发生中断时新值就会覆盖旧值。要解决数据被覆盖的问题,可以使用
一个稍微大点的缓冲区,这就涉及数据的写入、读出,可以使用环形缓冲区。
环形缓冲区特别适合这种场景:

  1. 一方写buffer
  2. 另一方读buffer

环形缓冲区实际上还是一维数组,假设有N 个数组项,从第0 个数组项开始遍历,访
问完第N-1 个数组项后,再从0 开始——这就是“环形”的含义,如下图所示:

环形缓冲区的工作原理如下图所示:

① 读位置、写位置:r、w,它们表示“下一个要读的位置”、“下一个要写的位置”,初始值都是0。这两个变量概念非常重要,记住写下标永远在读下标前面。
② 写数据时:把数据写入buffer[w],然后调整w指向下一个位置(当w 越界后要从0
开始),先写数据再调整下标位置。
③ 读数据时:从buffer[r]读出数据,然后调整r 指向下一个位置(当r 越界后要从0
开始),先读数据再调整下标位置。
④ 判断buffer 为空:r 等于w 时表示空,此时读下标位置追上了写下标位置。
⑤判断buffer为满:“下一个写位置”等于当前读位置,注意空出了一个元素未使用的,作为区分标志。

缓冲区的实现代码

circual_buffer.h

#ifndef _CIRCLE_BUF_H
#define _CIRCLE_BUF_H

#include <stdint.h>

//定义环形缓冲区
typedef struct circle_buf {
	uint32_t r; //读位置下标
	uint32_t w;  //写位置下标
	uint32_t len;  //缓冲区长度
	uint8_t *buf;  //存数据数组
}circle_buf, *p_circle_buf;


void circle_buf_init(p_circle_buf pCircleBuf, uint32_t len, uint8_t *buf);

int circle_buf_read(p_circle_buf pCircleBuf, uint8_t *pVal);

int circle_buf_write(p_circle_buf pCircleBuf, uint8_t val);

#endif /* _CIRCLE_BUF_H */

circual.c

#include <stdint.h>
#include "circle_buffer.h"

//环形缓冲区初始化
void circle_buf_init(p_circle_buf pCircleBuf, uint32_t len, uint8_t *buf)
{
	pCircleBuf->r = pCircleBuf->w = 0;
	pCircleBuf->len = len;
	pCircleBuf->buf = buf;
}

//读环形缓冲区数据
int circle_buf_read(p_circle_buf pCircleBuf, uint8_t *pVal)
{
	//环形缓冲区非空,r等于w表示空
	if (pCircleBuf->r != pCircleBuf->w)
	{
		*pVal = pCircleBuf->buf[pCircleBuf->r];
		
		pCircleBuf->r++;
		//r下标应该在0--len-1,如果下一个读位置等于len,下标从0开始
		if (pCircleBuf->r == pCircleBuf->len)
			pCircleBuf->r = 0;
		return 0;
	}
	else
	{
		return -1;
	}
}

//写数据到环形缓冲区
int circle_buf_write(p_circle_buf pCircleBuf, uint8_t val)
{
	uint32_t next_w;
	
	next_w = pCircleBuf->w + 1;
    //注意要先更新写下标位置,再去判断环形缓冲区是否非满
	if (next_w == pCircleBuf->len)
		next_w = 0;
	
	//环形缓冲区非满
	if (next_w != pCircleBuf->r)
	{
		pCircleBuf->buf[pCircleBuf->w] = val;
		pCircleBuf->w = next_w;
		return 0;
	}
	else
	{
		return -1;
	}
}

以下是stm32应用,使用缓冲区记录按键的值,防止丢失

#include "main.h"
#include "i2c.h"
#include "gpio.h"
#include "driver_oled.h"
#include "circle_buffer.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
struct soft_timer {
	uint32_t timeout;
	void * args;
	void (*func)(void *);
};
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

int g_key_cnt = 0; //统计按键次数

void key_timeout_func(void *args);

struct soft_timer key_timer = {~0, NULL, key_timeout_func};

static uint8_t g_data_buf[100];
static circle_buf g_key_bufs;  //缓冲区数据

void key_timeout_func(void *args)
{
	uint8_t key_val; /* 按下是0x1, 松开 0x81 */
	g_key_cnt++; //统计按键中断次数
	key_timer.timeout = ~0; //定时器计数值清0,防止反复触发按键计数
	
	/* read gpio */
	if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET)
		key_val = 0x1;
	else
		key_val = 0x81;
	
	/* put key val into circle buf */
	circle_buf_write(&g_key_bufs, key_val); 
}

void mod_timer(struct soft_timer *pTimer, uint32_t timeout)
{
	pTimer->timeout = HAL_GetTick() + timeout;
}

//定时器中断处理函数,每1ms触发一次
void check_timer(void)
{
	if (key_timer.timeout <= HAL_GetTick())
	{
		key_timer.func(key_timer.args);
	}
}

//按键中断处理函数,每次修改定时器的值推迟10ms
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if (GPIO_Pin == GPIO_PIN_14)
	{		
		mod_timer(&key_timer, 10);
	}
}
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	int len;
	/* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
	//在主函数中初始化缓冲区
  circle_buf_init(&g_key_bufs, 100, g_data_buf);
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */
  
	// Init OLED
    OLED_Init();
	 
	// 清屏
	OLED_Clear();
  
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  //HAL_Delay(10000);
  OLED_PrintString(0, 0, "Cnt     : "); //在第一行打印,第二个参数决定
  len = OLED_PrintString(0, 2, "Key val : "); //在第二行打印,第二个参数决定
  while (1)
  {
	  OLED_PrintSignedVal(len, 0, g_key_cnt);
		
	  uint8_t key_val = 0;
	  if (0 == circle_buf_read(&g_key_bufs, &key_val))
	  {
		  OLED_ClearLine(len, 2);  //清除一行指定位置后的数据
		  OLED_PrintHex(len, 2, key_val, 1);
	  }
		
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

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

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

相关文章

闯关leetcode——69. Sqrt(x)

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/sqrtx/description/ 内容 Given a non-negative integer x, return the square root of x rounded down to the nearest integer. The returned integer should be non-negative as well. You mu…

MySQL --基本查询(上)

文章目录 1.Create1.1单行数据全列插入1.2多行数据指定列插入1.3插入否则更新1.4替换 2.Retrieve2.1 select列2.1.1全列查询2.1.2指定列查询2.1.3查询字段为表达式2.1.4 为查询结果指定别名2.1.5结果去重 2.2where 条件2.2.1英语不及格的同学及英语成绩 ( < 60 )2.2.2语文成…

深度解读 2024 Gartner DevOps 魔力象限

上周 Gartner 刚发布了 2024 年度的 DevOps 魔力象限。我们也第一时间来深度解读一下这份行业里最权威的报告。 和2023年对比 23 年入围 14 家厂商&#xff0c;24 年入围 11 家。4 家厂商从报告中消失&#xff0c;分别是 Bitrise, Codefresh, Google Cloud Platform (GCP), VM…

调度_命令行_环境变量

linux的进程调度算法 饥饿问题 新建进程/时间片结束进程&#xff0c;若放回active&#xff0c;很可能该进程优先级太高&#xff0c;下一个还是执行该进程&#xff0c;导致不断执行同一进程&#xff0c;各进程调度不均衡。 饥饿问题解决 新建进程不能到active&#xff0c;要到…

力扣 24.两两交换链表中的节点

力扣《反转链表》系列文章目录 刷题次序&#xff0c;由易到难&#xff0c;一次刷通&#xff01;&#xff01;&#xff01; 题目题解206. 反转链表反转链表的全部 题解192. 反转链表 II反转链表的指定段 题解224. 两两交换链表中的节点两个一组反转链表25. K 个一组翻转链表K …

《操作系统 - 清华大学》第一讲:操作系统概述 —— 学习内容概述

文章目录 1. 内容摘要2. 实验内容 1. 内容摘要 在这里对学习内容做一个整体上的介绍&#xff0c;那在这里包括我们要学习的内容&#xff0c;实验的内容。操作系统课涉及到计算机系统当中的资源管理&#xff0c;所以我们围绕着操作系统的实现来介绍相关内容&#xff0c;那主要分…

①大缓存ModbusRTU485数据集中采集器寄存器线圈重映射从站并发采集Modbus 串口RS485 转 RS485

大缓存ModbusRTU485数据集中采集器寄存器线圈重映射从站并发采集https://item.taobao.com/item.htm?ftt&id811821574300 产品型号&#xff1a; 一分一路 MS-A1-C011 一分2路 MS-A1-C021 一分4路 MS-A1-C041 一分7路 MS-A1-C071 一般技术规格 1.串口 MS-A1…

【hot100-java】【最长公共子序列】

R8-多维dp篇 直接上递推 class Solution {public int longestCommonSubsequence(String text1, String text2) {char[] stext1.toCharArray(); char[] ttext2.toCharArray(); int ns.length;int mt.length;int [][] fnew int[n1][m1];for (int i0;i<n;i){for (int j0;j<…

MySQL | 实战 | 4 种将数据同步到ES方案

文章目录 1. 前言2. 数据同步方案2.1 同步双写2.2 异步双写2.3 定时更新2.4 基于 Binlog 实时同步 3. 数据迁移工具选型3.1 Canal3.2 阿里云 DTS3.3 Databus3.4 Databus和Canal对比3.4 其它 4. 后记 上周听到公司新同事分享 MySQL 同步数据到 ES 的方案&#xff0c;发现很有意思…

02【Matlab系统辨识】白噪声

1.白噪声与有色噪声 1.1 白噪声(white noise) 系统辨识中所用到的数据通常都含有噪声。从工程实际出发&#xff0c;这种噪声往往可以视为具有有理谱密度的平稳随机过程。白噪声是一种最简单的随机过程&#xff0c;是由一系列不相关的随机变量组成的理想化随机过程。白噪声的数…

vue3 vxe-grid 通过数据库返回的列信息,生成columns,并且其中有一列是img类型,进行slots的格式化处理。

1、一般我们写死的列信息的时候&#xff0c;会这样定义&#xff1a; 2、然后我们在template里面&#xff0c;这样这样写slots格式化部分&#xff1a; 这样表格中就会展示出一张图片&#xff0c;并且&#xff0c;我们点击了可以查看大图。 3、那么我们从数据库中返回的列&#…

三菱FX5U PLC故障处理(各种出错的内容、原因及处理方法进行说明。)

对使用系统时发生的各种出错的内容、原因及处理方法进行说明。 故障排除的步骤 发生故障时&#xff0c;按以下顺序实施故障排除。 1.确认各模块是否正确安装或正确配线。 2、确认CPU模块的LED。 3.确认各智能功能模块的LED。(各模块的用户手册) 4、连接工程工具&#xff0c;启…

Golang | Leetcode Golang题解之第题432题全O(1)的数据结构

题目&#xff1a; 题解&#xff1a; type node struct {keys map[string]struct{}count int }type AllOne struct {*list.Listnodes map[string]*list.Element }func Constructor() AllOne {return AllOne{list.New(), map[string]*list.Element{}} }func (l *AllOne) Inc(ke…

分布式环境中,接口超时到底怎么处理?

目录标题 为什么会存在超时?如何应对可能发生的超时?1. 设置合理的超时时间2. 重试机制3. 熔断机制4. 监控和报警5. 日志记录6. 限流和降级7. 异步处理 以上总结 为什么会存在超时? 接口超时是分布式系统中常见的问题&#xff0c;其原因多种多样&#xff0c;涉及网络、服务…

深入探究PR:那些被忽视却超实用的视频剪辑工具

如果想要了解视频剪辑的工具&#xff0c;那一定听说过pr视频剪辑吧。如果你是新手其实我更推荐你从简单的视频剪辑工具入手&#xff0c;这次我就介绍一些简单好操作的视频剪辑工具来入门吧。 1.福晰视频剪辑 连接直达>>https://www.pdf365.cn/foxit-clip/ 这款工具操…

Docker 付费订阅价格大幅上调,免费订阅功能受限,云计算和安全产品有调整

云计算de小白 同时&#xff0c;免费的 Docker Personal 订阅将不再包含 Build Cloud 分钟数&#xff0c;支持范围将从三个 Scout 存储库变为仅一个&#xff0c;并且仅限于一个具有 2 GB 存储空间的私有 Docker Hub 容器注册存储库。 不过&#xff0c;Docker也对云计算和安全产…

【WPF】03 动态生成控件

说明 今天记录一篇关于动态生成控件的方法&#xff0c;也是反复查了一些资料&#xff0c;逐步完善成自己需要的方法&#xff0c;感觉还是比较好用的。通过这个需求&#xff0c;在网上也找了一些资料&#xff0c;发现了一个开源图形UI组件HandyControl&#xff0c;觉得比较好&a…

统信服务器操作系统【Cron定时任务服务】

Cron定时任务服务服务介绍、服务管理、服务配置 文章目录 一、功能概述二、功能介绍1. Cron 服务管理2.Cron 服务管理3.Cron 服务配置run-parts一、功能概述 cron是一个可以用来根据时间、日期、月份、星期的组合来 调度对周期性任务执行的守护进程。利用 cron 所提供的功能,可…

分布式环境中,接口超时重试带来的的幂等问题如何解决?

目录标题 幂等不能解决接口超时吗&#xff1f;幂等的重要性什么是幂等?为什么需要幂等?接口超时了&#xff0c;到底如何处理&#xff1f; 如何设计幂等?幂等设计的基本流程实现幂等的8种方案1.selectinsert主键/唯一索引冲突&#xff08;常用&#xff09;2.直接insert 主键…

【Oauth2整合gateway网关实现微服务单点登录】

文章目录 一.什么是单点登录&#xff1f;二.Oauth2整合网关实现微服务单点登录三.时序图四.代码实现思路1.基于OAuth2独立一个认证中心服务出来2.网关微服务3产品微服务4.订单微服务5.开始测试单点登录 一.什么是单点登录&#xff1f; 单点登录&#xff08;Single Sign On&…