环形缓冲区例子

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

  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;elsekey_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 OLEDOLED_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.xdnf.cn/news/149359.html

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

相关文章

MyBatis - 动态SQL

前言 我们在某网站填写个人信息时&#xff0c;时常会遇到可以选填的空&#xff08;即可填&#xff0c;可不填&#xff09;&#xff0c;由于之前讲过的Java中的SQL语句都是固定的&#xff0c;且我们不可能对所有情况都写出与之对应的插入语句&#xff08;太过繁琐&#xff09;&…

【LLM多模态】Animatediff文生视频大模型

note AnimateDiff框架&#xff1a;核心是一个可插拔的运动模块&#xff0c;它可以从真实世界视频中学习通用的运动先验&#xff0c;并与任何基于相同基础T2I的个性化模型集成&#xff0c;以生成动画。训练策略&#xff1a;AnimateDiff的训练包括三个阶段&#xff1a; 领域适配…

56 mysql 用户权限相关的实现

前言 这里讨论 mysql 的权限相关处理 使用如下语句创建 tz_test 用户, 并赋予他 test_02 数据库的查询权限 create user tz_test% identified by tz_test; grant select on test_02.* to tz_test%; 查询目标数据表, 数据如下, tz_test_02 UPDATE command denied to user …

前端——表单和输入

今天我们来学习web前端中的表单和输入 表单 HTML 表单用于收集用户的输入信息&#xff0c;用表单标签来完成服务器的一次交互。 HTML 表单表示文档中的一个区域&#xff0c;此区域包含交互控件&#xff0c;将用户收集到的信息发送到 Web 服务器。 HTML 表单通常包含各种输入…

Apache Dolphinscheduler:一个开源的分布式工作流调度系统

一个开源的分布式工作流调度系统 Apache Dolphinscheduler概述安装 单机部署准备工作启动DolphinScheduler登录DolphinScheduler启停服务命令配置数据库初始化数据库 DolphinScheduler集群模式准备工作修改install_env.sh文件修改dolphinscheduler_env.sh文件初始化数据库部署访…

【Python】数据可视化之分布图

分布图主要用来展示某些现象或数据在地理空间、时间或其他维度上的分布情况。它可以清晰地反映出数据的空间位置、数量、密度等特征&#xff0c;帮助人们更好地理解数据的内在规律和相互关系。 目录 单变量分布 变量关系组图 双变量关系 核密度估计 山脊分布图 单变量分布…

谷歌网站收录查询,怎么查看网站在谷歌的收录情况

在进行谷歌网站收录查询时&#xff0c;我们需采取一种既专业又系统的方法&#xff0c;以确保能够准确评估网站在谷歌搜索引擎中的可见性和收录状态。这一过程不仅关乎技术细节&#xff0c;还涉及到对搜索引擎优化&#xff08;SEO&#xff09;策略的理解与应用。以下是一个基于专…

MobaXterm基本使用 -- 服务器状态、批量操作、显示/切换中文字体、修复zsh按键失灵

监控服务器资源 参考网址&#xff1a;https://www.cnblogs.com/144823836yj/p/12126314.html 显示效果 MobaXterm提供有这项功能&#xff0c;在会话窗口底部&#xff0c;显示服务器资源使用情况 如内存、CPU、网速、磁盘使用等&#xff1a; &#xff08;完整窗口&#xff0…

QT| “无法粘贴窗口部件”错误以及customplot

“无法粘贴窗口部件”错误以及customplot “无法粘贴窗口部件”错误customplot下载添加到项目中使用QCustomPlot常用的代码 “无法粘贴窗口部件”错误 情景&#xff1a;使用QT设计界面&#xff0c;很多部分比较类似&#xff0c;可以复制另一个界面的ui&#xff0c;但是粘粘的时…

力扣 中等 1901.寻找峰值II

文章目录 题目介绍题解 题目介绍 题解 需要明白一个事实&#xff1a;从任意一个点出发&#xff0c;可以经过一个递增路径&#xff0c;找到一个极大值点。 求出一行的最大值&#xff0c;如果这行最大值比上面的要小&#xff0c;那峰值&#xff08;之一&#xff09;就会在上面 …

React-Native 中使用 react-native-image-crop-picker 在华为手机上不能正常使用拍照功能

背景: React-Native 0.66 中使用 react-native-image-crop-picker 在安卓 华为手机上不能正常使用拍照功能, 其他品牌正常 代码如下: import ImagePicker from react-native-image-crop-picker;ImagePicker.openCamera(photoOptions).then(image > {callback(image);}) …

如何释放并重新获得ip地址呢?

如何释放并重新获得ip地址呢&#xff1f; 释放并重新获得一个IP地址的具体步骤如下&#xff1a; 1、要想从DHCP服务器重新获取ip&#xff0c;电脑必须设置成"自动获取ip",设置如下&#xff0c;在电脑桌面"网络"-属性-更改适配器设置为自动获取ip。 2、然…

在CentOS 6上安装Squid代理的方法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 Status: 已弃用 本文涵盖的 CentOS 版本已不再受支持。如果您目前正在运行 CentOS 6 服务器&#xff0c;我们强烈建议升级或迁移到受支持…

【威领,德新,中达安】9.23复盘

威领这次的底部是4个月 所以这种跳空高开&#xff0c;远离5日均线的&#xff0c;如果不是近期的利好板块&#xff0c;那么第二天可能要回调5日均线。所以按照我的收益准则&#xff0c;吃一个板可以出一半了。 到顶部十字剩下一半也出掉了。 如果做长期&#xff0c;我依旧认为威…

CSS03-CSS的引入方式

一、CSS的三种样式表 1-1、内部样式表 示例&#xff1a; 1-2、行内样式表 1-3、外部样式表 1-4、小结

在Windows系统上安装的 Arrow C++ 库

在Windows系统上安装的 Arrow C 库 正文第一步第二步第三步第四步注: 检查是否安装成功 吐槽 正文 第一步 git clone gitgithub.com:apache/arrow.git第二步 打开powershell (好像cmd也可以,不过我试了powershell中不报错,cmd中报错,不是很清楚为什么) 打开arrow的目录 cd …

统信服务器操作系统【qcow2 镜像空间扩容】方案

使用 qcow2 镜像安装系统,当默认安装系统存储空间不够用时,进行自定义扩容 文章目录 准备环境扩容步骤一、检查环境信息1.查看镜像信息2.查看镜像分区信息3.确认需要扩容的分区名二、扩容1.备份镜像2.创建新的镜像文件,并指定空间3.将系统扩容到新的镜像三、扩容 lvm 分区四…

C++读取txt文件中的句子在终端显示,同时操控鼠标滚轮(涉及:多线程,产生随机数,文件操作等)

文章目录 运行效果功能描述代码mian.cppincludeMouseKeyControl.hTipsManagement.h srcMouseControl.cppTipsManagement.cpp 运行效果 功能描述 线程一&#xff1a;每隔n随机秒&#xff0c;动一下鼠标滚轮&#xff0c;防止屏幕息屏。 线程二&#xff1a;运行时加载txt文件中的…

【RocketMQ入门】

RocketMQ入门 RocketMQ是阿里巴巴开源的分布式消息中间件&#xff0c;现在是Apache的一个顶级项目。在阿里内部使用非常 广泛&#xff0c;已经经过了"双11"这种万亿级的消息流转。 RocketMQ环境搭建 接下来我们先在linux平台下安装一个RocketMQ的服务 环境准备 …

C++ Mean Shift算法

原理 每个样本点最终会移动到核概率密度的峰值&#xff0c;移动到相同峰值的样本点属于同一种颜色 关键代码 template <typename PointType> inline typename MeanShift<PointType>::PointsVector MeanShift<PointType>::meanshift(const PointsVector &am…