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教程

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

STM32串口开发之环形缓冲区相关推荐

  1. c语言数组实现环形缓冲区,[嵌入式开发模块]环形缓冲区/循环队列 C语言实现

    忙着毕设,很久没有写文章了,终于答辩完了,得了个校优秀毕业设计.毕设做的是个智能接口模块,用一周时间入门了,MC9S12XEP100的开发,又用一周时间入门了uC/OS-II嵌入式操作系统,在做毕设的 ...

  2. 基于HAL库STM32串口驱动不定长数据接收

    STM32串口驱动不定长数据接收带环形缓冲区 最新框架代码 使用方法 源码 串口接口文件 环形缓冲区接口文件 移植图示 使用涉及4个文件, UART_Port.c UART_Port.h Circul ...

  3. 环形缓冲区-----适合在通信中接收数据(例如uart)

    为什么要用环形缓冲区 当有大量数据的时候,我们不能存储所有的数据,那么计算机处理数据的时候,只能先处理先来的,处理之后就会把数据释放掉,再处理下一个.那么已经处理的数据的内存就会被浪费掉.因为后来的数 ...

  4. STM32进阶之串口环形缓冲区实现

    文章目录 队列的概念 队列的特点 队列的常见两种形式 普通队列 环形队列 从队列到串口缓冲区的实现 定义一个结构体: 初始化 写入环形缓冲区的代码实现: 读取缓冲区的数据的代码实现: 测试效果 补充 ...

  5. STM32串口通信中使用printf发送数据配置方法 开发环境 Keil

    STM32串口通信中使用printf发送数据配置方法(开发环境 Keil RVMDK) 已有 12456 次阅读2011-6-29 23:29 | 在STM32串口通信程序中使用printf发送数据, ...

  6. 串口重定向(STM32 F411RET6开发版)

    实验器材:STM32 F411RET6开发版 任务目标:实现串口重定向. 任务内容:编写简单的控制台(console)程序,利用串口重定向功能实现对Nucleo开发板上用户指示灯LD2的控制. 一.C ...

  7. GD32F303RET6 串口空闲中断+DMA数据发送接收+环形缓冲区方式保存数据

    GD32F303RET6 DMA 通道映射关系 串口 源文件 #include "uart.h" #include "stdio.h" #include &qu ...

  8. FPGA 串口中断_一个严谨的STM32串口DMA发送amp;接收(1.5Mbps波特率)机制

    昨天分享的<嵌入式大杂烩读者福利:第一期>大家有去抽奖吗,没抽的可参与抽奖,碰碰运气.我最喜欢抽奖了,还记得前几个月疫情严重时期连抽中了3包口罩,真刺激,哈哈.之后多多安排抽奖,敬请期待. ...

  9. 一个严谨的STM32串口DMA发送接收(1.5Mbps波特率)机制

    文章目录 1 前言 2 串口有必要使用DMA吗 3 实现方式 4 STM32串口使用DMA 5 串口DMA接收 5.1 基本流程 5.2 相关配置 5.3 接收处理 5.3 .1 接收数据大小 5.3 ...

最新文章

  1. 语义分割网络经典:unet
  2. 感知不强又徒增功耗?为何今年5G手机也这么重视AI
  3. 理解css伪类和伪元素
  4. javaweb入门笔记(4)-request和response
  5. python语言中split-python中的split()函数和os.path.split()函数使用详解
  6. asp.net 为FCKeditor开发代码高亮插件实现代码
  7. mysql存过游标_mysql存储过程 游标 循环使用介绍
  8. 度度熊的午饭时光[2017百度之星资格赛 1004]
  9. Excel造测试大量数据
  10. Mbps和MB/s之间的换算
  11. 刷屏专用超长复制_求超长的刷屏文字
  12. elasticsearch搜索推荐系列(二)之 java实现中文转化为拼音与简称
  13. vue项目设置ip访问
  14. 光滑曲线_计算机图形学十:贝塞尔曲线与贝塞尔曲面
  15. PostgreSQL行级安全策略RLS和数据加密
  16. GWAs——全基因组关联分析流程
  17. 软件架构-Nosql之redis
  18. 欢迎Slashdot!
  19. 【Python基础库】保留重要缓存内容 dill 使用【案例】
  20. 应用、原生应用和混合应用的区别

热门文章

  1. Spring MVC,Spring Boot文件上传
  2. php ora-12154,ORA-12154 和 TNS-03505 监听错误的解决方法
  3. pandas将series所有值转变为字符串类型
  4. snownlp文本分词、情感分析、文本相似度与摘要生成
  5. php图像处理原生函数,php图像处理函数imagecopyresampled的用法
  6. 乐山市计算机学校欺骗,据说这个学校很乱。
  7. java 指令接口架构,JavaSE 基础大纲
  8. pytorch 中的数据类型,tensor的创建
  9. QT+VS中ui不能声明为指针?
  10. appium的三种等待方式 (还没实践过,记录在此)