STM32串口开发之环形缓冲区

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

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

  • 26
    点赞
  • 195
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
STM32进阶之串口环形缓冲区实现FIFO。 在STM32中,串口通信是一种常见的通信方式。为了提高串口通信的效率,我们可以使用环形缓冲区来实现FIFO(先进先出)的数据存储与读取。 串口环形缓冲区的实现主要包括一个接收缓冲区和一个发送缓冲区。为了避免数据覆盖和数据丢失,接收缓冲区和发送缓冲区的大小应根据实际需求进行合理设置。 在接收数据时,当有新的数据到达时,将数据存储到接收缓冲区的当前位置,并将接收缓冲区的指针移动到下一个位置。如果接收缓冲区已满,则将指针重新指向缓冲区的起始位置。 在发送数据时,将要发送的数据存储到发送缓冲区的当前位置,并将发送缓冲区的指针移动到下一个位置。如果发送缓冲区已满,则等待缓冲区有空位继续发送数据。 通过使用环形缓冲区实现串口的FIFO机制,我们可以在数据发送过程中实现异步操作,提高了系统的响应速度和数据处理的能力。 需要注意的是,当接收缓冲区或发送缓冲区满时,应该有相应的处理机制,例如中断或轮询方式来检测缓冲区的状态,并对缓冲区进行相应的处理,以免数据丢失或覆盖。 总之,串口环形缓冲区的实现可以提高串口通信的效率,实现FIFO机制。通过合理设置缓冲区的大小和相应的处理机制,我们可以实现稳定可靠的数据传输。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

strongercjd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值