嵌入式技术
嵌入式环形队列和消息队列是实现数据缓存和通信的常见数据结构,广泛应用于嵌入式系统中的通信协议和领域。
环形队列是一种先进先出(FIFO)的数据结构,其中数据被存储在一个环形缓冲区中。它使用两个指针,分别指向队列的头和尾,以便在读写操作时追踪队列的状态。当队列满时,新数据将覆盖旧数据,以确保队列的长度保持不变。环形队列在实现嵌入式通信协议时特别有用,例如UART,CAN等。
消息队列是一种多个发送者和接收者之间共享数据的通信机制。它允许多个任务或线程向队列发送消息,并允许多个任务或线程从队列中接收消息。每个消息都有一个固定的大小和格式,并可以根据需要进行排队和检索。在嵌入式系统中,消息队列广泛用于处理异步事件,例如中断处理和任务之间的通信。
主要应用于:
大致应用
嵌入式环形队列
嵌入式环形队列是一种先进先出(FIFO)的队列,其实现基于环形缓冲区。队列的头尾指针分别指向队列的第一个元素和最后一个元素,当队列满时,新加入的元素将覆盖队列头的元素。嵌入式环形队列的实现过程如下:
嵌入式环形队列的实现可以使用数组或链表来实现。使用数组时,需要考虑队列满时需要覆盖队列头的元素,所以需要额外的逻辑来保证正确性。
嵌入式环形队列操作步骤(大致如此)
1)定义一个固定大小的数组
1#define QUEUE_SIZE 10
2int 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}
14
15int 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}
24
25int 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 }
9
10 queue_item_t *item = &queue->items[queue->tail];
11 item->size = size;
12 item->data = malloc(size);
13 memcpy(item->data, data, size);
14
15 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);
11
12 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 < len; i++) {
62 mq->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 < len && i < mq->count; i++) {
86 buf[i] = mq->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 }
101
102 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 }
12
13 mq->head = 0;
14 mq->tail = 0;
15 mq->size = size;
16 mq->count = 0;
17
18 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 }
9
10 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 }
10
11 // 将消息写入队列缓存区
12 uint32_t i;
13 for (i = 0; i < len; i++) {
14 mq->buf[mq->tail] = buf[i];
15 mq->tail = (mq->tail + 1) % mq->size;
16 }
17
18 // 更新队列状态
19 mq->count += len;
20
21 return len;
22}
全部0条评论
快来发表一下你的评论吧 !