Queue.c
/**
******************************************************************************
* @文件名 Queue.c
* @作者 Jing
* @版本 V01
* @日期 2023-08-09
* @作用
******************************************************************************
* 修改记录:
* 日期 修改者 修改内容
* 2023-08-09 Jing 初版
******************************************************************************
* @注意
*
* Copyright (C) 2023-2025
* All rights reserved Jing
*
******************************************************************************/
#include "queue.h"
#include <string.h>
#include "usart.h"
#include "gpio.h"
DataPacket receiveQueue[MAX_QUEUE_SIZE]; // 接收队列
int front = 0; // 队头指针
int rear = 0; // 队尾指针
/*
**********************************************************************************
* 函数名: Enqueue
* 功 能: 数据进入队列
* 形 参: data,len
* 返回值: 无
**********************************************************************************
*/
void Enqueue(uint8_t *data, uint8_t len)
{
if (IsQueueFull())
{
// 队列已满,丢弃最旧的数据
front = (front + 1) % MAX_QUEUE_SIZE;
}
memcpy(receiveQueue[rear].data, data, len); //获取数据内容
receiveQueue[rear].len = len; //获取数据长度
rear = (rear + 1) % MAX_QUEUE_SIZE; //更新队尾指针
//HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); //测试使用
}
/*
**********************************************************************************
* 函数名: Dequeue
* 功 能: 队列提取数据
* 形 参: data
* 返回值: 无
**********************************************************************************
*/
int Dequeue(uint8_t *data)
{
if (IsQueueEmpty())
{
// 队列为空,没有数据可出队
return 0;
}
memcpy(data, receiveQueue[front].data, receiveQueue[front].len); //取出数据内容
int len = receiveQueue[front].len; //获取数据长度
front = (front + 1) % MAX_QUEUE_SIZE; //更新队首指针
return len; //返回数据长度值
}
/*
**********************************************************************************
* 函数名: IsQueueEmpty
* 功 能: 判断队列是否为空
* 形 参: 无
* 返回值: 0、1
**********************************************************************************
*/
int IsQueueEmpty(void)
{
return (front == rear);
}
/*
**********************************************************************************
* 函数名: IsQueueFull
* 功 能: 判断队列是否为满
* 形 参: 无
* 返回值: 0、1
**********************************************************************************
*/
int IsQueueFull(void)
{
return ((rear + 1) % MAX_QUEUE_SIZE == front);
}
/*
**********************************************************************************
* 函数名: ClearQueue
* 功 能: 清空队列、初始化队列
* 形 参: 无
* 返回值: 无
**********************************************************************************
*/
void ClearQueue(void)
{
front = 0;
rear = 0;
}
Queue.h
/**
******************************************************************************
* @文件名 Queue.h
* @作者 Jing
* @版本 V01
* @日期 2023-08-09
* @作用
******************************************************************************
* 修改记录:
* 日期 修改者 修改内容
* 2023-08-09 Jing 初版
******************************************************************************
* @注意
*
* Copyright (C) 2023-2025
* All rights reserved Jing
*
******************************************************************************/
#ifndef QUEUE_H
#define QUEUE_H
#include <stdint.h>
#define MAX_QUEUE_SIZE 256 // 接收队列最大容量
#define BUFFER_SIZE 100 // 数据包最大长度
typedef struct {
uint8_t data[BUFFER_SIZE];
uint8_t len;
} DataPacket;
void Enqueue(uint8_t *data, uint8_t len);
int Dequeue(uint8_t *data);
int IsQueueEmpty(void);
int IsQueueFull(void);
void ClearQueue(void);
#endif
主函数:
uint8_t receive_data[BUFFER_SIZE];
int main(void)
{
MX_GPIO_Init();
MX_DMA_Init();
MX_UART5_Init();
MX_USART3_UART_Init();
ClearQueue();
while (1)
{
// 从接收队列中取出数据并进行处理
int len = Dequeue(receive_data);
if (len > 0)
{
HAL_UART_Transmit_DMA(&huart3,receive_data,len);
}
}
}
简要说明:
在循环队列的实现中,每个位置上并不链接一个数组,而是一个指向DataPacket
结构体的指针。每个指针指向一个动态分配的DataPacket
结构体,该结构体包含数据内容和长度。
具体来说,入队函数会进行以下操作:
- 动态分配内存以创建一个新的
DataPacket
结构体。 - 将数据复制到新创建的
DataPacket
结构体中,并将数据长度保存在结构体的len
字段中。 - 将指向新创建的
DataPacket
结构体的指针存储到队列中(即存储到receiveQueue
数组中)。 - 更新队列的尾部指针(rear)。
出队函数会进行以下操作:
- 获取队列首部位置(front)上的
DataPacket
结构体指针。 - 从
DataPacket
结构体中获取数据内容和长度。 - 释放
DataPacket
结构体所占用的内存。 - 更新队列的首部指针(front)。
总结起来,在循环队列的实现中,每个位置上并不直接链接一个数组,而是存储一个指向DataPacket
结构体的指针。通过动态分配内存,可以为每个数据包创建独立的结构体,并在结构体中保存数据内容和长度。同时,通过更新队列的尾部指针和首部指针来实现入队和出队操作。
压力测试:
定时30ms发送12字节数据,半小时测试未出现丢帧情况(缺少1帧因为关闭串口时,数据帧已发送但未来得及接收)
2.0版本
/**
******************************************************************************
* @文件名 Queue.c
* @作者 Jing
* @版本 V01
* @日期 2023-08-11
* @作用
******************************************************************************
* 修改记录:
* 日期 修改者 修改内容
* 2023-08-11 Jing 2.0版
******************************************************************************
* @注意
*
* Copyright (C) 2023-2025
* All rights reserved Jing
*
******************************************************************************/
#include "queue.h"
#include <string.h>
#include "usart.h"
#include "gpio.h"
DataPacket receiveQueues[NUM_QUEUES][MAX_QUEUE_SIZE]; // 多个队列的数据存储数组
int front[NUM_QUEUES] = {0}; // 多个队列的队头指针
int rear[NUM_QUEUES] = {0}; // 多个队列的队尾指针
void Enqueue(uint8_t *data, uint8_t len, QueueFunction function)
{
int queueIndex = (int)function;
if (IsQueueFull(function))
{
// 队列已满,丢弃最旧的数据
front[queueIndex] = (front[queueIndex] + 1) % MAX_QUEUE_SIZE;
}
memcpy(receiveQueues[queueIndex][rear[queueIndex]].data, data, len); //获取数据内容
receiveQueues[queueIndex][rear[queueIndex]].len = len; //获取数据长度
rear[queueIndex] = (rear[queueIndex] + 1) % MAX_QUEUE_SIZE; //更新队尾指针
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); //测试使用
}
int Dequeue(uint8_t *data, QueueFunction function)
{
int queueIndex = (int)function;
if (IsQueueEmpty(function))
{
// 队列为空,没有数据可出队
return 0;
}
memcpy(data, receiveQueues[queueIndex][front[queueIndex]].data, receiveQueues[queueIndex][front[queueIndex]].len); //取出数据内容
int len = receiveQueues[queueIndex][front[queueIndex]].len; //获取数据长度
front[queueIndex] = (front[queueIndex] + 1) % MAX_QUEUE_SIZE; //更新队首指针
return len; //返回数据长度值
}
int IsQueueEmpty(QueueFunction function)
{
int queueIndex = (int)function;
return (front[queueIndex] == rear[queueIndex]);
}
int IsQueueFull(QueueFunction function)
{
int queueIndex = (int)function;
return ((rear[queueIndex] + 1) % MAX_QUEUE_SIZE == front[queueIndex]);
}
void ClearQueue(QueueFunction function)
{
int queueIndex = (int)function;
front[queueIndex] = 0;
rear[queueIndex] = 0;
}
void QueueInit(void)
{
int i;
for (i = 0; i < NUM_QUEUES; i++)
{
ClearQueue((QueueFunction)i);
}
}
/**
******************************************************************************
* @文件名 Queue.h
* @作者 Jing
* @版本 V01
* @日期 2023-08-11
* @作用
******************************************************************************
* 修改记录:
* 日期 修改者 修改内容
* 2023-08-11 Jing 2.0版
******************************************************************************
* @注意
*
* Copyright (C) 2023-2025
* All rights reserved Jing
*
******************************************************************************/
#ifndef QUEUE_H
#define QUEUE_H
#include <stdint.h>
#define MAX_QUEUE_SIZE 256 // 单个队列最大容量
#define BUFFER_SIZE 50 // 数据包最大长度
#define NUM_QUEUES 2 // 队列数量
typedef struct {
uint8_t data[BUFFER_SIZE];
uint8_t len;
} DataPacket;
typedef enum {
UART5_DMA_RECEIVE,
UART5_DMA_TRANSMIT
} QueueFunction;
void Enqueue(uint8_t *data, uint8_t len, QueueFunction function);
int Dequeue(uint8_t *data, QueueFunction function);
int IsQueueEmpty(QueueFunction function);
int IsQueueFull(QueueFunction function);
void ClearQueue(QueueFunction function);
void QueueInit(void);
#endif