嵌入式环形队列和消息队列的实现

说明
嵌入式环形队列和消息队列是实现数据缓存和通信的常见数据结构,广泛应用于嵌入式系统中的通信协议和领域。
环形队列是一种先进先出(fifo)的数据结构,其中数据被存储在一个环形缓冲区中。 它使用两个指针,分别指向队列的头和尾,以便在读写操作时追踪队列的状态。 当队列满时,新数据将覆盖旧数据,以确保队列的长度保持不变。 环形队列在实现嵌入式通信协议时特别有用,例如uart,can等。
消息队列是一种多个发送者和接收者之间共享数据的通信机制。 它允许多个任务或线程向队列发送消息,并允许多个任务或线程从队列中接收消息。 每个消息都有一个固定的大小和格式,并可以根据需要进行排队和检索。 在嵌入式系统中,消息队列广泛用于处理异步事件,例如中断处理和任务之间的通信。
主要应用于:
网络通信协议(例如tcp/ip,udp等)中的数据缓存和队列管理。嵌入式操作系统(例如freertos,uc/os等)中的任务通信和事件处理。汽车电子领域中的can和lin通信协议。工业自动化领域中的modbus,profibus等通信协议。无线通信领域中的蓝牙,zigbee,lora等通信协议。大致应用
串口通信中,可以使用环形队列来接收和发送数据。 当接收到新的数据时,将其存储到环形队列中,并在需要发送数据时从队列中取出数据发送。 这种方式可以减少中断处理的时间,提高系统的响应速度。多任务系统中,消息队列用于任务之间的通信。 每个任务都可以向消息队列中发送消息,其他任务可以从队列中获取消息并进行相应的处理。 这种方式可以实现任务之间的解耦,提高系统的可扩展性和可维护性。实时控制系统中,环形队列可以用于缓存传感器数据或控制命令。 当传感器或其他设备向系统发送数据时,可以将其存储到环形队列中,然后由控制任务从队列中获取数据并进行相应的处理。 这种方式可以减少系统对硬件的依赖性,提高系统的灵活性和可靠性。音频处理中,环形队列可以用于实现音频数据的缓存。 当音频数据输入时,将其存储到环形队列中,然后由音频处理任务从队列中获取数据并进行处理。 这种方式可以实现音频数据的流式处理,提高系统的处理效率和响应速度。嵌入式环形队列
嵌入式环形队列是一种先进先出(fifo)的队列,其实现基于环形缓冲区。 队列的头尾指针分别指向队列的第一个元素和最后一个元素,当队列满时,新加入的元素将覆盖队列头的元素。 嵌入式环形队列的实现过程如下:
队列初始化:初始化头尾指针为0,表示队列为空。入队操作:将元素插入队列尾部,尾指针加1,如果队列满了,则尾指针回到队列开头,覆盖头指针所指向的元素。出队操作:将队列头部元素出队,并将头指针加1,如果队列已经空了,则头指针回到队列开头。嵌入式环形队列的实现可以使用数组或链表来实现。 使用数组时,需要考虑队列满时需要覆盖队列头的元素,所以需要额外的逻辑来保证正确性。
嵌入式环形队列操作步骤(大致如此)
1)定义一个固定大小的数组
1#define queue_size 102int queue[queue_size];2)定义两个指针,分别指向队列的起始位置和末尾位置
1int head = 0; // 队列起始位置2int tail = 0; // 队列末尾位置3)实现入队操作,即将元素添加到队列末尾。 如果队列已满,则不能再添加元素
1void enqueue(int data) {2 if ((tail + 1) % queue_size == head) {3 // 队列已满4 return;5 }6 queue[tail] = data;7 tail = (tail + 1) % queue_size;8}4)实现出队操作,即将队列中的元素删除并返回。 如果队列为空,则不能执行出队操作。
1int dequeue() {2 if (head == tail) {3 // 队列为空4 return -1;5 }6 int data = queue[head];7 head = (head + 1) % queue_size;8 return data;9}5)实现查询队列大小的函数
1int queue_size() {2 return (tail - head + queue_size) % queue_size;3}完整代码实现
1#define queue_size 10 2int queue[queue_size]; 3int head = 0; 4int tail = 0; 5 6void enqueue(int data) { 7 if ((tail + 1) % queue_size == head) { 8 // 队列已满 9 return;10 }11 queue[tail] = data;12 tail = (tail + 1) % queue_size;13}1415int dequeue() {16 if (head == tail) {17 // 队列为空18 return -1;19 }20 int data = queue[head];21 head = (head + 1) % queue_size;22 return data;23}2425int queue_size() {26 return (tail - head + queue_size) % queue_size;27}嵌入式消息队列
嵌入式消息队列的实现原理
嵌入式消息队列通常采用循环缓冲区实现,即使用一个数组作为缓冲区,通过头指针和尾指针来管理队列。 消息队列的基本操作包括入队和出队
入队操作
入队操作将一个消息写入队列中,实现方法如下:
1void queue_push(queue_t *queue, void *data, size_t size) 2{ 3 uint32_t next = (queue->tail + 1) % queue->capacity; 4 5 if (next == queue->head) { 6 // 队列已满,不再添加数据 7 return; 8 } 910 queue_item_t *item = &queue->items[queue->tail];11 item->size = size;12 item->data = malloc(size);13 memcpy(item->data, data, size);1415 queue->tail = next;16}在入队操作中,我们首先检查队列是否已满。 如果队列已满,就直接返回,不再添加数据。 如果队列未满,则使用尾指针指向的空间来存储新的数据。 为了避免新的数据覆盖旧的数据,我们需要动态分配一个新的内存空间,将数据复制到该空间中,并将其地址存储到队列的元素中。 最后,我们将尾指针往后移动一个位置。
出队操作
出队操作将一个消息从队列中读取出来,并将其从队列中删除,实现方法如下
1void queue_pop(queue_t *queue, void *data, size_t *size) 2{ 3 if (queue->head == queue->tail) { 4 // 队列为空,无法读取数据 5 return; 6 } 7 8 queue_item_t *item = &queue->items[queue->head]; 9 *size = item->size;10 memcpy(data, item->data, *size);1112 free(item->data);13 queue->head = (queue->head + 1) % queue->capacity;14}在出队操作中,我们首先检查队列是否为空。 如果队列为空,就直接返回,无法读取数据。 如果队列非空,则使用头指针指向的空间来读取数据。 我们将元素中存储的数据复制到指定的地址中,并返回数据的大小。 最后,我们释放元素中分配的内存空间,并将头指针往后移动一个位置。
消息示例:
1#include 2#include 3#include 4 5// 消息队列结构体 6typedef struct { 7 uint8_t *buf; // 指向队列缓存区的指针 8 uint32_t head; // 队头指针 9 uint32_t tail; // 队尾指针 10 uint32_t size; // 队列容量 11 uint32_t count; // 当前队列中的消息数量 12} message_queue_t; 13 14// 创建一个消息队列 15message_queue_t *message_queue_create(uint32_t size) { 16 message_queue_t *mq = (message_queue_t *)malloc(sizeof(message_queue_t)); 17 if (mq == null) { 18 return null; 19 } 20 21 mq->buf = (uint8_t *)malloc(size); 22 if (mq->buf == null) { 23 free(mq); 24 return null; 25 } 26 27 mq->head = 0; 28 mq->tail = 0; 29 mq->size = size; 30 mq->count = 0; 31 32 return mq; 33} 34 35// 销毁一个消息队列 36void message_queue_destroy(message_queue_t *mq) { 37 if (mq == null) { 38 return; 39 } 40 41 if (mq->buf != null) { 42 free(mq->buf); 43 } 44 45 free(mq); 46} 47 48// 向消息队列中发送一条消息 49uint32_t message_queue_send(message_queue_t *mq, uint8_t *buf, uint32_t len) { 50 if (mq == null || buf == null || len == 0) { 51 return 0; 52 } 53 54 // 如果队列已满,则无法发送消息 55 if (mq->count >= mq->size) { 56 return 0; 57 } 58 59 // 将消息写入队列缓存区 60 uint32_t i; 61 for (i = 0; i buf[mq->tail] = buf[i]; 63 mq->tail = (mq->tail + 1) % mq->size; 64 } 65 66 // 更新队列状态 67 mq->count += len; 68 69 return len; 70} 71 72// 从消息队列中接收一条消息 73uint32_t message_queue_recv(message_queue_t *mq, uint8_t *buf, uint32_t len) { 74 if (mq == null || buf == null || len == 0) { 75 return 0; 76 } 77 78 // 如果队列为空,则无法接收消息 79 if (mq->count == 0) { 80 return 0; 81 } 82 83 // 读取队列缓存区中的消息 84 uint32_t i; 85 for (i = 0; i buf[mq->head]; 87 mq->head = (mq->head + 1) % mq->size; 88 } 89 90 // 更新队列状态 91 mq->count -= i; 92 93 return i; 94} 95 96// 获取消息队列中的消息数量 97uint32_t message_queue_count(message_queue_t *mq) { 98 if (mq == null) { 99 return 0;100 }101102 return mq->count;103}消息队列示例说明
上面的示例是一个基于循环队列实现的简单嵌入式消息队列的代码实现,下面详细说明其实现原理:
消息队列结构体
定义一个消息队列结构体,包含队列缓存区指针、队头指针、队尾指针、队列容量和当前队列中的消息数量等信息
1typedef struct {2 uint8_t *buf; // 指向队列缓存区的指针3 uint32_t head; // 队头指针4 uint32_t tail; // 队尾指针5 uint32_t size; // 队列容量6 uint32_t count; // 当前队列中的消息数量7} message_queue_t;创建消息队列
使用 message_queue_create 函数创建一个消息队列,该函数会动态分配一块内存用于存储消息队列结构体和队列缓存区,初始化消息队列的各个参数,并返回一个指向消息队列结构体的指针。
1message_queue_t *message_queue_create(uint32_t size) { 2 message_queue_t *mq = (message_queue_t *)malloc(sizeof(message_queue_t)); 3 if (mq == null) { 4 return null; 5 } 6 7 mq->buf = (uint8_t *)malloc(size); 8 if (mq->buf == null) { 9 free(mq);10 return null;11 }1213 mq->head = 0;14 mq->tail = 0;15 mq->size = size;16 mq->count = 0;1718 return mq;19}销毁消息队列
使用 message_queue_destroy 函数销毁一个消息队列,该函数会释放消息队列结构体和队列缓存区所占用的内存。
1void message_queue_destroy(message_queue_t *mq) { 2 if (mq == null) { 3 return; 4 } 5 6 if (mq->buf != null) { 7 free(mq->buf); 8 } 910 free(mq);11}发送消息
使用 message_queue_send 函数向消息队列中发送一条消息,该函数会将消息写入队列缓存区,并更新队列的状态,返回实际写入队列缓存区的消息长度。
1uint32_t message_queue_send(message_queue_t *mq, uint8_t *buf, uint32_t len) { 2 if (mq == null || buf == null || len == 0) { 3 return 0; 4 } 5 6 // 如果队列已满,则无法发送消息 7 if (mq->count >= mq->size) { 8 return 0; 9 }1011 // 将消息写入队列缓存区12 uint32_t i;13 for (i = 0; i buf[mq->tail] = buf[i];15 mq->tail = (mq->tail + 1) % mq->size;16 }1718 // 更新队列状态19 mq->count += len;2021 return len;22}

人工智能会成为阿联酋的新“石油”吗?
热阻相关的JEDEC标准介绍
思特威推出全新0.7μm像素尺寸5200万CMOS图像传感器
采用AT89C52单片机实现彩色像片洗印机温控系统的设计方案
【号外】科陆电子中标深圳充电桩建设项目大单
嵌入式环形队列和消息队列的实现
什么是强化学习
介绍基于不确定的语法条件生成类似Java的强类型程序
2019年华为5G手机总售出690万台,智能手机发货量超过2.4亿台
微型电池需求爆发 小电池企业利好
国产游戏主机该如何发展
AMD桌面处理器份额3年来首次环比下滑
使用TDA2050构建一个2x32瓦音频放大器电路
六氟化硫气体检测——SF6气体红外泄漏报警监测系统
浅谈建筑防火监控系统在民用住宅建筑设计中的应用
怎样设计出接近要求规格的DC/DC转换器电路
什么是LED光色质量
关于人工智能最佳交互界面的分析介绍
为什么我抓不到baidu的数据包
Amber Solutions 专利支持电网的数字化管理