嵌入式環形隊列和消息隊列是實現數據緩存和通信的常見數據結構,廣泛應用于嵌入式系統中的通信協議和領域。
環形隊列是一種先進先出(FIFO)的數據結構,其中數據被存儲在一個環形緩沖區中。它使用兩個指針,分別指向隊列的頭和尾,以便在讀寫操作時追蹤隊列的狀態。當隊列滿時,新數據將覆蓋舊數據,以確保隊列的長度保持不變。環形隊列在實現嵌入式通信協議時特別有用,例如UART,CAN等。
消息隊列是一種多個發送者和接收者之間共享數據的通信機制。它允許多個任務或線程向隊列發送消息,并允許多個任務或線程從隊列中接收消息。每個消息都有一個固定的大小和格式,并可以根據需要進行排隊和檢索。在嵌入式系統中,消息隊列廣泛用于處理異步事件,例如中斷處理和任務之間的通信。
主要應用于:
- 網絡通信協議(例如TCP/IP,UDP等)中的數據緩存和隊列管理。
- 嵌入式操作系統(例如FreeRTOS,uC/OS等)中的任務通信和事件處理。
- 汽車電子領域中的CAN和LIN通信協議。
- 工業自動化領域中的Modbus,Profibus等通信協議。
- 無線通信領域中的藍牙,Zigbee,LoRa等通信協議。
大致應用
- 串口通信中,可以使用環形隊列來接收和發送數據。當接收到新的數據時,將其存儲到環形隊列中,并在需要發送數據時從隊列中取出數據發送。這種方式可以減少中斷處理的時間,提高系統的響應速度。
- 多任務系統中,消息隊列用于任務之間的通信。每個任務都可以向消息隊列中發送消息,其他任務可以從隊列中獲取消息并進行相應的處理。這種方式可以實現任務之間的解耦,提高系統的可擴展性和可維護性。
- 實時控制系統中,環形隊列可以用于緩存傳感器數據或控制命令。當傳感器或其他設備向系統發送數據時,可以將其存儲到環形隊列中,然后由控制任務從隊列中獲取數據并進行相應的處理。這種方式可以減少系統對硬件的依賴性,提高系統的靈活性和可靠性。
- 音頻處理中,環形隊列可以用于實現音頻數據的緩存。當音頻數據輸入時,將其存儲到環形隊列中,然后由音頻處理任務從隊列中獲取數據并進行處理。這種方式可以實現音頻數據的流式處理,提高系統的處理效率和響應速度。
嵌入式環形隊列
嵌入式環形隊列是一種先進先出(FIFO)的隊列,其實現基于環形緩沖區。隊列的頭尾指針分別指向隊列的第一個元素和最后一個元素,當隊列滿時,新加入的元素將覆蓋隊列頭的元素。嵌入式環形隊列的實現過程如下:
- 隊列初始化:初始化頭尾指針為0,表示隊列為空。
- 入隊操作:將元素插入隊列尾部,尾指針加1,如果隊列滿了,則尾指針回到隊列開頭,覆蓋頭指針所指向的元素。
- 出隊操作:將隊列頭部元素出隊,并將頭指針加1,如果隊列已經空了,則頭指針回到隊列開頭。
嵌入式環形隊列的實現可以使用數組或鏈表來實現。使用數組時,需要考慮隊列滿時需要覆蓋隊列頭的元素,所以需要額外的邏輯來保證正確性。
嵌入式環形隊列操作步驟(大致如此)
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}
評論
查看更多