基于stm32串口环形缓冲队列处理机制—入门级(单字节)

2018-06-10 12:31:52来源: eefocus 关键字:stm32  串口环形缓冲队列  处理机制

1.1 实验简介

  最简单的串口数据处理机制是数据接收并原样回发的机制是:成功接收到一个数,触发进入中断,在中断函数中将数据读取出来,然后立即。这一种数据处理机制是“非缓冲中断方式”,虽然这种数据处理方式不消耗时间,但是这种数据处理方式严重的缺点是:数据无缓冲区,如果先前接收的的数据如果尚未发送完成(处理完成),然后串口又接收到新的数据,新接收的数据就会把尚未处理的数据覆盖,从而导致“数据丢包”。

  对于“数据丢包”,最简单的办法就是使用一个数组来接收数据:每接收一个数据,数组下标偏移。虽然这样的做法能起到一定的“缓冲效果”,但是数组的空间得不到很好的利用,已处理的数据仍然会占据原有的数据空间,直到该数组“满载”(数组的每一个元素都保存了有效的数据),将整个数组的数据处理完成后,重新清零数组,才能开启新一轮的数据接收。

  那么,有什么好的数据接收处理机制既能起到“缓冲”效果,又能有效地利用“数组空间”?答案是:有的,那就是“环形缓冲区”。

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

 

  当然,环形缓冲区的“头指针”和“尾指针”可以用“头变量”和“尾变量”来代替,因为切换数组的元素空间,除了可以用“指针偏移法”之外,还可以用“素组下标偏移法”。当串口接收到新的数组,则将数组保存到环形缓冲区中,同时将“尾变量”加一,以保存下一个数据;应用程序在读取数据时,“头变量”加一,以读取下一个数据。

“环形缓冲区”数据接收处理机制的好处在于:利用了队列的特点,一头进,一头出,互不影响,在数据进去(往里存)的时候,另一边也可以把数据读出来,而读出来的数据,留下的空位,又可以增加多的存储空间,从而避免一边接收数据且一边处理数据会在数据量密集的时候而导致的丢掉数据或者数据产生冲突的问题。

  如果仅有一个线程读取环形缓冲区的数据,只有一个串口往环形缓冲区写入数据,则不需要添加互斥保护机制就可以保证数据的正确性。

  需要注意的是,如果串口每接收x个字节的数据才处理一次,则环形缓冲区的缓冲数组的大小必须是x的N倍,具体N为多少,需要结合具体的数据接收速率以及处理速率,适当调节。这就好比喻,水壶永远大于水杯,这样子水壶才能存放很多杯水。

如果觉得前文隐晦难懂,那么下面我们来一起讨论一下环形队列的具体状态以及实现。下文构建的环形队列采用的是“头变量”“尾变量”来控制队列的存储和读取。

首先,我们会构造一个结构体,并定义一个结构变量。

#define MAX_SIZE  12               //缓冲区大小

 

typedef struct 

{

  unsigned char head;        //缓冲区头部位置

  unsigned char tail;         //缓冲区尾部位置

  unsigned char ringBuf[MAX_SIZE]; //缓冲区数组

} ringBuffer_t;

 

ringBuffer_t buffer;                 //定义一个结构体

定义一个结构头体则表示新的消息队列已经创建完成。新建创建的队列,头指针head和尾指针tail都是指向数组的元素0。如下图所示,此时的消息队列是“空队列”。

 

当如果有11个数据abcdefghijk存入缓冲队列,则如下图所示:

 

当如果l加入队列,则缓冲队列处于满载状态,如下图所示:如果此时,接收到新的数据并需要保存,则tail需要归零,将接收到的数据存到数组的第一个元素空间,如果尚未读取缓冲数组的一个元素空间的数据,则此数据会被新接收的数据覆盖。同时head需要增加1,修改头节点偏移位置丢弃早期数据。

 

读取缓冲队列中的11个数据后,状态如下:

 

当消息队列中的所有数据都读取出来后,此时环形队列是空的,状态如下图所示。从图可以总结得知,如果tail和head相等,则表示缓冲队列是空的。

 

 

1.3 软件设计

1.3.1 ringBuffer.c

1. 构造环形缓冲区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

/**********************************************************************************************
描述   :      环形缓冲读写
作者   :      Jahol Fan
版本   :      V1.0
修改   :      
完成日期: 
Notice    :本程序只供学习使用,未经作者许可,不得用于其它任何用途。版权所有,盗版必究
***********************************************************************************************/
#include "ringbuffer.h"

#define BUFFER_MAX  36               //缓冲区大小

typedef struct 
{
  unsigned char headPosition;        //缓冲区头部位置
  unsigned char tailPositon;         //缓冲区尾部位置
  unsigned char ringBuf[BUFFER_MAX]; //缓冲区数组
} ringBuffer_t;

ringBuffer_t buffer; //定义一个结构体

首先,需要构建一个结构体ringBuffer_t,如果定义一个结构体变量buffer,则意味着创建一个环形缓冲区。

2. 往环形缓冲区存数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14

/**
* @brief 写一个字节到环形缓冲区
* @param data:待写入的数据
* @return none
*/
void RingBuf_Write(unsigned char data)
{
  buffer.ringBuf[buffer.tailPositon]=data;     //从尾部追加
  if(++buffer.tailPositon>=BUFFER_MAX)         //尾节点偏移
    buffer.tailPositon=0;                      //大于数组最大长度 归零 形成环形队列
  if(buffer.tailPositon == buffer.headPosition)//如果尾部节点追到头部节点,则修改头节点偏移位置丢弃早期数据
    if(++buffer.headPosition>=BUFFER_MAX)
      buffer.headPosition=0;
}

8行:将数据存放到tailPosition所指向的元素空间。

9行:tailPosition变量自增1,并且判断,如果大于最大缓冲,则将tailPosition归零。

11行:如果tailPositon与headPosition相等,则表示,数据存入速度大于数据取出速度,从到导致“追尾”。此时headPosition需要自增1,以丢弃早期数据,这也就是数据“覆盖现象”,这种现象是不允许存在的,解决这种现象的办法是将缓冲队列的空间再开大点。

13行:如果headPosition也大于最大数组,则需要将headPosition清零。

3. 读取环形缓冲区的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

/**
* @brief 读取环形缓冲区的一个字节的数据
* @param *pData:指针,用于保存读取到的数据
* @return 1表示缓冲区是空的,0表示读取数据成功
*/
u8 RingBuf_Read(unsigned char* pData)
{
  if(buffer.headPosition == buffer.tailPositon)    //如果头尾接触表示缓冲区为空
    {
            return 1;   //返回1,环形缓冲区是空的
    }
  else
  {
    *pData=buffer.ringBuf[buffer.headPosition];    //如果缓冲区非空则取头节点值并偏移头节点
    if(++buffer.headPosition>=BUFFER_MAX)
      buffer.headPosition=0;
    return 0;     //返回0,表示读取数据成功
  }
}

8行:首先判断headPosition是否等于tailPositon,如果相等,则表明,此时缓冲区是空的。

10行:缓冲区为空,则直接返回,不执行后面的程序

12行:如果缓冲区不为空,则条件成立并执行

14行:读取headPosition所指向的环形缓冲队列的元素空间的数据。

15行:headPosition自增1以读取下一个数据。如果headPosition大于最

[1] [2]

关键字:stm32  串口环形缓冲队列  处理机制

编辑:什么鱼 引用地址:http://www.eeworld.com.cn/mcu/article_2018061039733.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:基于STM32的虚拟多线程
下一篇:最后一页

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

网友正在学习IC视频

推荐阅读
全部
stm32
串口环形缓冲队列
处理机制

小广播

独家专题更多

东芝在线展会——芯科技智社会创未来
东芝在线展会——芯科技智社会创未来
2017东芝PCIM在线展会
2017东芝PCIM在线展会
TI车载信息娱乐系统的音视频解决方案
TI车载信息娱乐系统的音视频解决方案
汇总了TI汽车信息娱乐系统方案、优质音频解决方案、汽车娱乐系统和仪表盘参考设计相关的文档、视频等资源

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2018 EEWORLD.com.cn, Inc. All rights reserved