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的幂。

  • 单片机
    +关注

    关注

    5847

    文章

    39931

    浏览量

    611107
  • 缓冲区
    +关注

    关注

    0

    文章

    28

    浏览量

    8960
  • 程序
    +关注

    关注

    108

    文章

    2965

    浏览量

    78299
  • uart
    +关注

    关注

    20

    文章

    977

    浏览量

    98789
  • 串口通信
    +关注

    关注

    31

    文章

    1389

    浏览量

    54164
收藏 人收藏

    评论

    相关推荐

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

    实现整个环形缓冲区的数据读写。看图,队列头就是指向已经存储的数据,并且这个数据是待处理的。下一个CPU处理的数据
    发表于 06-08 14:03

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

    实现整个环形缓冲区的数据读写。看图,队列头就是指向已经存储的数据,并且这个数据是待处理的。下一个
    发表于 08-17 13:11

    STM32串口环形缓冲区实现

    实现整个环形缓冲区的数据读写。看图,队列头就是指向已经存储的数据,并且这个数据是待处理
    发表于 10-16 11:40

    环形缓冲区的设计分享!

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

    环形缓冲区简介

    环形缓冲区环形缓冲区简介  在单片机中串口通信是我们使用最频繁的,使用串口通信就会用到串口的数据接收与发送,环形缓冲区方式接收数据可以更好的保证数据丢帧率第。  在通信
    发表于 08-17 06:56

    怎么实现串口环形缓冲区

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

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

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

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

    环形缓冲区读完就丢弃了,而且是不能读取任意的位置,现在想到的方法就是: 搞一个数组当缓冲区,不断增加数据,记住缓冲区头和尾部对应的序号,满了就全部往前移动,但这种方法在缓冲区很大, 满了就得全部移动,这样的效率会不会太低?是否有其他思路实现
    发表于 04-17 14:39

    环形缓冲区读写操作的分析与实现

    环形缓冲区是嵌入式系统中一种重要的常用数据结构。在多任务环境下实现时,如果有多个读写任务,一般需要用信号量来保护多个任务共享的环形缓冲区。但是如果只存在1 个读
    发表于 04-15 11:35 40次下载

    STM32串口数据接收 --环形缓冲区

    环形缓冲区环形缓冲区简介  在单片机中串口通信是我们使用最频繁的,使用串口通信就会用到串口的数据接收与发送,环形缓冲区方式接收数据可以更好的保证数据丢帧率第。  在通信
    发表于 12-28 19:24 27次下载
    STM32串口数据接收 --<b>环形</b><b>缓冲区</b>

    环形缓冲区实现原理

    环形缓冲区作为数据结构来存放通信中发送和接收的数据。环形缓冲区是一个先进先出的循环缓冲区,可以向通信程序提供对缓冲区的互斥访问。
    的头像 发表于 03-22 10:03 6564次阅读
    <b>环形</b><b>缓冲区</b>的<b>实现</b>原理

    缓冲区是啥意思 STM32串口数据接收之环形缓冲区

    缓冲区顾名思义是缓冲数据用的。实现缓冲区最简单的办法时,定义多个数组,接收一包数据到数组A,就把接收数据的地址换成数组B,每个数据有个标记字节用于表示这个数组是否收到数据,收到数据是否处理
    的头像 发表于 07-22 15:33 8051次阅读

    基于C语言实现环形缓冲区/循环队列

    实现环形缓冲区
    的头像 发表于 04-11 10:39 943次阅读
    基于C语言<b>实现</b><b>环形</b><b>缓冲区</b>/循环队列

    下载硬声App