STM32串口开发之环形缓冲区

2023-05-16

01、简介

在之前的文章《stm32 串口详解》中,我们讲解了串口的基本应用,使用串口中断接收数据,串口中断发送回包(一般可以使用非中断形式发送回包,在数据接收不频繁的应用中。串口接收中断保证串口数据及时响应,使用非中断方式发送回包即可)。

后面的文章《STM32使用DMA接收串口数据》和《STM32使用DMA发送串口数据》讲解了如何使用DMA辅助串口收发数据,使用DMA的好处在于不用CPU即可完成串口收发数据,减轻CPU负担,在串口通信频繁且不想频繁中断的应用中非常有用。

除了上述两种场景,还有一种应用场景:串口接收数据长度位置,频率未知,不要求实时处理的场景。如果采用上述方案,接收一帧数据立即处理,那么在处理的时候来的数据包就“丢失”了。这个时候就需要缓冲队列来解决这个问题。

02、缓冲区

缓冲区看名字就知道,是缓冲数据用的。实现缓冲区最简单的办法时,定义多个数组,接收一包数据到数组A,就把接收数据的地址换成数组B,每个数据有个标记字节用于表示这个数组是否收到数据,收到数据是否处理完成。

上述方案是完全可行的,但有缺点:

①缓冲数据组数一定,且有多变量,代码结构不太清晰。

②接收数据长度可能大于数组大小,也可能小于数组大小。不灵活,需要接收数据很长时容易出错,且内存利用率低。

解决这个问题的好办法是:环形缓冲区。

环形缓冲区就是一个带“头指针”和“尾指针”的数组。“头指针”指向环形缓冲区中可读的数据,“尾指针”指向环形缓冲区中可写的缓冲空间。通过移动“头指针”和“尾指针”就可以实现缓冲区的数据读取和写入。在通常情况下,应用程序读取环形缓冲区的数据仅仅会影响“头指针”,而串口接收数据仅仅会影响“尾指针”。当串口接收到新的数组,则将数组保存到环形缓冲区中,同时将“尾指针”加1,以保存下一个数据;应用程序在读取数据时,“头指针”加1,以读取下一个数据。当“尾指针”超过数组大小,则“尾指针”重新指向数组的首元素,从而形成“环形缓冲区”!,有效数据区域在“头指针”和“尾指针”之间。如下图

图片

如上面说的,环形缓冲区其实就是一个数组,将其“剪开”,然后“拉直”后如下图

图片

环形缓冲区的特性

1、先进新出。

2、当缓冲区被使用完,且又有新的数据需要存储时,丢掉历史最久的数据,保存最新数据。

03、代码实现

环形缓冲区的实现很简单,只需要简单的几个接口即可。

首先需要创建一个环形缓冲区

#define  RINGBUFF_LEN          (500)     //定义最大接收字节数 500
#define  RINGBUFF_OK           1     
#define  RINGBUFF_ERR          0   
typedef struct
{
    uint16_t Head;           
    uint16_t Tail;
    uint16_t Lenght;
    uint8_t  Ring_data[RINGBUFF_LEN];
}RingBuff_t;
RingBuff_t ringBuff;//创建一个ringBuff的缓冲区

当我们发现环形缓冲区被“冲爆”时,也就是缓冲区满了,但是还有待缓冲的数据时,只需要修改RINGBUFF_LEN的宏定义,增大缓冲区间即可。

环形缓冲区的初始化


/**
* @brief  RingBuff_Init
* @param  void
* @return void
* @note   初始化环形缓冲区
*/
void RingBuff_Init(void)
{
  //初始化相关信息
  ringBuff.Head = 0;
  ringBuff.Tail = 0;
  ringBuff.Lenght = 0;
}

主要是将环形缓冲区的头,尾和长度清零,表示没有任何数据存入。

环形缓冲区的写入

/**
* @brief  Write_RingBuff
* @param  uint8_t data
* @return FLASE:环形缓冲区已满,写入失败;TRUE:写入成功
* @note   往环形缓冲区写入uint8_t类型的数据
*/
uint8_t Write_RingBuff(uint8_t data)
{
  if(ringBuff.Lenght >= RINGBUFF_LEN) //判断缓冲区是否已满
  {
    return RINGBUFF_ERR;
  }
  ringBuff.Ring_data[ringBuff.Tail]=data;
  ringBuff.Tail = (ringBuff.Tail+1)%RINGBUFF_LEN;//防止越界非法访问
  ringBuff.Lenght++;
  return RINGBUFF_OK;
}

这个接口是写入一个字节到环形缓冲区。这里注意:大家可以根据自己的实际应用修改为一次缓冲多个字节。并且这个做了缓冲区满时报错且防止非法越界的处理,大家可以自行修改为缓冲区满时覆盖最早的数据。

环形缓冲区的读取

/**
* @brief  Read_RingBuff
* @param  uint8_t *rData,用于保存读取的数据
* @return FLASE:环形缓冲区没有数据,读取失败;TRUE:读取成功
* @note   从环形缓冲区读取一个u8类型的数据
*/
uint8_t Read_RingBuff(uint8_t *rData)
{
  if(ringBuff.Lenght == 0)//判断非空
  {
    return RINGBUFF_ERR;
  }
  *rData = ringBuff.Ring_data[ringBuff.Head];//先进先出FIFO,从缓冲区头出
  ringBuff.Head = (ringBuff.Head+1)%RINGBUFF_LEN;//防止越界非法访问
  ringBuff.Lenght--;
  return RINGBUFF_OK;
}

读取的话也很简单,同样是读取一个字节,大家可以自行修改为读取多个字节。

04、验证

光说不练假把式,下面我们就来验证上面的代码可行性。

串口中断函数中缓冲数据

void USART1_IRQHandler(void)
{
  if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE))
  {
    Write_RingBuff(USART_ReceiveData(USART1));
    USART_ClearFlag(USART1, USART_FLAG_RXNE);
  }
}

在主循环中,读取缓冲区的数据,然后发送出去,因为是简单的demo,添加了延时模拟CPU处理其他任务。


while (1)
  {
    if(Read_RingBuff(&data))            //从环形缓冲区中读取数据
    {
      USART_SendData(USART1, data);
    }
    SysCtlDelay(1*(SystemCoreClock/3000));
  }

验证,间隔100ms发送数据。

图片

结果显示没有出现丢包问题。如果你的应用场景串口通信速率快,数据量大或处理速度慢导致丢包,建议增大RINGBUFF_LEN的宏定义,增大缓冲区间即可。

KeilIAR的工程文件下载地址:

https://github.com/strongercjd/STM32F207VCT6

点击查看本文所在的专辑,STM32F207教程

关注公众号,第一时间收到文章更新

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32串口开发之环形缓冲区 的相关文章

  • GCC - 如何停止链接 malloc?

    我正在努力将我的代码缩减到最小的骨架大小 我使用的是只有 32k 闪存的 STM32F0 需要很大一部分闪存用于数据存储 我的代码已经有大约 20k 闪存大小 其中一些是由于使用了 STM32 HAL 函数 我可以在以后需要时对其进行解释和
  • 优化 ARM Cortex M3 代码

    我有一个 C 函数 它尝试将帧缓冲区复制到 FSMC RAM 这些函数将游戏循环的帧速率降低至 10FPS 我想知道如何分析反汇编的函数 我应该计算每个指令周期吗 我想知道CPU把时间花在哪里 在哪个部分 我确信该算法也是一个问题 因为它的
  • 134-基于stm32单片机矿井瓦斯天然气浓度温湿度检测自动通风系统Proteus仿真+源程序...

    资料编号 134 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 ds1302时钟 DHT11温湿度 电机 蜂鸣器 制作一个基于stm32单片机矿井瓦斯天然气浓度温湿度检测自动通风系统Proteus仿真 2 通过DH
  • 137-基于stm32单片机智能保温杯控制装置Proteus仿真+源程序

    资料编号 137 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DS18B20传感器 电机 制作一个基于stm32单片机智能保温杯控制装置Proteus仿真 2 通过DS18b20传感器检测当前保温杯水的温度 并且
  • STM32F4 通过软复位跳转到引导加载程序,无需 BOOT0 和 BOOT1 引脚

    我问这个问题是因为可以在这里找到类似问题的答案 通过应用程序跳转到 STM32 中的引导加载程序 即从用户闪存在引导模式下使用引导 0 和引导 1 引脚 用户 JF002 JF002回答 当我想跳转到引导加载程序时 我在其中一个备份寄存器中
  • 匹配 STM32F0 和 zlib 中的 CRC32

    我正在研究运行 Linux 的计算机和 STM32F0 之间的通信链路 我想对我的数据包使用某种错误检测 并且由于 STM32F0 有 CRC32 硬件 并且我在 Linux 上有带有 CRC32 的 zlib 所以我认为在我的项目中使用
  • STM32F103

    提示 来源正点原子 参考STM32F103 战舰开发指南V1 3PDF资料 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 提示 这里可以添加本文要记录的大概内容 开发环境硬件普中科技 接线图在g
  • STM32 GPIO工作原理详解

    STM32 GPIO介绍 1 STM32引脚说明 GPIO是通用输入 输出端口的简称 是STM32可控制的引脚 GPIO的引脚与外部硬件设备连接 可实现与外部通讯 控制外部硬件或者采集外部硬件数据的功能 以STM32F103ZET6芯片为例
  • SHT10温湿度传感器——STM32驱动

    实验效果 硬件外观 接线 3 3V供电 IIC通讯 代码获取 查看下方 END
  • 在 Atollic TrueStudio、STM32CubeMX 中导入 C 库

    我目前正在开发 STM32F767ZI Nucleo 板和一个小安全芯片 microchip atecc508a 通过 i2c 连接进行连接 该芯片有一个可用的库加密验证库 https github com MicrochipTech cr
  • 跟着野火学FreeRTOS:第一段(任务定义,切换以及临界段)

    在裸机系统中 系统的主体就是 C P U CPU CP U 按照预先设定的程序逻辑在 m a i n
  • 最终启动顺序错误 - STM32L476 的 Eclipse System Workbench 调试

    我正在尝试调试和运行 STM32L476 的简单汇编代码 我已经设置了 Eclipse Oxygen 在 Eclipse 中安装了最新版本的 System Workbench 插件并安装了 ST Link 驱动程序 IDE 成功构建了程序
  • STM32 暂停调试器时冻结外设

    当到达断点或用户暂停代码执行时 调试器可以停止 Cortex 中代码的执行 但是 当皮质停止在暂停状态下执行代码时 调试器是否会冻结其他外设 例如 DMA UART 和定时器 您只能保留时间 r 取决于外围设备 我在进入主函数时调用以下代码
  • STM32F4XX的12位ADC采集数值超过4096&右对齐模式设置失败

    文章目录 一 前言 二 问题1 数值超过4096 三 问题1的排错过程 四 问题2 右对齐模式设置失败 五 问题2的解决方法 5 1 将ADC ExternalTrigConv设置为0 5 2 使用ADC StructInit 函数 一 前
  • 核心耦合内存在 STM32F4xx 上可执行吗?

    尝试从 STM32F429s CCM 运行代码 但每当我命中 CCM 中的第一条指令时 我总是会遇到硬故障 并且 IBUSERR 标志被设置 该指令有效且一致 STM32F4xx 是否可能不允许从 CCM 执行 数据访问效果良好 alios
  • 通过JTAG恢复STM32 MCU磨掉的标记

    我有一块可能带有 STM32 MCU 的板 我想为该板制作定制固件 因为库存板有很多问题 不幸的是 电路板制造商很友善地磨掉了所有标记 有没有办法通过 jtag 获取设备 系列 ID 并将其交叉引用到型号 我能找到的一切都是关于获取芯片的唯
  • STM32 上的位置无关代码 - 指针

    我已成功在 STM32 上构建并运行位置无关的代码 向量表和 GOT 已修补 一切正常 但我对这样的代码有问题 double myAdd double x return x 0 1 double ptrmyAdd double myAdd
  • 在 Contiki 程序中使用 malloc

    考虑以下 Contiki 程序 include
  • 使用 STM32F0 ADC 单独读取不同的输入

    STM32F072CBU 微控制器 我有多个 ADC 输入 并且希望单独读取它们 STMcubeMX 生成样板代码 假设我希望按顺序读取所有输入 但我无法弄清楚如何纠正这个问题 这篇博文 http blog koepi info 2015
  • 移动数组中的元素

    我需要一点帮助 我想将数组中的元素向上移动一个元素 以便新位置 1 包含位置 1 中的旧值 new 2 包含 old 1 依此类推 旧的最后一个值被丢弃 第一个位置的新值是我每秒给出的新值 我使用大小为 10 的数组 uint32 t TE

随机推荐

  • Liunx下源代码安装&&make&&makefile

    Linux下安装软件的方式分为源代码安装和二进制安装 源代码安装 xff0c 即使用应用程序源代码进行编译安装二进制安装 xff0c 例如red hat发行的 rpm包 debian发行的 deb包 源代码安装 用c语言为例 include
  • Linux下rpm&yum&apt-get

    RPM简介 RPM命名 RedHat Package Manager xff0c 简称则为RPM 属于Red Hat阵营的 xff0c 与其并列的则是debian centos中大部分我们安装都是使用yum install xff0c 而d
  • Java面试题复习整理(多线程)

    文章目录 1 aio nio bio epoll select2 reactor模式介绍Reactor软件工程java代码总结 3 Java中的cas乐观锁4 自旋锁是什么 xff1f 自旋锁 amp amp 自适应自旋锁 amp amp
  • Grpc&&protocol buffer结合提供grpc服务

    Grpc amp amp protocol buffer 关于下载 xff1a 首先下载一个protobuf 对于mac系统就brew install protobuf 就可以了 然后可以 protoc version 看下安装的版本号 x
  • 指针p,*p,&p之间的区别

    假设我们定义一个指针p 那么会经常使用到三个符号 xff1a 1 xff0c p xff1b p是一个指针变量的名字 xff0c 表示此指针变量指向的内存地址 xff0c 如果使用 p来输出的话 xff0c 它将是一个16进制数 2 xff
  • 自己经历的Java面试题(附答案)

    本篇记录一下自己面试的一些中大厂的 1 3年Java开发的面试题以及自己对题目理解的答案 xff08 结合网上查的资料 xff09 部分可能更新的不及时 xff0c 有问题可以评论区讨论 面试题不分先后 1 hashmap rehash 过
  • Redisson相关使用&&分布式锁浅析

    注 xff1a 本篇的redisson版本基于3 13 3 xff1b 本篇的demo将我写的源代码贴了出来 xff0c 每个方法都有清晰的注释 xff0c 分布式锁相关的代码以及验证是我手动验证Redis中key状态来判断的 文章目录 简
  • SpringBoot实战elasticSearchAPI微服务开发

    文章目录 前言启动一个ElasticSearchSpringBoot引入ElasticSearch索引创建 amp amp 更新插入删除 写 操作ElasticSearch的API查询操作源码参考 前言 本文准备记录一下ElasticSea
  • Leetcode链表刷题思路汇总

    链表 链表相关的题 xff0c 最快的入门方法就是做题的时候画图 标注A的next节点是哪里 xff0c 单链表的遍历只能是单向的 xff0c 从头结点到尾结点 例如 xff1a 给你一个链表的head头 xff0c 让你返回最后元素的值
  • Java基础之字符串&equals、==、包装类、常量池

    在java中有三中对字符串的操作方式 注 xff1a 文章只注明思路原理 不注明方法 xff0c 看API就行了 文章就涉及到啥写啥了 xff0c 哈哈 xff0c 瞅着可能乱一点 但是这么写就很舒服 1 String 常量 效率较低 指的
  • 基于激光雷达的人型识别(无人车)

    原文在这 finalobject cn 这个项目是无人车里的一部分 xff0c 完成激光雷达的驱动 xff0c 数据采集然后后期的处理以及人型识别 xff0c 并不涉及车辆硬件的控制 主要分三个大块讲吧 xff0c 硬件驱动 数据聚类 xf
  • ARM Cortex M4 SVC指令作用

    xff08 1 xff09 SVC指令 xff1a 摘自 http infocenter arm com help index jsp topic 61 com arm doc dui0203ic Cacdfeci html 与更早版本的
  • SVC和PendSV

    转载于 xff1a http book 2cto com 201209 4625 html 1 xff0e SVC SVC xff08 Supervisor Call xff09 指令用于产生一个SVC异常 它是用户模式代码中的主进程 xf
  • esp8266烧录Html文件,实现内置网页控制设备!

    代码地址如下 xff1a http www demodashi com demo 14321 html 一 前言 xff1b 这个月也快结束了 xff0c 时间真快 xff0c 我服务器知识自学依然在路途中 xff0c 这几天听到热点网页配
  • Golang创建XML

    package main import 34 encoding xml 34 34 fmt 34 34 io ioutil 34 type Post struct XMLName xml Name 96 xml 34 post 34 96
  • xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    1 解压 tar zxvf wmsdk bundle xxxxxxx tar gz cd wmsdk bundle xxxxxxx 2 make installpkgs 或者installpkgs sh 3 安装gcc arm none e
  • 超声波换能器的几点总结

    超声波换能器是超声波测量的关键件 xff0c 必须保证超声波换能器的质量以及特性稳定 1 超声波换能器的激励信号频率接近谐振频率是触发信号强度最大 2 传感器是被动元器件 xff0c 激励信号进行激励会产生震动 xff0c 接收端接收信号
  • windows7安装ubuntu双系统教程

    转载自 xff1a http www cnblogs com masbay p 10745170 html windows7安装ubuntu双系统教程 一 先搞清楚自己电脑的类型 xff1a 本次安装的Ubuntu的电脑类型是MBR传统bi
  • STM32 串口详解

    目录 01 USART的特点 02 USART简介 2 1 数据传输模型 2 2 帧结构 2 3 波特率 03 STM32的USART 04 代码配置 01 USART的特点 USART是通用异步收发传输器 xff08 UniversalA
  • STM32串口开发之环形缓冲区

    01 简介 在之前的文章 stm32 串口详解 中 xff0c 我们讲解了串口的基本应用 xff0c 使用串口中断接收数据 xff0c 串口中断发送回包 xff08 一般可以使用非中断形式发送回包 xff0c 在数据接收不频繁的应用中 串口