嵌入式环形队列和消息队列是实现数据缓存和通信的常见数据结构,广泛应用于嵌入式系统中的通信协议和领域。
环形队列是一种先进先出(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 102int queue[queue_size];3int head = 0;4int tail = 0;56void 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;45 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 }78 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#include2#include3#include45// 消息队列结构体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;1314// 创建一个消息队列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 }2021 mq->buf = (uint8_t *)malloc(size);22 if (mq->buf == null) {23 free(mq);24 return null;25 }2627 mq->head = 0;28 mq->tail = 0;29 mq->size = size;30 mq->count = 0;3132 return mq;33}3435// 销毁一个消息队列36void message_queue_destroy(message_queue_t *mq) {37 if (mq == null) {38 return;39 }4041 if (mq->buf != null) {42 free(mq->buf);43 }4445 free(mq);46}4748// 向消息队列中发送一条消息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 }5354 // 如果队列已满,则无法发送消息55 if (mq->count >= mq->size) {56 return 0;57 }5859 // 将消息写入队列缓存区60 uint32_t i;61 for (i = 0; i buf[mq->tail] = buf[i];63 mq->tail = (mq->tail + 1) % mq->size;64 }6566 // 更新队列状态67 mq->count += len;6869 return len;70}7172// 从消息队列中接收一条消息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 }7778 // 如果队列为空,则无法接收消息79 if (mq->count == 0) {80 return 0;81 }8283 // 读取队列缓存区中的消息84 uint32_t i;85 for (i = 0; i buf[mq->head];87 mq->head = (mq->head + 1) % mq->size;88 }8990 // 更新队列状态91 mq->count -= i;9293 return i;94}9596// 获取消息队列中的消息数量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 }67 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 }56 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 }56 // 如果队列已满,则无法发送消息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}
数字设计之时钟约束和时钟类型介绍
华为计划在巴西圣保罗新建一座手机工厂制造5G智能手机面向南美市场
蔡司工业CT揭示车灯质量隐藏的缺陷
英特尔于AI的七重助力
NANK南卡A2降噪蓝牙耳机测评:降噪功能强大!
嵌入式环形队列和消息队列是如何去实现的?
人工智能技术在电力系统中的应用现状和发展方向
连杆浮球液位计常见故障检查及维修方法
中国卫通声明:与华为Mate60pro之间并无合作关系
印刷线路板精密测试中的图像识别技术
Phantom Space想成为火箭领域的亨利·福特这可能吗?
RDA 3G/4G射频功率放大器核心技术指标达到国际领先水平
智能穿戴设备关键技术及其发展趋势
英飞凌推出全新TDA388xx系列同步降压稳压器
理清PCB布局思路 画pcb板的布局原则
南麟 无线充电 方案
Wi-Fi HaLow:这是什么,为什么重要
跑分没输过,体验没赢过,大模型刷分何时休?
可控硅的特性与检测,SCR Characteristics and test
关于苹果ios10.3系统的重大改变 以下这些你都需要了解