环形缓冲队列
在单片机相关的开发中,有时候会遇到,读取的速度慢与接收的速度,导致通讯数据丢失或者说不连续的情况、比如单片机的串口通讯。
可以使用环形缓冲区实现,把接收到的数据放入环形缓冲队列,然后需要需要用到数据的时候,从队列中依次读取即可。这样可以在一定程度上,防止数据的丢失。
分析
- 首先环形缓冲区本质是一个定长的数组,只是它的收尾相连了
- 我们定义一个缓冲区的结构体
- 结构体成员包含: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;
}