0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

环形缓冲区的实现思路

CHANBAEK 来源:固件工人 作者:固件工人 2023-01-17 15:07 次阅读

1.1 环形缓冲区的实现思路

单片机程序开发一般都会用到UART串口通信,通过通信来实现上位机和单片机程序的数据交互。通信中为了实现正常的收发,一般都会有对应的发送和接收缓存来暂存通信数据。这里使用环形缓冲区的方式来设计数据收发的缓存,即缓冲区溢出后,从缓冲区数组的起始索引处重新进行数据的存储,这样可以比较高效地使用缓冲区。

核心思路摘抄如下。规定以下所有方案,在缓冲区满时不可再写入数据,缓冲区空时不能读数据。

常规数组环形缓冲区思路:

设缓冲区大小为N,队头out,队尾in,out、in均是下标表示。

  • 初始时,in = out = 0
  • 队头队尾的更新用取模操作,out = (out + 1) % N,in = (in + 1) % N
  • out == in表示缓冲区空,(in + 1) % N == out表示缓冲区满
  • 入队que[in] = value; in = (in + 1) % N;
  • 出队ret = que[out]; out = (out + 1) % N;
  • 数据长度 len = (in - out + N) % N

改进版数组环形缓冲区思路:

同样假设缓冲区大小为N,队头out,队尾in,out、in为数组下标,但数据类型为unsigned int。

  • 初始时,in = out = 0
  • 上调缓冲区大小N为2的幂,假设为M
  • 队头队尾更新不再取模,直接++out,++in
  • out == in表示缓冲区空,(in - out) == M表示缓冲区满
  • 入队que[in & (M - 1)] = value; ++in;
  • 出队ret = que[out & (M - 1)] ; ++out;
  • in - out表示数据长度

1.2 环形缓冲区的代码实现

本文对应的工程代码链接如下。该工程基于eclipse IDE开发,编译器使用arm-none-eabi-gcc,使用的硬件STM32F429I-DISCO开发板。

https://download.csdn.net/download/goodrenze/85163032

根据以上的环形缓冲区设计思路,先定义缓存对应的结构体类型如下。

typedef struct _UartBuf_t
{
#if UART_RECORD_LOST_NUM
    uint32_t TxLostNum;
#endif
    uint8_t* TxBuf;
#if UART_BUF_SIZE_IS_2POW
    uint16_t TxIn;
    uint16_t TxOut;
    uint16_t TxSize;
#else
    int16_t TxIn;
    int16_t TxOut;
    int16_t TxSize;
#endif


#if UART_RECORD_LOST_NUM
    uint32_t RxLostNum;
#endif
    uint8_t* RxBuf;
#if UART_BUF_SIZE_IS_2POW
    uint16_t RxIn;
    uint16_t RxOut;
    uint16_t RxSize;
#else
    int16_t RxIn;
    int16_t RxOut;
    int16_t RxSize;
#endif
}UartBuf_t;

以上结构体中,UART_RECORD_LOST_NUM宏定义用于设置是否记录丢失的数据个数,UART_BUF_SIZE_IS_2POW宏定义用于设置收发缓存的长度是否是2的幂,如果缓存长度是2的幂,则缓存索引和长度使用无符号数,否则使用有符号数。TxBuf和RxBuf指针用于指向对应的发送和接收的缓存数组。

本例程的UART串口发送和接收都是用串口中断来实现的,串口中断处理函数的代码实现如下,只需要在对应的串口中断入口函数中调用该函数进行串口数据的收发处理即可。

// 以下环形缓冲区的设计思路参考以下链接:
// https://www.cnblogs.com/zengzy/p/5139582.html
void UartIrqService(USART_TypeDef* UartX, UartBuf_t* Buf)
{
  uint32_t SR = UartX->SR;
  uint32_t CR1 = UartX->CR1;
  uint32_t CR3 = UartX->CR3;


  UNUSED(CR3);


  while(SR & USART_SR_RXNE)
  {
#if UART_RECORD_LOST_NUM
    if(SR & USART_SR_ORE)
    {
      Buf->RxLostNum++;
    }
#endif


#if UART_BUF_SIZE_IS_2POW
    if ((Buf->RxIn - Buf->RxOut) != Buf->RxSize)
    {
      Buf->RxBuf[Buf->RxIn & (Buf->RxSize - 1)] = (uint8_t)(UartX->DR & (uint8_t)0x00FF);
      Buf->RxIn++;
    }
#else
    if (((Buf->RxIn + 1) % Buf->RxSize) != Buf->RxOut)
    {
      Buf->RxBuf[Buf->RxIn++] = (uint8_t)(UartX->DR & (uint8_t)0x00FF);
      Buf->RxIn %= Buf->RxSize;
    }
#endif
    else
    {
      Buf->RxBuf[Buf->RxIn] = (uint8_t)(UartX->DR & (uint8_t)0x00FF);
#if UART_RECORD_LOST_NUM
      Buf->RxLostNum++;
#endif
    }


    SR = UartX->SR;
  }


  if((SR & USART_SR_TXE) && (CR1 & USART_CR1_TXEIE))
  {
    if(Buf->TxIn != Buf->TxOut)
    {
#if UART_BUF_SIZE_IS_2POW
      UartX->DR = (uint8_t)(Buf->TxBuf[Buf->TxOut & (Buf->TxSize - 1)] & (uint8_t)0x00FF);
      Buf->TxOut++;
#else
      UartX->DR = (uint8_t)(Buf->TxBuf[Buf->TxOut++] & (uint8_t)0x00FF);
      Buf->TxOut %= Buf->TxSize;
#endif
    }
    else
    {
      CLEAR_BIT(UartX->CR1, USART_CR1_TXEIE);
    }
  }
}

以上代码就是上面提到的环形缓冲区思路的具体实现。当缓冲区的数据长度是2的幂的时候,可以省去求余的运算,可以提高代码的执行速度。所以如果要求代码的执行时间尽量短,可以考虑将缓冲区的长度设置成2的幂。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6001

    文章

    43973

    浏览量

    620821
  • 缓冲区
    +关注

    关注

    0

    文章

    31

    浏览量

    9031
  • 程序
    +关注

    关注

    114

    文章

    3631

    浏览量

    79540
  • uart
    +关注

    关注

    22

    文章

    1159

    浏览量

    99955
  • 串口通信
    +关注

    关注

    32

    文章

    1577

    浏览量

    54860
收藏 人收藏

    评论

    相关推荐

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

    完了数据,‘0’地址空间的数据进行释放掉,列队头指向下一个可以处理数据的地址‘1’。从而实现整个环形缓冲区的数据读写。看图,队列头就是指向已经存储的数据,并且这个数据是待处理的。下一个CPU处理的数据
    发表于 06-08 14:03

    MCU进阶之串口环形缓冲区实现

    是列队头的数据,处理完了数据,‘0’地址空间的数据进行释放掉,列队头指向下一个可以处理数据的地址‘1’。从而实现整个环形缓冲区的数据读写。看图,队列头就是指向已经存储的数据,并且这个数据是待处理的。下一个
    发表于 08-17 13:11

    STM32串口环形缓冲区实现

    是列队头的数据,处理完了数据,‘0’地址空间的数据进行释放掉,列队头指向下一个可以处理数据的地址‘1’。从而实现整个环形缓冲区的数据读写。看图,队列头就是指向已经存储的数据,并且这个数据是待处理
    发表于 10-16 11:40

    环形缓冲区的设计分享!

    去访问该缓冲区的最后一个内存位置的的后一位置时回到环形缓冲区的起点。类似一个环一样。这样形容就很好理解了,当然有办法实现了。我在这里采用了2种方式
    发表于 10-28 23:29

    STM32串口环形缓冲区实现方法

    STM32串口环形缓冲区实现
    发表于 12-24 07:30

    环形缓冲区简介

    STM32串口数据接收 --环形缓冲区环形缓冲区简介  在单片机中串口通信是我们使用最频繁的,使用串口通信就会用到串口的数据接收与发送,环形
    发表于 08-17 06:56

    怎么实现串口环形缓冲区

    怎么实现串口环形缓冲区
    发表于 12-06 06:01

    请问怎么实现串口环形缓冲区FIFO?

    请问怎么实现串口环形缓冲区FIFO?
    发表于 12-06 07:23

    STM32环形缓冲区怎么实现

    STM32环形缓冲区怎么实现
    发表于 12-07 07:25

    如何实现STM32串口环形缓冲区

    如何实现STM32串口环形缓冲区
    发表于 12-08 06:13

    请问串口的DMA接收缓冲区是不是环形缓冲区

    大家好!请问串口的DMA接收缓冲区是不是环形缓冲区?通过阅读串口部分的代码,我了解到这样几点:1、串口的DMA接收时循环接收,当缓冲区满了会重新从头开始覆盖掉之前的数据,和
    发表于 08-30 14:27

    rtt的环形缓冲区读完就丢弃了?

    ;rtt的环形缓冲区读完就丢弃了,而且是不能读取任意的位置,现在想到的方法就是: 搞一个数组当缓冲区,不断增加数据,记住缓冲区头和尾部对应的序号,满了就全部往前移动,但这种方法在
    发表于 04-17 14:39

    环形缓冲区实现原理

    在通信程序中,经常使用环形缓冲区作为数据结构来存放通信中发送和接收的数据。环形缓冲区是一个先进先出的循环缓冲区,可以向通信程序提供对
    的头像 发表于 03-22 10:03 7174次阅读
    <b class='flag-5'>环形</b><b class='flag-5'>缓冲区</b>的<b class='flag-5'>实现</b>原理

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

    STM32进阶之串口环形缓冲区实现
    的头像 发表于 09-19 09:20 1637次阅读
    STM32进阶之串口<b class='flag-5'>环形</b><b class='flag-5'>缓冲区</b><b class='flag-5'>实现</b>

    C++环形缓冲区设计与实现

    的存储空间。环形缓冲区的特点是其终点和起点是相连的,形成一个环状结构。这种数据结构在处理流数据和实现数据缓存等场景中具有广泛的应用。 环形缓冲区
    的头像 发表于 11-09 11:21 479次阅读
    C++<b class='flag-5'>环形</b><b class='flag-5'>缓冲区</b>设计与<b class='flag-5'>实现</b>