在嵌入式系统中,任务之间常常需要通过一种结构化方式传递消息或数据,例如传感器采集值上传、串口数据解析、指令调度、事件响应等。在这些场景下,“队列(Queue)”是一种非常经典且高效的数据组织形式。
通用队列模块以结构体数组为核心,结合固定内存与循环索引,实现了一个轻量、稳定、适配性强的队列管理器。该模块适合用于裸机或 RTOS 系统,是构建通信缓冲、消息队列、任务分发机制的基础组件。
核心结构与概念
队列管理器 queue_t
队列通过 queue_t 结构体进行管理,内部维护以下成员:
- base:队列内存起始地址;
- size:队列最大容量(元素个数);
- element_size:每个元素的字节数;
- front、rear:队头与队尾索引,用于控制循环进出。
队列结构采用 循环数组,即环形缓冲的方式,避免频繁移动内存,入队出队仅需更新索引。
功能概览
接口函数 | 功能描述 |
queue_create | 创建并初始化队列 |
queue_is_empty | 判断队列是否为空 |
queue_is_full | 判断队列是否已满 |
queue_put | 入队(添加元素) |
queue_get | 出队(移除元素) |
queue_peek | 预读取队头元素,不出队 |
queue_del | 删除队头元素,不读取 |
queue_size | 获取当前队列长度 |
queue_clr | 清空队列 |
一、模块功能概述
本队列模块支持以下基础能力:
- 队列的初始化与创建;
- 判断队列空满状态;
- 元素入队与出队;
- 队列长度查询;
- 队列内容预读取(不出队);
- 队列清空;
- 队尾元素删除。
该队列采用循环数组结构,不依赖动态内存分配,适合资源受限的嵌入式环境使用。支持自定义元素类型,通过配置元素大小与容器地址实现通用性管理。
二、使用方式简述
使用者在使用该模块时需自行提供以下参数:
- 数据缓冲区指针;
- 缓冲区容量(元素个数);
- 单个元素的大小(字节);
- 队列管理结构体实例。
通过调用创建函数对队列进行初始化后,即可使用相关接口进行操作,如入队出队等。
三、接口功能说明
1. 创建队列
通过 queue_create 函数传入结构体、容器地址、容器容量、元素大小,即可完成队列初始化。
2. 状态判断
提供 queue_is_empty 和 queue_is_full 接口判断当前队列状态,便于进行边界处理。
3. 元素操作
- 使用 queue_put 向队列尾部写入一个元素;
- 使用 queue_get 从队列头部读取一个元素并出队;
- 使用 queue_peek 可在不出队的情况下获取队头元素地址;
- 使用 queue_del 可直接丢弃队头元素(删除而不读取);
- 使用 queue_clr 可清空整个队列内容;
- 使用 queue_size 可获取当前队列中元素数量。
1. 串口接收命令缓存
串口中断接收到数据帧后,将帧内容通过 queue_put 入队,主循环中调用 queue_get 提取完整指令进行解析,避免数据丢失或阻塞。
typedef struct {
uint8_t data[8];
} uart_cmd_t;
uart_cmd_t cmd_buf[16];
queue_t cmd_queue;
queue_create(&cmd_queue, cmd_buf, 16, sizeof(uart_cmd_t));
2. 多任务消息传递(RTOS环境)
任务 A 收集数据并通过 queue_put 入队,任务 B 轮询或通过事件驱动使用 queue_get 获取消息,完成跨任务通信。
3. 简易任务调度器
使用 queue_t 管理一个“函数指针 + 参数”结构体列表,即可构建轻量的调度任务表,实现软中断调度或定时任务执行。
通用队列管理模块,是构建嵌入式通信、缓存和调度功能的坚实基础。通过灵活封装与高效实现,该模块能够帮助开发者快速构建稳定的数据流通路。在系统架构设计中,清晰的数据组织方式往往是高质量代码的关键,队列,正是连接数据与逻辑的桥梁。
开源代码:
#include "queue.h"
#include <stddef.h>
#include <string.h>
/* @brief 创建一个队列
* @param[in] q - 队列管理器
* @param[in] base - 队列容器基地址
* @param[in] size - 队列容器可容纳元素个数
* @param[in] element_size - 队列元素大小(sizeof(type))
* @return true - 创建成功, false - 创建失败
*/
bool queue_create(queue_t *q, void *base, int size, int element_size)
{
if (q == NULL || base == NULL || size== 0 || element_size == 0)
return false;
q->base = base;
q->size = size;
q->element_size = element_size;
q->front = q->rear = 0;
return true;
}
/* @brief 判断队列是否为空
* @return true - 队列为空, false - 队列非空
*/
bool queue_is_empty(queue_t *q)
{
return q->front == q->rear;
}
/* @brief 判断队列是否满
* @return true - 队列非满, false - 队列未满
*/
bool queue_is_full(queue_t *q)
{
return (q->rear + 1) % q->size == q->front;
}
/* @brief 入队(添加元素至队尾)
* @param[in] element - 队列元素
* @return true - 成功入队, false - 队列满
*/
bool queue_put(queue_t *q, void *element)
{
if (queue_is_full(q))
return false;
memcpy((unsigned char *)q->base + q->rear * q->element_size, element,
q->element_size);
q->rear = (q->rear + 1) % q->size;
return true;
}
/* @brief 出队(移除队首元素)
* @param[in] element - 指向队首元素的地址
* @return true - 成功出队, false - 队列为空
*/
bool queue_get(queue_t *q, void *element)
{
if (queue_is_empty(q))
return false;
memcpy(element, (unsigned char *)q->base + q->front * q->element_size,
q->element_size);
q->front = (q->front + 1) % q->size;
return true;
}
/* @brief 获取队首元素地址(预读取)
* @param[in] element - 指向队首元素的地址
* @return true - 获取成功, false - 获取失败队列为空
*/
bool queue_peek(queue_t *q, void **element)
{
if (queue_is_empty(q))
return false;
*element = (void *)((unsigned char *)q->base + q->front *
q->element_size);
return true;
}
/* @brief 删除队尾元素
* @return true - 成功移除, false - 队列为空,
*/
bool queue_del(queue_t *q)
{
if (queue_is_empty(q))
return false;
q->front = (q->front + 1) % q->size;
return true;
}
/* @brief 获取队列元素个数
* @return 队列元素个数
*/
int queue_size(queue_t *q)
{
return (q->rear + q->size - q->front ) % q->size;
}
/* @brief 清空队列
* @return none
*/
void queue_clr(queue_t *q)
{
q->front = q->rear = 0;
}