C语言-环形缓冲队列

环形缓冲队列

在单片机相关的开发中,有时候会遇到,读取的速度慢与接收的速度,导致通讯数据丢失或者说不连续的情况、比如单片机的串口通讯。
可以使用环形缓冲区实现,把接收到的数据放入环形缓冲队列,然后需要需要用到数据的时候,从队列中依次读取即可。这样可以在一定程度上,防止数据的丢失。

分析

  • 首先环形缓冲区本质是一个定长的数组,只是它的收尾相连了
  • 我们定义一个缓冲区的结构体
  • 结构体成员包含:r 待读取的索引,w待写入的索引,buffer 缓冲区数据
  • 判断队列不为空(ptr->r != ptr->w),可读取
  • 判断队列已满(w + 1 == r),不可写入

代码

#include <stdio.h>
#include <stdint.h>

#define BUFFER_SIZE 8

typedef struct {
    uint8_t r;
    uint8_t w;
    uint8_t buffer[BUFFER_SIZE];
} Circle_buffer_Type,*bufferPtr;

/*缓冲区初始化*/
void BufferInit(bufferPtr ptr){
    ptr->r = 0;
    ptr->w = 0;
    for(uint8_t i = 0;i<BUFFER_SIZE;i++){
        ptr->buffer[i] = 0;
    }
}

/*判断缓冲区是否空*/
uint8_t Buffer_is_empty(bufferPtr ptr) {
     return ptr->r == ptr->w;
}

/* 判断缓冲区是否已满 */
uint8_t Buffer_is_full(bufferPtr ptr) {
    return (((ptr->w + 1)) % BUFFER_SIZE) == ptr->r;
}

/* 往缓冲区写数据 */
int Buffer_write(bufferPtr ptr,uint8_t dataW){
    if(Buffer_is_full(ptr)) {
        return -1;
    }
    ptr->buffer[ptr->w] = dataW;
    ptr->w = (ptr->w + 1) % BUFFER_SIZE;  // 防止w位置溢出,重定向到起止位置0
    return 0;
}

/* 往缓冲区读数据 */
int Buffer_read(bufferPtr ptr,uint8_t *dataReadPtr) {
    if(Buffer_is_empty(ptr)) 
       return -1;
    else {
      *dataReadPtr =  ptr->buffer[ptr->r];
      ptr->r = (ptr->r + 1) % BUFFER_SIZE;  //  防止r位置溢出,重定向到起止位置0
      return 0;
    }
}

int main(void){
    Circle_buffer_Type circle;
    BufferInit(&circle);
    for(uint8_t i=0;i<=8;i++){
      Buffer_write(&circle, (i + 1) * 10);
    }
    uint8_t dataR;

    while(1){
        int activeChar;
        printf("please input a random char to active read\r\n");
        scanf("%d",&activeChar);
        Buffer_read(&circle,&dataR);
        printf("read content is: %d , current read pointer is : %d\r\n",dataR,circle.r);
    }

    // printf("current W position is %d\r\n",circle.w);
    // printf("current r position is %d\r\n",circle.r);

    // for(uint8_t i=0;i<8;i++){
        // printf("current index-%d content is %d\r\n",i,circle.buffer[i]);
    // }

    return 0;
}

chatGPT的串口通讯案例代码

处理大量数据的串口接收可以采用环形缓冲区(Circular Buffer)的方式,这样可以有效地处理大量数据而不会丢失数据或导致处理器过载。以下是一个简单的代码示例,演示了如何使用环形缓冲区来处理串口接收的大量数据

#include <avr/io.h>
#include <avr/interrupt.h>

// 定义串口参数
#define BAUD_RATE 9600
#define F_CPU 16000000UL
#define UBRR_VALUE ((F_CPU / (BAUD_RATE * 16UL)) - 1)

// 定义环形缓冲区大小
#define BUFFER_SIZE 128

// 环形缓冲区结构体
typedef struct {
    uint8_t buffer[BUFFER_SIZE];
    volatile uint8_t head;
    volatile uint8_t tail;
} CircularBuffer;

// 定义环形缓冲区
CircularBuffer rx_buffer = {{0}, 0, 0};

// 初始化串口
void uart_init() {
    // 设置波特率
    UBRR0H = (uint8_t)(UBRR_VALUE >> 8);
    UBRR0L = (uint8_t)(UBRR_VALUE);
    // 启用接收和发送
    UCSR0B |= (1 << RXEN0) | (1 << TXEN0);
    // 开启接收中断
    UCSR0B |= (1 << RXCIE0);
    // 设置数据格式:8位数据,1位停止位
    UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00);
}

// 串口发送数据
void uart_transmit(uint8_t data) {
    // 等待发送缓冲区为空
    while (!(UCSR0A & (1 << UDRE0)));
    // 将数据加载到发送缓冲区
    UDR0 = data;
}

// 获取环形缓冲区中的数据
uint8_t circular_buffer_get(CircularBuffer *buffer) {
    // 检查缓冲区是否为空
    if (buffer->head == buffer->tail) {
        return 0; // 缓冲区为空,返回0
    }
    // 从缓冲区中读取数据
    uint8_t data = buffer->buffer[buffer->tail];
    buffer->tail = (buffer->tail + 1) % BUFFER_SIZE;
    return data;
}

// 串口接收数据中断服务程序
ISR(USART_RX_vect) {
    // 读取接收缓冲区中的数据
    uint8_t received_data = UDR0;
    // 将数据存储到环形缓冲区中
    uint8_t next_head = (rx_buffer.head + 1) % BUFFER_SIZE;
    // 检查缓冲区是否已满
    if (next_head != rx_buffer.tail) {
        rx_buffer.buffer[rx_buffer.head] = received_data;
        rx_buffer.head = next_head;
    }
    // 如果缓冲区已满,则可以选择丢弃数据或者进行其他处理
}

int main(void) {
    // 初始化串口
    uart_init();
    
    // 发送数据给传感器
    uart_transmit('A'); // 示例数据,根据实际情况修改
    
    // 主循环
    while (1) {
        // 在这里执行主要任务
        
        // 检查环形缓冲区中是否有数据可用
        while (rx_buffer.head != rx_buffer.tail) {
            // 从环形缓冲区中读取数据
            uint8_t received_data = circular_buffer_get(&rx_buffer);
            // 处理接收到的数据
            // 可以将数据存储到另一个缓冲区中,或进行其他处理
        }
    }
    
    return 0;
}

小结:

  • 使用中断处理接收数据:通过使能串口接收中断,可以在接收到数据时立即进行处理,避免数据丢失或延迟响应。

  • 使用环形缓冲区存储数据:通过定义一个环形缓冲区,可以有效地存储大量的接收数据,确保不会因为数据量过大而导致缓冲区溢出或丢失数据。

  • 在主循环中处理数据:在主循环中不断检查环形缓冲区中是否有数据可用,以及时处理接收到的数据,保证数据的及时处理和响应。


模拟串口使用环形缓冲区

没入列2个数据,出列1个数据

出列(数据处理)的速度比入列速度满,才能体现环形缓冲区的意义

#include "stdio.h"

int input[9] = {1,2,3,4,5,6,7,8,9};

int buf[8] = {0};
int head=0;
int tail=0;
// 入列
void inputData(int val){
    printf("val:%d head:%d\r\n",val,head);
    buf[head++] = val;
    if(head == 8) {
        head = 0;
    }
}
// 队列判空
int is_empty(){
    return head == tail;
}
// 出列计数
int get_count = 0;

// 出列
int outData(int *val){
    if(!is_empty()) {
        *val = buf[tail++];
        if(tail == 8) {
            tail = 0;
        }
        return 1;
    }
    return 0;
}

int main(void) {

    for(int i=0;i<9;i++){
        inputData(input[i]);
        if(i%2 == 0) {
            int val;
            if(outData(&val)) {
                printf("output: val:%d count:%d\r\n",val,get_count);
                get_count++;
            }
        }
    }

    while(!is_empty()) {
        int val;
        if(outData(&val))  {
            printf("output: val:%d count:%d\r\n",val,get_count);
            get_count++;
        }
    }
    return 0;
}

在这里插入图片描述


参考文章

【知识分享】数据结构的应用——队列

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值