STM32HAL 快速入门(二十):UART 中断改进 —— 环形缓冲区解决数据丢失

前言

大家好,这里是 Hello_Embed。上一篇我们用中断方式实现了 UART 收发,但发现一个关键问题:若 CPU 在处理其他任务时未及时重新使能接收中断,新数据会覆盖旧数据,导致丢失。本篇的核心改进方案是 ——“中断接收 + 环形缓冲区”:中断中实时将接收的数据存入缓冲区,主程序从缓冲区按需读取,彻底解决 “接收不及时” 的痛点。下一篇我们将进一步学习更高效的 DMA 方式,现在先聚焦这个经典的中断优化方案。

本篇笔记所提及的环形缓冲区相关知识可在同系列笔记14-15中找到

一、改进核心思路

要解决数据丢失,关键是让 “接收” 和 “处理” 解耦 —— 接收端用中断快速存数据,处理端(主程序)慢慢读数据,无需同步等待。具体思路有两点:

  1. 提前使能接收中断:程序启动时就开启 UART 接收中断,确保任何时候有数据都能被捕获;
  2. 中断中存环形缓冲区:每次接收中断触发时,立即将数据存入环形缓冲区,避免数据在寄存器中被覆盖,主程序从缓冲区读取数据时不影响接收。
二、代码实现:从缓冲区定义到中断处理

我们基于上一篇的工程修改,核心是在usart.c中集成环形缓冲区,实现 “中断存数据、主程序读数据” 的流程。

1. 准备工作:包含头文件与定义核心变量

首先在usart.c的开头包含环形缓冲区头文件(需确保路径正确),并定义接收相关的变量:

#include <circle_buffer.h>  // 包含环形缓冲区头文件/* USER CODE BEGIN 1 */
// 1. 发送完成标志(沿用上篇)
static volatile int g_tx_cplt = 0;
// 2. 接收暂存变量:每次中断接收1字节,先存在这里
static uint8_t g_RecvChar;
// 3. 环形缓冲区存储数组:容量100字节,可存100个接收数据
static uint8_t g_RecvBuf[100];
// 4. 环形缓冲区结构体:管理读写指针和长度
static circle_buf g_uart1_rx_bufs;
/* USER CODE END 1 */
2. 步骤 1:初始化环形缓冲区 + 启动接收中断

定义StartUART1Recv函数,作用是初始化环形缓冲区提前使能接收中断—— 程序启动时调用一次,后续无需手动开启中断。

/* USER CODE BEGIN 1 */
// 启动UART1接收:初始化缓冲区+使能接收中断
void StartUART1Recv(void)
{// 初始化环形缓冲区:绑定结构体、容量100、存储数组g_RecvBufcircle_buf_init(&g_uart1_rx_bufs, 100, g_RecvBuf);// 使能接收中断:接收1字节到g_RecvChar,触发中断后进入回调函数HAL_UART_Receive_IT(&huart1, &g_RecvChar, 1);
}
/* USER CODE END 1 */
  • circle_buf_init参数说明:&g_uart1_rx_bufs(缓冲区结构体)、100(容量)、g_RecvBuf(存储数组);
  • HAL_UART_Receive_IT:使能 RXNE 中断(RDR 寄存器非空时触发),接收的 1 字节暂存到g_RecvChar
3. 步骤 2:接收中断回调 —— 数据存入缓冲区

重写HAL_UART_RxCpltCallback(接收完成回调函数),核心逻辑是:将暂存的字节存入环形缓冲区,并重新使能接收中断(确保下一个数据能被捕获)。

/* USER CODE BEGIN 1 */
// 接收完成回调函数:中断触发后执行
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if (huart == &huart1)  // 确认是USART1的中断{// 1. 将接收的字节(g_RecvChar)写入环形缓冲区circle_buf_write(&g_uart1_rx_bufs, g_RecvChar);// 2. 重新使能接收中断:准备接收下一个字节(关键!避免中断断连)HAL_UART_Receive_IT(&huart1, &g_RecvChar, 1);}
}
/* USER CODE END 1 */
  • 为什么要 “重新使能中断”?HAL_UART_Receive_IT是 “一次性” 的 —— 接收 1 字节后会自动关闭中断,必须重新调用才能继续接收下一字节。
4. 步骤 3:封装缓冲区读取函数

定义UART1GetChar函数,供主程序调用,从环形缓冲区读取 1 字节数据(成功返回 0,失败返回 - 1,对应缓冲区空)。

/* USER CODE BEGIN 1 */
// 从环形缓冲区读取1字节数据
int UART1GetChar(uint8_t *pVal)
{// 调用环形缓冲区读函数,将数据存入pVal指向的地址return circle_buf_read(&g_uart1_rx_bufs, pVal);
}
/* USER CODE END 1 */
  • pVal:主程序传入的 “数据存储地址”,读取成功后,缓冲区的数据会存在这里;
  • 返回值:0 表示读取成功(有数据),-1 表示缓冲区空(无数据)。
5. 沿用发送相关函数

若需要保留中断发送功能,可沿用上篇的发送完成回调和等待函数(确保发送流程正常):

/* USER CODE BEGIN 1 */
// 发送完成回调函数(沿用)
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{if (huart == &huart1)g_tx_cplt = 1;
}// 等待发送完成(沿用,主程序调用)
void Wait_Tx_Complete(void)
{while (g_tx_cplt == 0);  // 等待发送完成标志置位g_tx_cplt = 0;           // 复位标志
}
/* USER CODE END 1 */
三、主程序调用:实现 “接收 - 处理 - 返回” 完整流程

main.c中,先启动接收中断,再通过 “发送提示→读取缓冲区→数据加 1 返回” 的逻辑,验证改进方案是否有效。

1. 声明外部函数

main.c/* USER CODE BEGIN PV */区域,声明usart.c中定义的函数:

/* USER CODE BEGIN PV */
// 声明外部函数:启动接收中断、等待发送完成、读取缓冲区数据
extern void StartUART1Recv(void);
extern void Wait_Tx_Complete(void);
extern int UART1GetChar(uint8_t *pVal);
/* USER CODE END PV */
2. 主程序核心逻辑
/* USER CODE BEGIN 2 */
// 1. 启动UART1接收:初始化缓冲区+使能中断(程序启动时执行一次)
StartUART1Recv();// 2. 定义发送的提示信息和接收变量
char *str1 = "Please enter a char : \r\n";
uint8_t c;  // 存储从缓冲区读取的字节
/* USER CODE END 2 *//* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{/* USER CODE BEGIN 3 */// 步骤1:发送提示信息(中断方式)HAL_UART_Transmit_IT(&huart1, (uint8_t *)str1, strlen(str1));Wait_Tx_Complete();  // 等待发送完成// 步骤2:从环形缓冲区读取1字节(循环等待,直到有数据)while (0 != UART1GetChar(&c));  // 返回0表示读取成功,退出循环// 步骤3:数据加1后返回(查询方式,简单场景可用)c += 1;HAL_UART_Transmit(&huart1, &c, 1, 1000);    // 发送加1后的字符HAL_UART_Transmit(&huart1, (uint8_t *)"\r\n", 2, 1000);  // 换行
}
/* USER CODE END 3 */
四、实验验证:数据丢失问题解决

烧录程序后,用串口工具一次性发送 “12345”(模拟快速连续发送),结果如下:
请添加图片描述

可以看到,单片机正确接收了所有字符,并返回 “23456”—— 证明环形缓冲区成功暂存了所有数据,即使主程序在处理发送,也不会丢失接收的数据。

五、核心流程梳理(为什么能解决丢失?)

整个改进方案的闭环流程如下,关键是 “接收” 和 “处理” 的解耦:

  1. 启动阶段StartUART1Recv初始化缓冲区→使能接收中断;
  2. 接收阶段:电脑发数据→RXNE 中断触发→HAL_UART_RxCpltCallback将数据存入缓冲区→重新使能中断(准备下一次接收);
  3. 处理阶段:主程序通过UART1GetChar从缓冲区读数据→加 1 后返回→即使主程序耗时,缓冲区也会暂存新数据,不会被覆盖。
结尾

“中断 + 环形缓冲区” 是 UART 通信中解决数据丢失的经典方案,它兼顾了中断的高效性和缓冲区的可靠性,适合中低速、数据量不大的场景。下一篇笔记,我们将学习更高级的 “DMA 方式”—— 让 DMA 硬件替 CPU 完成 “数据搬运”,彻底解放 CPU,适合高速、大数据量的通信场景。
Hello_Embed 继续带你探索 UART 通信的高效实现方式,敬请期待~

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

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

相关文章

使用Docker搭建MaxKB智能体平台

1、系统要求 详见&#xff1a; https://maxkb.cn/docs/v2/quick_start https://maxkb.cn/docs/v2/installation/offline_installtion https://maxkb.cn/docs/v2/installation/online_installtion 2、安装Docker 合集&#xff1a;Docker安装与使用 3、安装MaxKB 详见&#xf…

宠物电商痛点破解:智能客服的关键作用

在宠物电商蓬勃发展的当下&#xff0c;行业面临着诸多痛点。从客户咨询的高频率到订单处理的复杂性&#xff0c;每一个环节都可能成为制约发展的瓶颈。而智能客服的出现&#xff0c;为这些痛点提供了有效的解决方案&#xff0c;成为宠物电商行业不可或缺的助力。一、宠物电商的…

基于GraphRAG+Ollama验证知识图谱和检索增强融合

之前介绍了知识图谱与检索增强的融合探索GraphRAG。 https://blog.csdn.net/liliang199/article/details/151189579 这里尝试在CPU环境&#xff0c;基于GraphRAGOllama&#xff0c;验证GraphRAG构建知识图谱和检索增强查询过程。 1 环境安装 1.1 GraphRAG安装 在本地cpu环境…

36页可编辑PPT | 某制造集团灯塔工厂解决方案

制造业企业订单种类多&#xff0c;传统产线换型慢&#xff0c;库存高&#xff0c;财务压力大。工人年龄大&#xff0c;招工难&#xff0c;工资涨&#xff0c;效率低。海外对手用低价和柔性产线抢单&#xff0c;国内同行用数字化缩短交期。企业想扩产&#xff0c;又怕投资重、回…

Redis 非缓存核心场景及实例说明

Redis 非缓存核心场景及实例说明 一、分布式锁 分布式锁用于解决分布式系统中多节点竞争同一资源的问题&#xff0c;确保操作原子性。Redis 实现分布式锁的核心思路是利用键的唯一性和原子命令&#xff0c;通常基于 Redisson 框架简化实现&#xff08;底层依赖 Redis 命令&…

【技术教程】如何将ONLYOFFICE文档集成到使用Spring Boot框架编写的Java Web应用程序中

在现代协作办公环境中&#xff0c;将功能强大的文档编辑器无缝集成到自有业务系统中&#xff0c;已成为提升工作效率和用户体验的关键需求。ONLYOFFICE 文档服务器提供了一套成熟的在线文档编辑解决方案&#xff0c;而 Java Spring Boot 则是构建高效、模块化 Web 应用的热门框…

openharmony之AV_CodeC音视频编解码模块详解(二)

1. 音频解码器函数调用流程 1.1 音频解码器架构概览 decoder:解码器 encoder:编码器 前面文章介绍了关于openHarmony的AV_CodeC模块,这篇文章将详细讲解编解码时函数的调用流程 音频解码器采用插件化架构,核心实现位于: services/engine/codec/audio/decoder/audio_ffmpeg…

PDF24 Creator:免费的多功能PDF工具

在处理PDF文件时&#xff0c;一个功能强大且免费的PDF工具是许多用户的首选。PDF24 Creator作为一款免费的PDF工具&#xff0c;提供了丰富的功能&#xff0c;帮助用户创建、编辑和转换PDF文件&#xff0c;满足从初学者到专业用户的各种需求。它不仅支持PDF与Word、Excel等15种以…

VBA 中使用 ADODB 操作 SQLite 插入中文乱码问题

问题 使用 VBA 的 ADODB 对象的 command 对象、parameter 对象&#xff0c;插入的中文数据为乱码 驱动下载、安装、引用 驱动网址(下载路径) 使用的 ODBC 驱动&#xff08;需要梯子才能下载&#xff0c;感谢大佬开源&#xff09; http://www.ch-werner.de/sqliteodbc/ 版本…

执行select * from a where rownum<1;,数据库子进程崩溃,业务中断。

文章目录环境症状触发条件解决方案环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5.2 症状 执行select * from a where rownum<1;&#xff0c;数据库子进程崩溃&#xff0c;业务中断。 触发条件 select 和 where条件带有rownum…

python库 Py2app 的详细使用(将 Python 脚本变为 MacOS 独立软件包)

更多内容请见: python3案例和总结-专栏介绍和目录 文章目录 一、Py2app 概述 1.1 Py2app 介绍 1.2 安装 1.3 替代工具推荐 二、基础使用 2.1 最简单的 setup.py 文件 2.2 完整示例 2.3 配置选项详解 2.4 完整项目案例 2.5 打包为单文件应用(可选) 三、高级配置 3.1 处理特定…

NTP配置为客户端广播监听模式

前言 项目需求&#xff1a; 使能ntp为客户端模式&#xff0c;能监服务端广播模式发出的ntp报文&#xff0c;计算出服务端的时间与客户端的时间偏差并上报。 开发状况&#xff1a; 交叉编译ntp源码&#xff0c;将修改后的ntpd进程部署到设备上作为客户端完成项目需求 如何操作&a…

Claude-Flow 使用指南

Claude-Flow 不仅仅是一个工具&#xff0c;更是一个强大的AI驱动开发编排平台。本问初步带您深入了解 Claude-Flow v2.0.0 Alpha 的强大功能&#xff0c;助您在AI开发领域如虎添翼。1. 简介&#xff1a;什么是 Claude-Flow&#xff1f; Claude-Flow v2 Alpha 是一个企业级的AI编…

系统梳理 Test-Time Compute 的主要实现路径

编者按&#xff1a; AI 真的在“思考”吗&#xff1f;当模型面对数学推理、代码生成或复杂决策时&#xff0c;它是如何一步步推演出答案的&#xff1f;如果你曾困惑于大模型在关键任务中表现不稳定、缺乏可解释性&#xff0c;甚至生成结果难以验证&#xff0c;那么你并不孤单。…

vue 经常写的echarts图表模块结构抽取

vue 经常写的echarts图表模块结构抽取将项目中经常写的结构抽取一下, 方便以后用 表头包含标题和右侧操作部分下面为图表 <div class"chartBox"><div class"chartheadbox"><div class"chartheadleft">这是图表标题</div>…

主流的开源协议(MIT,Apache,GPL v2/v3)

文章目录1. MIT 协议 (MIT License)2. Apache 2.0 协议 (Apache License 2.0)3. GPL v2 协议 (GNU General Public License v2)“开源协议选择指南”的流程图 flowchart TDA[开始选择开源协议] --> B{是否要求修改后必须开源?<br>(是否具有 传染性?)};B -- 是&…

CameraService笔记

cameraservicecamera 结构图1. 启动CameraServer1.1 注册media.camera服务1.2 构造CameraService1.3 CameraService::onFirstRef1.4 CameraService::enumerateProviders&#xff1a;前置准备知识1.4 CameraService::enumerateProviders&#xff1a;Provider和Device初始化1.4.1…

MacOS 15.6 编译SDL3 Android平台多架构so库

成功编译输出: 编译: Android平台多架构编译脚本: sdl3_android_build.sh #!/bin/bash# 设置变量 macos 其他系统需要更改路径 SDL_SOURCE_DIR=$(pwd)/SDL BUILD_DIR=${SDL_SOURCE_DIR}/../sdl3_build_android NDK_PATH=$HOME/Library/Android/Sdk/Ndk/25.2.9519653 CMAKE…

Real-IAD D³: A Real-World 2D/Pseudo-3D/3D Dataset for Industrial Anomaly

Real-IAD D: A Real-World 2D/Pseudo-3D/3D Dataset for Industrial Anomaly Detection Paper Github 摘要 随着工业异常检测&#xff08;Industrial Anomaly Detection, IAD&#xff09;复杂程度的不断提升&#xff0c;多模态检测方法已成为机器视觉领域的研究焦点。然而&a…

IT需求提示未读信息查询:深度技术解析与性能优化指南【类似:钉钉已读 功能】

IT需求提示未读信息查询&#xff1a;深度技术解析与性能优化指南【类似&#xff1a;钉钉已读 功能】 DROP TABLE IF EXISTS rs_kpi_it_need_tip; CREATE TABLE IF NOT EXISTS rs_kpi_it_need_tip (id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID&#xff…