环形缓冲区(Ring Buffer)在STM32 HAL库中的应用:防止按键丢失

news2024/11/26 2:04:52

环形缓冲区(Ring Buffer)又称为循环缓冲区或圆形队列,是一种数据结构,它用于管理固定大小的数据存储空间。环形缓冲区本质是一个一维数组,不过是收尾相连的,类比一条蛇咬自己尾巴。

环形缓冲区重要性:避免数据被覆盖。比如,使用中断函数或者定时器函数记录按键,如果只能记录一个键值的话,如果不能及时读走出来,再次发生中断时新值就会覆盖旧值。使用环形缓冲区可以避免数据被覆盖。

本文将详细介绍环形缓冲区的概念、工作原理,并以STM32F407微控制器结合HAL库为例,探讨如何利用环形缓冲区有效防止按键输入丢失。可参考定时器处理按键抖动

 一、开发环境

硬件:正点原子探索者 V3 STM32F407开发板

单片机:STM32F407ZGT6

Keil版本:5.32

STM32CubeMX版本:6.9.2

STM32Cube MCU Packges版本:STM32F4xx_DFP.2.14.0

按键引脚:PE2
串口:USART1(PA9,PA10)

  二、配置STM32CubeMX

  1. 启动STM32CubeMX,新建STM32CubeMX项目
  2. 选择MCU:在软件中选择你的STM32型号-STM32F407ZGT6。
  3. 选择时钟源:

  4. 配置时钟:
  5. 使能Debug功能:Serial Wire
  6. HAL库时基选择:SysTick
  7. USART1配置:选择异步模式。

8.配置工程参数:在Project标签页中,配置项目名称和位置,选择工具链MDK-ARM。​ 9.生成代码:在Code Generator标签页中,配置工程外设文件与HAL库,勾选头文件.c和.h文件分开,然后点击Project > Generate Code生成代码。 

三、代码实现与部署

  1.   添加circle_buffer.c,circle_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 */
    
    #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)
    {
    	if (pCircleBuf->r != pCircleBuf->w)
    	{
    		*pVal = pCircleBuf->buf[pCircleBuf->r];
    		
    		pCircleBuf->r++;
    		
    		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;
    	}
    }
    
  2. main.c增加代码

    /* USER CODE BEGIN Header */
    /**
      ******************************************************************************
      * @file           : main.c
      * @brief          : Main program body
      ******************************************************************************
      * @attention
      *
      * Copyright (c) 2024 STMicroelectronics.
      * All rights reserved.
      *
      * This software is licensed under terms that can be found in the LICENSE file
      * in the root directory of this software component.
      * If no LICENSE file comes with this software, it is provided AS-IS.
      *
      ******************************************************************************
      */
    /* USER CODE END Header */
    /* Includes ------------------------------------------------------------------*/
    #include "main.h"
    #include "usart.h"
    #include "gpio.h"
    
    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include <stdio.h>
    #include "circle_buffer.h"
    /* 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, 松开 0x2 */
    	g_key_cnt++;
    	key_timer.timeout = ~0;
    	/* read gpio */
    		if (HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_2 ) == GPIO_PIN_RESET)
        key_val = 0x1;
    		else
    		key_val = 0x2;
    	/* 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;
    }
    
    void check_timer(void)
    {
    	if (key_timer.timeout <= HAL_GetTick())
    	{
    		key_timer.func(key_timer.args);
    	}
    }
    
    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    {
    	if (GPIO_Pin == GPIO_PIN_2)
    	{		
    		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 */
    
      /* 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_USART1_UART_Init();
      /* USER CODE BEGIN 2 */
      HAL_Delay(5000);
    
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
    		uint8_t key_val = 0;  
    	  if (0 == circle_buf_read(&g_key_bufs, &key_val))
    	  {	 
    				printf("key_cnt:%d\r\n",g_key_cnt); //按下和松开累计次数
    				if(key_val == 0x1)
    					 printf("The key is pressed\r\n"); //按键按下
    				else if (key_val == 0x2)
    					 printf("Key is released\r\n");//按键松开
    	  }
    		
      }
      /* USER CODE END 3 */
    }
  3. stm32f4xx_it.c增加代码
    /**
      * @brief This function handles System tick timer.
      */
    void SysTick_Handler(void)
    {
      /* USER CODE BEGIN SysTick_IRQn 0 */
    
      /* USER CODE END SysTick_IRQn 0 */
      HAL_IncTick();
      /* USER CODE BEGIN SysTick_IRQn 1 */
    	extern void check_timer(void);
      check_timer();
      /* USER CODE END SysTick_IRQn 1 */
    }
  4.  usart.c增加代码:usart.c的第1行添加头文件#include <stdio.h>
    #include <string.h>,在末尾用户代码区增加如下代码。printf调用“fputc()”,“fgetc()”,该函数会使用HAL_UART_Transmit发送数据。
    /*
    * 添加如下代码,可不在工程设置中勾选Use MicroLIB
    */
    #pragma import(__use_no_semihosting)
    
    struct __FILE
    {
    	int a;
    };
    
    FILE __stdout;
    FILE __stdin;
    
    void _sys_exit(int x)
    {
    }
    
    
    /*****************************************************
    *function: 写字符文件函数
    *param1: 输出的字符
    *param2: 文件指针
    *return: 输出字符的ASCII码
    ******************************************************/
    int fputc(int ch, FILE *f)
    {
    	HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 10);
    	return ch;
    }
    /*****************************************************
    *function: 读字符文件函数
    *param1: 文件指针
    *return: 读取字符的ASCII码
    ******************************************************/
    int fgetc(FILE *f)
    {
    	uint8_t ch = 0;
    	HAL_UART_Receive(&huart1, (uint8_t*)&ch, 1, 10);
    	return (int)ch;
    }
  5. 连接USART1:用USB转TTL工具连接当前硬件USART1的PA9、PA10,GND。​​
  6. 打开串口助手:​​
  7. 编译代码:Keil编译生成的代码。
  8. 烧录程序:将编译好的程序用ST-LINK烧录到STM32微控制器中。

四、运行结果

观察结果:一旦程序烧录完成并运行,复位3秒后,按下4次,按下的4次(8次按下松开)每次都被记录下来。

复位3秒内,按下4次,按下的4次(8次按下松开)也被记录下来了,说明按下的键值被环形缓冲区记录下来了。

​五、注意事项

1.确保你的开发环境和工具已经正确安装和配置。

2.如果没有打印电压值,按一下复位键,检查连接和电源是否正确,注意根据你所用的硬件来接线,不要接错线。
3.在串口打印数据时,要确保波特率等参数与串口助手设置一致。

通过上述步骤,你就可以在STM32F407 HAL库利用环形缓冲区有效防止按键输入丢失。

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

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

相关文章

危机四伏|盘点紧盯我国的五大APT组织

毒云藤&#xff08;APT-C-01&#xff09; 命名&#xff1a;该组织是 360 独立发现的&#xff0c;并率先披露了该组织的部分相关信息符合 360 对 APT 组织就行独立命名的条件。 360 威胁情报中心将 APT-C-01 组织命名为“毒云藤”&#xff0c;主要是考虑了以下几方面的因素&…

L1415 【哈工大_操作系统】CPU调度策略一个实际的schedule函数

L2.7 CPU调度策略 1、调度的策略 周转时间&#xff1a;任务进入到任务结束&#xff08;后台任务更关注&#xff09;响应时间&#xff1a;操作发生到响应时&#xff08;前台任务更关注&#xff09;吞吐量&#xff1a;CPU完成的任务量 响应时间小 -> 切换次数多 -> 系统…

curses函数库简介及使用

curses函数库简介及使用 导语curses简介屏幕输出读取清除移动字符 键盘键盘模式输入 窗口WINDOW常用函数屏幕刷新优化 子窗口keypad彩色显示pad总结参考文献 导语 curses函数库主要用来实现对屏幕和光标的操作&#xff0c;它的功能定位处于简单文本行程序和完全图形化界面之间…

【重学 MySQL】五十八、文本字符串(包括 enum set)类型

【重学 MySQL】五十八、文本字符串&#xff08;包括 enum set&#xff09;类型 CHAR 和 VARCHARTEXT 系列ENUMSET示例注意事项 在 MySQL 中&#xff0c;文本字符串类型用于存储字符数据。这些类型包括 CHAR、VARCHAR、TEXT 系列&#xff08;如 TINYTEXT、TEXT、MEDIUMTEXT 和 L…

鸿蒙开发(NEXT/API 12)【管理应用与Wear Engine服务的连接状态】手机侧应用开发

监测应用与Wear Engine服务的连接状态 华为运动健康App在后台停止服务&#xff08;如功耗过高&#xff09;&#xff0c;从而导致应用与Wear Engine服务的连接状态发生变化。对于类似这种不确定的断开情况&#xff0c;开发者可以通过本功能特性了解当前应用和Wear Engine的连接…

NatGo我的世界联机篇

书接上回 这里的TCP是JAVA&#xff0c;UDP是BE&#xff0c;选自适合你的映射类型 内网端口就填下面图片在你游戏同一个地方的数字!!! 就是我填12345的地方&#xff0c;mod-自定义局域网联机 默认 25565&#xff0c;如果出现无法创建本地游戏&#xff0c;那可能是端口被占用或…

卷积层是如何学习到图像特征的?

你好啊&#xff0c;我是董董灿。 想搞懂这个问题&#xff0c;需要先了解我们所说的特征指的是什么&#xff1f;然后再了解卷积核是如何学到的特征。 我们一步步来。 1、我们先来理解图像的特征 对于一张原始图像而言&#xff0c;说原始图像是相对于经过卷积处理而言的。 对…

【文心智能体】旅游攻略版,手把手教你调用插件实现智能体,绝对的干货满满!

1.灵感来源 需要创建一个智能体&#xff0c;首先当然是需要一个创作灵感啦&#xff0c;那么恰逢国庆假期&#xff0c;但是网上各种各样的旅游资料使我头晕目眩&#xff0c;刚好呢百度的文心智能体平台给我们提供了各种各样的插件以及知识库&#xff0c;可以供我们随心所欲地调…

嵌入式硬件设计中EDA布局与布线实现

大家好,今天主要给大家分享一下,如何使用立创EDA进行布局和布线,具体实现过程如下: 第一:PCB概念介绍 在介绍PCB的时候,先来说明一下,电子管的发明史。 贝尔在1876年发明了电话,爱迪生1879年发明了白炽灯、特斯拉于1888年发明了电动机,所有这些,都为电子学的诞生准…

2024 热门的4大电脑剪辑软件大盘点。

在电脑上进行视频剪辑能够让我们更好的发挥创意&#xff0c;制作出精彩的视频作品。同时也需要依赖一些比较专业的视频剪辑工具。这几款视频剪辑软件&#xff0c;无论是制作个人视频、商业广告还是电影预告片&#xff0c;都是非常不错的选择。 1、福昕电脑剪辑 直达链接&#…

vSAN05:vSAN延伸集群简介与创建、资源要求与计算、高级功能配置、维护、故障处理

目录 vSAN延伸集群延伸集群创建延伸集群的建议网络配置vSAN延伸集群的端口见证主机的资源要求vSAN延伸集群中见证节点带宽占用vSAN延伸集群的允许故障数vSAN延伸集群不同配置下的空间占用 vSAN延伸集群的HA配置vSAN延伸集群的DRS配置vSAN存储策略以及虚拟机/主机策略的互操作vS…

系统架构设计师③:数据块系统

系统架构设计师③&#xff1a;数据块系统 数据库模式 数据库模式是指数据库的结构和组织方式&#xff0c;它描述了数据库中数据的逻辑结构和组织方式&#xff0c;是数据库设计的核心组成部分。以下是关于数据库模式的详细解析&#xff1a; 一、定义与组成 定义&#xff1a;…

4款专业电脑数据恢复软件,帮你保障数据安全。

电脑里面会出现的数据丢失场景有很多&#xff0c;像硬盘故障、回收站清空、电脑格式化、系统崩溃、病毒入侵等等&#xff1b;如果发现数据丢失后&#xff0c;建议应停止使用电脑&#xff0c;避免新的数据写入覆盖丢失的数据。然后再尝试进行数据找回&#xff0c;如果想自己进行…

系统分析师16:系统测试与维护

1 内容概要 2 软件测试类型 2.1 测试类型 动态测试【计算机运行】 白盒测试法&#xff1a;关注内部结构与逻辑灰盒测试法&#xff1a;介于两者之间黑盒测试法&#xff1a;关注输入输出及功能 静态测试【人工监测和计算机辅助分析】 桌前检查代码审查代码走查以上三个都是做的…

【C++11】新特性

前言&#xff1a; C11 是C编程语言的一个重要版本&#xff0c;于2011年发布。它带来了数量可观的变化&#xff0c;包含约 140 个新特性&#xff0c;以及对 C03 标准中约600个缺陷的修正&#xff0c;更像是从 C98/03 中孕育出的新语言 列表初始化 C11 中的列表初始化&#xff0…

社群团购中的用户黏性价值:以开源小程序多商户AI智能名片商城源码为例

摘要&#xff1a;本文探讨社群团购中的用户黏性价值&#xff0c;分析其与传统团购网站的区别&#xff0c;并阐述开源小程序多商户AI智能名片商城源码在增强社群团购用户黏性方面可能发挥的作用。 一、引言 在当今的商业环境中&#xff0c;社群团购逐渐成为一种重要的营销模式。…

新个性化时尚解决方案!Prompt2Fashion:自动生成多风格、类型时尚图像数据集。

今天给大家介绍一种自动化生成时尚图像数据的方法Prompt2Fashion。 首先创建了一组描述&#xff0c;比如“适合婚礼的休闲风格服装”&#xff0c;然后用这些描述来指导计算机生成图像。具体来说&#xff0c;他们使用了大型语言模型来写出这些服装的描述&#xff0c;接着将这些描…

毕业设计 大数据电影数据分析与可视化系统

文章目录 0 简介1 课题背景2 效果实现3 爬虫及实现4 Flask框架5 Ajax技术6 Echarts7 最后 0 简介 今天学长向大家介绍一个机器视觉的毕设项目 &#x1f6a9;基于大数据的电影数据分析与可视化系统 项目运行效果(视频)&#xff1a; 毕业设计 大数据电影评论情感分析 &#x1…

运动传感器

运动传感器 当你走近一些自动开关门、自动开关灯泡或自动启动自动扶梯的地方时&#xff0c;你是否会产生这样的疑问&#xff1a;**"它是怎么做到的&#xff1f; **它是怎么做到的&#xff1f; 如果有&#xff0c;本教程不仅会回答&#xff0c;还会告诉你如何制作。 让我们…