網(wǎng)絡(luò)服務(wù)機(jī)構(gòu)seo數(shù)據(jù)優(yōu)化
提示:文章寫完后,目錄可以自動(dòng)生成,如何生成可參考右邊的幫助文檔
文章目錄
- 前言
- 一、隊(duì)列是什么?
- 而在freertos中,隊(duì)列是什么呢?
- ①如果要進(jìn)行中斷、任務(wù)的交流,那我用全局變量行嗎?
- ②那為什么隊(duì)列就可以代替全局變量的功能呢?
- ③看一看在freertos中隊(duì)列的結(jié)構(gòu)
- ④問題:當(dāng)多個(gè)任務(wù)寫入消息給一個(gè)“滿隊(duì)列”時(shí),這些任務(wù)都會(huì)進(jìn)入阻塞狀態(tài),也就是說有多個(gè)任務(wù) 在等待同一 個(gè)隊(duì)列的空間。那當(dāng)隊(duì)列中有空間時(shí),哪個(gè)任務(wù)會(huì)進(jìn)入就緒態(tài)?
- ⑤數(shù)據(jù)寫隊(duì)列、讀隊(duì)列操作過程
- 二、隊(duì)列的結(jié)構(gòu)體
- 1.結(jié)構(gòu)體內(nèi)容
- 2.結(jié)構(gòu)體示意圖
- 三、隊(duì)列相關(guān)API函數(shù)介紹
- 1.創(chuàng)建隊(duì)列
- 2.寫消息入隊(duì)列
- (1)前三個(gè)函數(shù)
- (2)后面的函數(shù)
- 3.從隊(duì)列中讀取消息
- 代碼例子:
- 四、隊(duì)列入隊(duì)和出隊(duì)操作實(shí)驗(yàn)
- 1.實(shí)驗(yàn)?zāi)繕?biāo)
- 2.例程
- ①main.c
- ②freertos_demo();
- ③操作隊(duì)列和存儲(chǔ)大數(shù)據(jù)塊
- ④任務(wù)一:實(shí)現(xiàn)入隊(duì)
- ⑤任務(wù)二:小數(shù)據(jù)出隊(duì)
- ⑥任務(wù)三:大數(shù)據(jù)出隊(duì)
- 3.例程運(yùn)行結(jié)果:
- 五、隊(duì)列相關(guān)API函數(shù)解析
- 1.隊(duì)列的創(chuàng)建API函數(shù):xQueueCreate( )
- 2.往隊(duì)列寫入數(shù)據(jù)API函數(shù)(入隊(duì)):xQueueSend( )
- 3.從隊(duì)列讀取數(shù)據(jù)API函數(shù)(出隊(duì)): xQueueReceive( )
前言
`本文包括以下內(nèi)容:
一、隊(duì)列是什么?
綜述:隊(duì)列是一種特殊的數(shù)據(jù)結(jié)構(gòu),它遵循先進(jìn)先出(FIFO)的原則。隊(duì)列中的元素按照其插入的順序進(jìn)行訪問和處理,新元素被插入到隊(duì)列的末尾,而已存在的元素則在隊(duì)列的前端進(jìn)行操作和刪除。隊(duì)列的操作包括入隊(duì)(enqueue)和出隊(duì)(dequeue),入隊(duì)表示將元素插入到隊(duì)列的末尾,而出隊(duì)則表示將隊(duì)列的前端元素移除并返回。隊(duì)列常用于需要按照先后順序處理元素的場(chǎng)景,例如任務(wù)調(diào)度、消息傳遞等。
而在freertos中,隊(duì)列是什么呢?
隊(duì)列是任務(wù)到任務(wù)、任務(wù)到中斷、中斷到任務(wù)數(shù)據(jù)交流的一種機(jī)制(消息傳遞)
①如果要進(jìn)行中斷、任務(wù)的交流,那我用全局變量行嗎?
答:不行。以這個(gè)圖為例,比如一個(gè)全局變量a=0,兩個(gè)任務(wù)里面都有對(duì)全局變量a的自增,如果兩個(gè)任務(wù)優(yōu)先級(jí)不同,當(dāng)運(yùn)行任務(wù)1時(shí)a++可能運(yùn)行到讀數(shù)據(jù)-修改數(shù)據(jù),但還沒有寫數(shù)據(jù)時(shí)就已經(jīng)被高優(yōu)先級(jí)的任務(wù)二打斷,導(dǎo)致a≠1,執(zhí)行任務(wù)二后才=1,這樣的話,執(zhí)行兩次任務(wù)卻讓全局變量a的值是錯(cuò)誤的。
所以全局變量的弊端:數(shù)據(jù)無保護(hù),導(dǎo)致數(shù)據(jù)不安全,當(dāng)多個(gè)任務(wù)同時(shí)對(duì)該變量操作時(shí),數(shù)據(jù)易受損
②那為什么隊(duì)列就可以代替全局變量的功能呢?
首先來看隊(duì)列的結(jié)構(gòu):
可以看到,任務(wù)A和B是寫隊(duì)列操作,寫隊(duì)列這個(gè)函數(shù)呢,它會(huì)進(jìn)入臨界區(qū),完成實(shí)際操作后再退出臨界區(qū),所以:寫隊(duì)列時(shí)實(shí)際關(guān)閉了系統(tǒng)中斷,使得臨界區(qū)代碼可以完整的運(yùn)行不被打斷,而讀隊(duì)列也是同理。
所以,讀寫隊(duì)列具有程序保護(hù)功能,防止多任務(wù)同時(shí)訪問造成的數(shù)據(jù)沖突。
③看一看在freertos中隊(duì)列的結(jié)構(gòu)
在隊(duì)列中可以存儲(chǔ)數(shù)量有限、大小固定的數(shù)據(jù)。隊(duì)列中的每一個(gè)數(shù)據(jù)叫做“隊(duì)列項(xiàng)目”,隊(duì)列能夠存儲(chǔ)“隊(duì)列項(xiàng)目”的最大數(shù)量稱為隊(duì)列的長(zhǎng)度。
所以隊(duì)列的核心特征:隊(duì)列長(zhǎng)度和每個(gè)隊(duì)列項(xiàng)目大小。需要我們自己創(chuàng)建時(shí)設(shè)置。
在Freertos中,隊(duì)列的特點(diǎn):
(1)FIFO是First-In First-Out的縮寫,意為先進(jìn)先出。在隊(duì)列中,新元素被插入到隊(duì)列的末尾,而已存在的元素則在隊(duì)列的前端進(jìn)行操作和刪除。當(dāng)需要訪問或處理隊(duì)列中的元素時(shí),先訪問或處理隊(duì)列中最早插入的元素,然后按照插入的先后順序依次訪問或處理其他元素。這種先進(jìn)先出的特性使得隊(duì)列成為一種常用的數(shù)據(jù)結(jié)構(gòu),在任務(wù)調(diào)度、緩存管理、消息傳遞等場(chǎng)景中得到廣泛應(yīng)用。
(2)在FreeRTOS中,隊(duì)列可以采用實(shí)際值傳遞或者傳遞指針的方式進(jìn)行數(shù)據(jù)傳遞。實(shí)際值傳遞是指將數(shù)據(jù)的副本拷貝到隊(duì)列中進(jìn)行傳遞,這樣操作的是數(shù)據(jù)的副本,對(duì)原始數(shù)據(jù)沒有影響。而傳遞指針則是將指向?qū)嶋H數(shù)據(jù)的指針放入隊(duì)列中,這樣可以避免復(fù)制大量的數(shù)據(jù),但需要注意在使用指針傳遞時(shí),確保不會(huì)出現(xiàn)指針指向無效數(shù)據(jù)的情況。
在傳遞較大的數(shù)據(jù)時(shí),采用指針傳遞可以避免頻繁的數(shù)據(jù)復(fù)制,提高效率。但需要注意在使用指針傳遞時(shí),要確保數(shù)據(jù)的有效性,即確保指針指向的數(shù)據(jù)在傳遞過程中不會(huì)被修改或釋放,以免導(dǎo)致數(shù)據(jù)錯(cuò)誤或懸掛指針的情況。
(3)隊(duì)列在FreeRTOS中是一種通用的機(jī)制,可以被任何任務(wù)或中斷使用來發(fā)送和讀取消息。這是因?yàn)殛?duì)列是一種共享的數(shù)據(jù)結(jié)構(gòu),用于在不同的任務(wù)或中斷之間傳遞數(shù)據(jù)。任何任務(wù)或中斷都可以使用隊(duì)列的API函數(shù)來發(fā)送消息到隊(duì)列或從隊(duì)列中讀取消息,無論它們屬于哪個(gè)任務(wù)。
這種靈活性使得隊(duì)列成為一種常用的通信機(jī)制,在多任務(wù)或多中斷的系統(tǒng)中,可以方便地進(jìn)行任務(wù)間的數(shù)據(jù)傳遞和同步。通過隊(duì)列,任務(wù)和中斷可以安全地共享數(shù)據(jù),避免競(jìng)爭(zhēng)條件和數(shù)據(jù)沖突的問題。同時(shí),任務(wù)和中斷可以根據(jù)需要進(jìn)行阻塞或喚醒,以實(shí)現(xiàn)有效的同步和通信。
(4)當(dāng)任務(wù)向一個(gè)隊(duì)列發(fā)送消息時(shí),可以通過指定一個(gè)阻塞時(shí)間來控制任務(wù)的行為。
如果隊(duì)列已滿,無法將消息入隊(duì),任務(wù)可以選擇以下幾種行為:
①阻塞等待:任務(wù)可以指定一個(gè)阻塞時(shí)間,如果隊(duì)列已滿,則任務(wù)會(huì)在隊(duì)列有空閑位置之前被阻塞。任務(wù)將等待,直到隊(duì)列有空閑位置并成功將消息入隊(duì),或者等待的超時(shí)時(shí)間到達(dá)。
②非阻塞立即返回:任務(wù)可以選擇在隊(duì)列已滿時(shí)立即返回,而不進(jìn)行阻塞等待。任務(wù)可以根據(jù)返回的結(jié)果來判斷是否消息成功發(fā)送到隊(duì)列中。
④問題:當(dāng)多個(gè)任務(wù)寫入消息給一個(gè)“滿隊(duì)列”時(shí),這些任務(wù)都會(huì)進(jìn)入阻塞狀態(tài),也就是說有多個(gè)任務(wù) 在等待同一 個(gè)隊(duì)列的空間。那當(dāng)隊(duì)列中有空間時(shí),哪個(gè)任務(wù)會(huì)進(jìn)入就緒態(tài)?
⑤數(shù)據(jù)寫隊(duì)列、讀隊(duì)列操作過程
二、隊(duì)列的結(jié)構(gòu)體
1.結(jié)構(gòu)體內(nèi)容
如下:
typedef struct QueueDefinition
{int8_t * pcHead /* 存儲(chǔ)區(qū)域的起始地址 */int8_t * pcWriteTo; /* 下一個(gè)寫入的位置 */union{QueuePointers_t xQueue; SemaphoreData_t xSemaphore; } u ;List_t xTasksWaitingToSend; /* 等待發(fā)送列表 */List_t xTasksWaitingToReceive; /* 等待接收列表 */volatile UBaseType_t uxMessagesWaiting; /* 非空閑隊(duì)列項(xiàng)目的數(shù)量 */UBaseType_t uxLength; /* 隊(duì)列長(zhǎng)度 */UBaseType_t uxItemSize; /* 隊(duì)列項(xiàng)目的大小 */volatile int8_t cRxLock; /* 讀取上鎖計(jì)數(shù)器 */volatile int8_t cTxLock; /* 寫入上鎖計(jì)數(shù)器 *//* 其他的一些條件編譯 */
} xQUEUE;
這段代碼是一個(gè)隊(duì)列的定義,具體的結(jié)構(gòu)體成員解釋如下:
int8_t * pcHead: 存儲(chǔ)區(qū)域的起始地址,即隊(duì)列的存儲(chǔ)空間的首地址。
int8_t * pcWriteTo: 下一個(gè)寫入位置的指針,用于指示下一個(gè)要寫入數(shù)據(jù)的位置。
union { QueuePointers_t xQueue; SemaphoreData_t xSemaphore; } u: 一個(gè)聯(lián)合體,用于保存隊(duì)列指針或信號(hào)量數(shù)據(jù)。
List_t xTasksWaitingToSend: 等待發(fā)送列表,用于存儲(chǔ)等待向隊(duì)列發(fā)送消息的任務(wù)。
List_t xTasksWaitingToReceive: 等待接收列表,用于存儲(chǔ)等待從隊(duì)列接收消息的任務(wù)。
volatile UBaseType_t uxMessagesWaiting: 非空閑隊(duì)列項(xiàng)目的數(shù)量,用于記錄當(dāng)前隊(duì)列中等待接收的消息數(shù)量。
UBaseType_t uxLength: 隊(duì)列長(zhǎng)度,表示隊(duì)列可以容納的最大項(xiàng)目數(shù)量。
UBaseType_t uxItemSize: 隊(duì)列項(xiàng)目的大小,表示每個(gè)項(xiàng)目占用的字節(jié)數(shù)。
volatile int8_t cRxLock: 讀取上鎖計(jì)數(shù)器,用于記錄當(dāng)前隊(duì)列被讀取操作鎖定的次數(shù)。
volatile int8_t cTxLock: 寫入上鎖計(jì)數(shù)器,用于記錄當(dāng)前隊(duì)列被寫入操作鎖定的次數(shù)。
2.結(jié)構(gòu)體示意圖
三、隊(duì)列相關(guān)API函數(shù)介紹
使用隊(duì)列的主要流程:創(chuàng)建隊(duì)列 ->寫隊(duì)列 -> 讀隊(duì)列。主要包括創(chuàng)建隊(duì)列、寫隊(duì)列、讀隊(duì)列三個(gè)部分。
1.創(chuàng)建隊(duì)列
參數(shù)說明:
uxQueueLength:隊(duì)列的長(zhǎng)度,即隊(duì)列可以容納的最大項(xiàng)目數(shù)量。
uxItemSize:隊(duì)列中每個(gè)項(xiàng)目的大小,即每個(gè)項(xiàng)目占用的字節(jié)數(shù)。
返回值:
成功創(chuàng)建隊(duì)列時(shí),返回一個(gè)有效的隊(duì)列句柄(QueueHandle_t)。
創(chuàng)建隊(duì)列失敗時(shí),返回 NULL。
示例用法:
#include "FreeRTOS.h"
#include "queue.h"// 創(chuàng)建一個(gè)長(zhǎng)度為10,每個(gè)項(xiàng)目大小為4字節(jié)的隊(duì)列
QueueHandle_t xQueue = xQueueCreate(10, sizeof(int));
if (xQueue != NULL) {// 隊(duì)列創(chuàng)建成功
} else {// 隊(duì)列創(chuàng)建失敗
}
xQueueCreate() 函數(shù)用于在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建一個(gè)隊(duì)列,并返回一個(gè)隊(duì)列句柄,該句柄可用于后續(xù)對(duì)隊(duì)列進(jìn)行操作,如發(fā)送消息和接收消息。注意,在使用完隊(duì)列后,需要使用 vQueueDelete() 函數(shù)來刪除隊(duì)列,以釋放相關(guān)的資源。
2.寫消息入隊(duì)列
寫消息到隊(duì)列里,只能往隊(duì)列頭部、隊(duì)列尾部、覆寫方式寫入隊(duì)列這三種方法,覆寫只有在隊(duì)列的隊(duì)列長(zhǎng)度為 1 時(shí),才能夠使用
(1)前三個(gè)函數(shù)
xQueueSend()、xQueueSendToBack() 和 xQueueSendToFront() 函數(shù)都是用于向隊(duì)列中寫入消息的函數(shù),它們的作用類似,但有一些細(xì)微的差別。
這些函數(shù)的函數(shù)原型如下:
BaseType_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);BaseType_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);BaseType_t xQueueSendToFront(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
參數(shù)說明:
xQueue:要寫入的隊(duì)列的句柄。
pvItemToQueue:指向要寫入隊(duì)列的消息的指針。
xTicksToWait:寫入操作的超時(shí)時(shí)間,如果隊(duì)列已滿,則等待一段時(shí)間再嘗試寫入??梢允褂?portMAX_DELAY 來表示無限等待。
返回值:
如果成功寫入消息到隊(duì)列,則返回 pdPASS。
如果寫入消息失敗(如隊(duì)列已滿),并且在指定的超時(shí)時(shí)間內(nèi)未能成功寫入,則返回 errQUEUE_FULL。
示例用法:
#include "FreeRTOS.h"
#include "queue.h"// 創(chuàng)建一個(gè)長(zhǎng)度為10,每個(gè)項(xiàng)目大小為4字節(jié)的隊(duì)列
QueueHandle_t xQueue = xQueueCreate(10, sizeof(int));int message = 42;// 向隊(duì)列尾部寫入消息
if (xQueueSend(xQueue, &message, portMAX_DELAY) == pdPASS) {// 消息寫入成功
} else {// 消息寫入失敗
}// 向隊(duì)列尾部寫入消息(與 xQueueSend() 等效)
if (xQueueSendToBack(xQueue, &message, portMAX_DELAY) == pdPASS) {// 消息寫入成功
} else {// 消息寫入失敗
}// 向隊(duì)列頭部寫入消息
if (xQueueSendToFront(xQueue, &message, portMAX_DELAY) == pdPASS) {// 消息寫入成功
} else {// 消息寫入失敗
}
這些函數(shù)用于將消息寫入隊(duì)列中,xQueueSend() 和 xQueueSendToBack() 將消息寫入隊(duì)列的尾部,而 xQueueSendToFront() 將消息寫入隊(duì)列的頭部。如果隊(duì)列已滿,則寫入操作將會(huì)阻塞,直到隊(duì)列有可用空間或超時(shí)。
(2)后面的函數(shù)
xQueueOverwrite() 和 xQueueOverwriteFromISR() 函數(shù)用于覆寫隊(duì)列中的消息,僅適用于隊(duì)列長(zhǎng)度為1的情況。
這些函數(shù)的函數(shù)原型如下:
BaseType_t xQueueOverwrite(QueueHandle_t xQueue, const void *pvItemToQueue);BaseType_t xQueueOverwriteFromISR(QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken);
參數(shù)說明:
xQueue:要操作的隊(duì)列的句柄。
pvItemToQueue:指向要寫入隊(duì)列的消息的指針。
pxHigherPriorityTaskWoken:一個(gè)指向 BaseType_t 類型變量的指針,用于指示是否有更高優(yōu)先級(jí)的任務(wù)需要喚醒。在 xQueueOverwriteFromISR() 中使用,可以設(shè)置為 NULL。
返回值:
如果成功覆寫隊(duì)列中的消息,則返回 pdPASS。
如果隊(duì)列為空或隊(duì)列長(zhǎng)度不為1,則返回 errQUEUE_FULL。
示例用法:
#include "FreeRTOS.h"
#include "queue.h"// 創(chuàng)建一個(gè)長(zhǎng)度為1,每個(gè)項(xiàng)目大小為4字節(jié)的隊(duì)列
QueueHandle_t xQueue = xQueueCreate(1, sizeof(int));int message = 42;// 覆寫隊(duì)列中的消息
if (xQueueOverwrite(xQueue, &message) == pdPASS) {// 消息覆寫成功
} else {// 消息覆寫失敗
}// 在中斷中覆寫隊(duì)列中的消息
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (xQueueOverwriteFromISR(xQueue, &message, &xHigherPriorityTaskWoken) == pdPASS) {// 消息覆寫成功
} else {// 消息覆寫失敗
}
xQueueOverwrite() 和 xQueueOverwriteFromISR() 函數(shù)用于覆寫隊(duì)列中的消息。在隊(duì)列長(zhǎng)度為1的情況下,可以使用這些函數(shù)來覆蓋隊(duì)列中的現(xiàn)有消息,而不需要等待或創(chuàng)建新的消息。請(qǐng)注意,這些函數(shù)只適用于隊(duì)列長(zhǎng)度為1的情況。
在中斷處理程序中使用 xQueueOverwriteFromISR() 函數(shù)時(shí),需要將 pxHigherPriorityTaskWoken 參數(shù)設(shè)置為非空指針,并且根據(jù)實(shí)際情況判斷是否需要喚醒更高優(yōu)先級(jí)的任務(wù)。
3.從隊(duì)列中讀取消息
xQueueReceive()
從隊(duì)列頭部讀取消息,并將消息從隊(duì)列中刪除。
如果隊(duì)列為空,任務(wù)將進(jìn)入阻塞狀態(tài),直到隊(duì)列中有消息可讀取。
返回pdPASS表示成功讀取到消息,返回errQUEUE_EMPTY表示隊(duì)列為空。
xQueuePeek()
從隊(duì)列頭部讀取消息,但不刪除消息。
如果隊(duì)列為空,任務(wù)將進(jìn)入阻塞狀態(tài),直到隊(duì)列中有消息可讀取。
返回pdPASS表示成功讀取到消息,返回errQUEUE_EMPTY表示隊(duì)列為空。
xQueueReceiveFromISR()
在中斷中從隊(duì)列頭部讀取消息,并將消息從隊(duì)列中刪除。
與xQueueReceive()函數(shù)類似,但是特別適用于在中斷服務(wù)例程(ISR)中使用。
返回pdPASS表示成功讀取到消息,返回errQUEUE_EMPTY表示隊(duì)列為空。
xQueuePeekFromISR()
在中斷中從隊(duì)列頭部讀取消息,但不刪除消息。
與xQueuePeek()函數(shù)類似,但是特別適用于在中斷服務(wù)例程(ISR)中使用。
返回pdPASS表示成功讀取到消息,返回errQUEUE_EMPTY表示隊(duì)列為空。
這些函數(shù)都是用于讀取隊(duì)列中的消息,并根據(jù)需要?jiǎng)h除消息或者保留消息。其中,xQueueReceiveFromISR()和xQueuePeekFromISR()函數(shù)專門用于在中斷服務(wù)例程中使用。這些函數(shù)將任務(wù)或中斷服務(wù)例程阻塞直到隊(duì)列中有消息可讀取。如果隊(duì)列為空,任務(wù)或中斷服務(wù)例程將進(jìn)入阻塞狀態(tài),直到隊(duì)列中有消息可讀取。返回值用于指示讀取操作是否成功。
代碼例子:
下面是一個(gè)簡(jiǎn)單的示例代碼,展示了如何使用xQueueReceive()和xQueuePeek()函數(shù)從隊(duì)列中讀取消息:
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"// 定義一個(gè)全局隊(duì)列
QueueHandle_t queue;// 任務(wù)函數(shù)1,向隊(duì)列中發(fā)送消息
void task1(void *pvParameters) {int msg = 100;while(1) {// 發(fā)送消息到隊(duì)列中xQueueSend(queue, &msg, 0);// 任務(wù)延時(shí)1秒vTaskDelay(pdMS_TO_TICKS(1000));}
}// 任務(wù)函數(shù)2,從隊(duì)列中讀取消息并刪除
void task2(void *pvParameters) {int receivedMsg;while(1) {// 從隊(duì)列中讀取并刪除消息if(xQueueReceive(queue, &receivedMsg, portMAX_DELAY) == pdPASS) {printf("Received message: %d\n", receivedMsg);}// 任務(wù)延時(shí)500毫秒vTaskDelay(pdMS_TO_TICKS(500));}
}int main() {// 創(chuàng)建隊(duì)列queue = xQueueCreate(5, sizeof(int));// 創(chuàng)建任務(wù)1xTaskCreate(task1, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);// 創(chuàng)建任務(wù)2xTaskCreate(task2, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, NULL);// 啟動(dòng)調(diào)度器vTaskStartScheduler();// 如果調(diào)度器啟動(dòng)失敗,則打印錯(cuò)誤信息printf("Failed to start FreeRTOS scheduler!\n");return 0;
}
在這個(gè)示例中,我們創(chuàng)建了一個(gè)全局隊(duì)列queue,然后創(chuàng)建了兩個(gè)任務(wù)task1和task2。task1任務(wù)通過xQueueSend()函數(shù)將消息發(fā)送到隊(duì)列中,而task2任務(wù)使用xQueueReceive()函數(shù)從隊(duì)列中讀取并刪除消息。每個(gè)任務(wù)都使用vTaskDelay()函數(shù)延時(shí)一定的時(shí)間,以模擬任務(wù)執(zhí)行的過程。
當(dāng)task2任務(wù)成功從隊(duì)列中讀取到消息時(shí),將打印消息內(nèi)容。這里使用了portMAX_DELAY作為阻塞時(shí)間參數(shù),表示如果隊(duì)列為空,任務(wù)將一直阻塞,直到有消息可讀取。
這個(gè)示例展示了如何使用xQueueReceive()函數(shù)從隊(duì)列中讀取消息并刪除,以及如何使用xQueueSend()函數(shù)向隊(duì)列中發(fā)送消息。實(shí)際應(yīng)用中,可以根據(jù)需要在任務(wù)中使用這些函數(shù)來實(shí)現(xiàn)消息傳遞和同步。
四、隊(duì)列入隊(duì)和出隊(duì)操作實(shí)驗(yàn)
1.實(shí)驗(yàn)?zāi)繕?biāo)
這次例程我會(huì)進(jìn)行一個(gè)非常細(xì)致的講解:
2.例程
①main.c
int main(void)
{HAL_Init(); /* 初始化HAL庫 */sys_stm32_clock_init(360, 25, 2, 8); /* 設(shè)置時(shí)鐘,180Mhz */delay_init(180); /* 延時(shí)初始化 */usart_init(115200); /* 串口初始化為115200 */led_init(); /* 初始化LED */key_init(); /* 初始化按鍵 */sdram_init(); /* SRAM初始化 */lcd_init(); /* 初始化LCD */my_mem_init(SRAMIN); /* 初始化內(nèi)部?jī)?nèi)存池 */my_mem_init(SRAMEX); /* 初始化外部?jī)?nèi)存池 */my_mem_init(SRAMCCM); /* 初始化CCM內(nèi)存池 */freertos_demo();
}
除了那些裸機(jī)也用到的函數(shù)外,還有這些內(nèi)存初始化設(shè)置:
sdram_init():用于初始化SRAM(靜態(tài)隨機(jī)存取存儲(chǔ)器)。SRAM是一種高速的存儲(chǔ)器,通常用于存儲(chǔ)數(shù)據(jù)、變量或者代碼。
my_mem_init(SRAMIN):用于初始化內(nèi)部?jī)?nèi)存池。內(nèi)部?jī)?nèi)存池是指在芯片內(nèi)部的一塊存儲(chǔ)空間,用于存儲(chǔ)數(shù)據(jù)、變量或者代碼。
my_mem_init(SRAMEX):用于初始化外部?jī)?nèi)存池。外部?jī)?nèi)存池是指連接在芯片外部的一塊存儲(chǔ)空間,通常是使用外部存儲(chǔ)器(如SDRAM、NOR Flash)擴(kuò)展的存儲(chǔ)器。
my_mem_init(SRAMCCM):用于初始化CCM(Core-Coupled Memory)內(nèi)存池。CCM內(nèi)存是一種與CPU核心緊密耦合的存儲(chǔ)器,它具有低延遲和高帶寬的特點(diǎn),適用于存儲(chǔ)關(guān)鍵數(shù)據(jù)和代碼。
最重要的當(dāng)然就是freertos_demo();函數(shù)了。
②freertos_demo();
/*** @brief FreeRTOS例程入口函數(shù)* @param 無* @retval 無*/
void freertos_demo(void)
{ /* 隊(duì)列的創(chuàng)建 */key_queue = xQueueCreate( 2, sizeof(uint8_t) );if(key_queue != NULL){printf("key_queue隊(duì)列創(chuàng)建成功!!\r\n");}else printf("key_queue隊(duì)列創(chuàng)建失敗!!\r\n");big_date_queue = xQueueCreate( 1, sizeof(char *) );if(big_date_queue != NULL){printf("big_date_queue隊(duì)列創(chuàng)建成功!!\r\n");}else printf("big_date_queue隊(duì)列創(chuàng)建失敗!!\r\n");xTaskCreate((TaskFunction_t ) start_task,(char * ) "start_task",(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,(void * ) NULL,(UBaseType_t ) START_TASK_PRIO,(TaskHandle_t * ) &start_task_handler );vTaskStartScheduler();
}
這是一個(gè)名為freertos_demo()的函數(shù),用于演示FreeRTOS中隊(duì)列的創(chuàng)建和任務(wù)的創(chuàng)建與調(diào)度。
key_queue = xQueueCreate(2, sizeof(uint8_t)):創(chuàng)建一個(gè)容量為2,元素大小為uint8_t的隊(duì)列,用于存儲(chǔ)按鍵值。如果隊(duì)列創(chuàng)建成功,會(huì)打印"key_queue隊(duì)列創(chuàng)建成功!!“,否則打印"key_queue隊(duì)列創(chuàng)建失敗!!”。
big_date_queue = xQueueCreate(1, sizeof(char *)):創(chuàng)建一個(gè)容量為1,元素大小為char指針的隊(duì)列,用于存儲(chǔ)大數(shù)據(jù)塊。如果隊(duì)列創(chuàng)建成功,會(huì)打印"big_date_queue隊(duì)列創(chuàng)建成功!!“,否則打印"big_date_queue隊(duì)列創(chuàng)建失敗!!”.
xTaskCreate(start_task, “start_task”, START_TASK_STACK_SIZE, NULL, START_TASK_PRIO, &start_task_handler):創(chuàng)建一個(gè)名為"start_task"的任務(wù),使用start_task()函數(shù)作為任務(wù)函數(shù)。該任務(wù)的堆棧大小為START_TASK_STACK_SIZE,優(yōu)先級(jí)為START_TASK_PRIO。任務(wù)句柄start_task_handler用于后續(xù)操作。
vTaskStartScheduler():啟動(dòng)FreeRTOS調(diào)度器,開始執(zhí)行任務(wù)。
③操作隊(duì)列和存儲(chǔ)大數(shù)據(jù)塊
QueueHandle_t key_queue; /* 小數(shù)據(jù)句柄 */
QueueHandle_t big_date_queue; /* 大數(shù)據(jù)句柄 */
char buff[100] = {"我是一個(gè)大數(shù)組,大大的數(shù)組 124214 uhsidhaksjhdklsadhsaklj"};
key_queue和big_date_queue是隊(duì)列的句柄(或稱為隊(duì)列的指針),用于在程序中引用這兩個(gè)隊(duì)列。key_queue是一個(gè)指向小數(shù)據(jù)隊(duì)列的句柄,big_date_queue是一個(gè)指向大數(shù)據(jù)隊(duì)列的句柄。
另外,還定義了一個(gè)名為buff的字符數(shù)組,長(zhǎng)度為100,用于存儲(chǔ)大數(shù)據(jù)塊。該數(shù)組中包含了一個(gè)字符串,表示一個(gè)大數(shù)據(jù)塊的內(nèi)容。
④任務(wù)一:實(shí)現(xiàn)入隊(duì)
/* 任務(wù)一,實(shí)現(xiàn)入隊(duì) */
void task1( void * pvParameters )
{uint8_t key = 0;char * buf;BaseType_t err = 0;buf = &buff[0]; /* buf = &buff[0] */while(1) {key = key_scan(0);if(key == KEY0_PRES || key == KEY1_PRES){err = xQueueSend( key_queue, &key, portMAX_DELAY );if(err != pdTRUE){printf("key_queue隊(duì)列發(fā)送失敗\r\n");}}else if(key == WKUP_PRES){err = xQueueSend( big_date_queue, &buf, portMAX_DELAY );if(err != pdTRUE){printf("key_queue隊(duì)列發(fā)送失敗\r\n");}}vTaskDelay(10);}
}
在任務(wù)函數(shù)開始時(shí),定義了一個(gè)key變量和一個(gè)buf指針變量。buf指針指向了之前提到的buff數(shù)組的第一個(gè)元素。
在任務(wù)的主循環(huán)中,首先調(diào)用key_scan(0)函數(shù)來獲取按鍵值,并將其賦值給key變量。然后通過條件判斷,判斷按鍵值是否為KEY0_PRES或KEY1_PRES,如果是,則將key值發(fā)送到key_queue隊(duì)列中,使用xQueueSend()函數(shù)來實(shí)現(xiàn)。如果發(fā)送失敗,則打印"key_queue隊(duì)列發(fā)送失敗"。
另外,如果按鍵值為WKUP_PRES,則將buf指針發(fā)送到big_date_queue隊(duì)列中,同樣使用xQueueSend()函數(shù)來實(shí)現(xiàn)。如果發(fā)送失敗,則打印"key_queue隊(duì)列發(fā)送失敗"。
最后,通過vTaskDelay(10)函數(shù)來延時(shí)10個(gè)系統(tǒng)時(shí)鐘周期,然后繼續(xù)下一次循環(huán)。
這個(gè)任務(wù)函數(shù)的功能是根據(jù)按鍵值將數(shù)據(jù)發(fā)送到不同的隊(duì)列中,實(shí)現(xiàn)了數(shù)據(jù)入隊(duì)的操作。
⑤任務(wù)二:小數(shù)據(jù)出隊(duì)
/* 任務(wù)二,小數(shù)據(jù)出隊(duì) */
void task2( void * pvParameters )
{uint8_t key = 0;BaseType_t err = 0;while(1){err = xQueueReceive( key_queue,&key,portMAX_DELAY);if(err != pdTRUE){printf("key_queue隊(duì)列讀取失敗\r\n");}else {printf("key_queue讀取隊(duì)列成功,數(shù)據(jù):%d\r\n",key);}}
}
在任務(wù)函數(shù)開始時(shí),定義了一個(gè)key變量和一個(gè)err變量。
在任務(wù)的主循環(huán)中,調(diào)用xQueueReceive()函數(shù)從key_queue隊(duì)列中接收數(shù)據(jù),并將接收到的數(shù)據(jù)保存在key變量中。使用portMAX_DELAY作為阻塞時(shí)間,表示如果隊(duì)列為空,任務(wù)將一直阻塞直到有數(shù)據(jù)可用。
接收數(shù)據(jù)后,通過條件判斷,判斷數(shù)據(jù)接收是否成功。如果接收失敗,則打印"key_queue隊(duì)列讀取失敗"。如果接收成功,則打印"key_queue讀取隊(duì)列成功,數(shù)據(jù):"并打印出接收到的key值。
這個(gè)任務(wù)函數(shù)的功能是從key_queue隊(duì)列中接收數(shù)據(jù)并進(jìn)行處理
⑥任務(wù)三:大數(shù)據(jù)出隊(duì)
/* 任務(wù)三,大數(shù)據(jù)出隊(duì) */
void task3( void * pvParameters )
{char * buf;BaseType_t err = 0;while(1){err = xQueueReceive( big_date_queue,&buf,portMAX_DELAY);if(err != pdTRUE){printf("big_date_queue隊(duì)列讀取失敗\r\n");}else {printf("數(shù)據(jù):%s\r\n",buf);}}
}
根據(jù)你提供的代碼,這是一個(gè)名為task3()的任務(wù)函數(shù),用于實(shí)現(xiàn)大數(shù)據(jù)出隊(duì)操作。
在任務(wù)函數(shù)開始時(shí),定義了一個(gè)buf指針變量和一個(gè)err變量。
在任務(wù)的主循環(huán)中,調(diào)用xQueueReceive()函數(shù)從big_date_queue隊(duì)列中接收數(shù)據(jù),并將接收到的數(shù)據(jù)保存在buf指針變量中。使用portMAX_DELAY作為阻塞時(shí)間,表示如果隊(duì)列為空,任務(wù)將一直阻塞直到有數(shù)據(jù)可用。
接收數(shù)據(jù)后,通過條件判斷,判斷數(shù)據(jù)接收是否成功。如果接收失敗,則打印"big_date_queue隊(duì)列讀取失敗"。如果接收成功,則打印"數(shù)據(jù):"并打印出接收到的字符串?dāng)?shù)據(jù)。
這個(gè)任務(wù)函數(shù)的功能是從big_date_queue隊(duì)列中接收大數(shù)據(jù)塊,并進(jìn)行處理。
3.例程運(yùn)行結(jié)果:
五、隊(duì)列相關(guān)API函數(shù)解析
1.隊(duì)列的創(chuàng)建API函數(shù):xQueueCreate( )
xQueueCreate()函數(shù)是一個(gè)FreeRTOS中用于創(chuàng)建隊(duì)列的API函數(shù)。它的內(nèi)部實(shí)現(xiàn)過程如下:
首先,函數(shù)會(huì)檢查傳入的隊(duì)列長(zhǎng)度和隊(duì)列元素大小是否合法。如果不合法,函數(shù)會(huì)返回NULL,表示隊(duì)列創(chuàng)建失敗。
接著,函數(shù)會(huì)為隊(duì)列分配內(nèi)存空間,包括隊(duì)列控制塊和隊(duì)列存儲(chǔ)區(qū)。隊(duì)列控制塊是一個(gè)結(jié)構(gòu)體,用于管理隊(duì)列的各種屬性和狀態(tài)信息;隊(duì)列存儲(chǔ)區(qū)是一個(gè)連續(xù)的內(nèi)存塊,用于存儲(chǔ)隊(duì)列中的元素。
然后,函數(shù)會(huì)初始化隊(duì)列控制塊的各個(gè)字段。例如,設(shè)置隊(duì)列的長(zhǎng)度、元素大小、存儲(chǔ)區(qū)的起始地址等。
接下來,函數(shù)會(huì)初始化隊(duì)列的信號(hào)量,用于實(shí)現(xiàn)隊(duì)列的同步和互斥訪問。這個(gè)信號(hào)量用于控制任務(wù)對(duì)隊(duì)列的讀取和寫入操作,確保只有一個(gè)任務(wù)在訪問隊(duì)列的時(shí)候。
最后,函數(shù)會(huì)返回創(chuàng)建的隊(duì)列的指針。如果隊(duì)列創(chuàng)建失敗,函數(shù)將返回NULL。
需要注意的是,xQueueCreate()函數(shù)只是創(chuàng)建了隊(duì)列的數(shù)據(jù)結(jié)構(gòu),并沒有分配隊(duì)列存儲(chǔ)區(qū)的內(nèi)存空間。實(shí)際的內(nèi)存分配是在調(diào)用xQueueSend()和xQueueReceive()等函數(shù)時(shí)進(jìn)行的。這是因?yàn)殛?duì)列的存儲(chǔ)區(qū)大小是根據(jù)隊(duì)列長(zhǎng)度和元素大小動(dòng)態(tài)計(jì)算的,所以需要在運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存空間。
2.往隊(duì)列寫入數(shù)據(jù)API函數(shù)(入隊(duì)):xQueueSend( )
xQueueSend()函數(shù)是一個(gè)FreeRTOS中用于往隊(duì)列寫入數(shù)據(jù)的API函數(shù),也被稱為入隊(duì)操作。它的內(nèi)部實(shí)現(xiàn)過程如下:
首先,函數(shù)會(huì)檢查傳入的隊(duì)列指針和待寫入的數(shù)據(jù)指針是否合法。如果隊(duì)列指針或數(shù)據(jù)指針為空,函數(shù)會(huì)返回一個(gè)錯(cuò)誤碼,表示寫入操作失敗。
接著,函數(shù)會(huì)嘗試獲取隊(duì)列的信號(hào)量。這是為了確保只有一個(gè)任務(wù)在訪問隊(duì)列的時(shí)候,避免多個(gè)任務(wù)同時(shí)寫入隊(duì)列導(dǎo)致數(shù)據(jù)混亂。
如果成功獲取到隊(duì)列的信號(hào)量,函數(shù)會(huì)將待寫入的數(shù)據(jù)復(fù)制到隊(duì)列的存儲(chǔ)區(qū)中。具體的復(fù)制方式取決于隊(duì)列的類型。例如,如果是一個(gè)字節(jié)隊(duì)列,直接將數(shù)據(jù)復(fù)制到存儲(chǔ)區(qū)即可;如果是一個(gè)結(jié)構(gòu)體隊(duì)列,需要按照結(jié)構(gòu)體的大小逐個(gè)成員進(jìn)行復(fù)制。
寫入數(shù)據(jù)后,函數(shù)會(huì)更新隊(duì)列的相關(guān)屬性,例如隊(duì)列中的元素?cái)?shù)量、讀取和寫入指針等。
最后,函數(shù)會(huì)釋放隊(duì)列的信號(hào)量,表示寫入操作完成。
需要注意的是,xQueueSend()函數(shù)在寫入數(shù)據(jù)時(shí),有兩種寫入模式可以選擇:阻塞模式和非阻塞模式。在阻塞模式下,如果隊(duì)列已滿,寫入操作將會(huì)阻塞當(dāng)前任務(wù),直到隊(duì)列有空閑位置可寫入;在非阻塞模式下,如果隊(duì)列已滿,寫入操作將會(huì)立即返回一個(gè)錯(cuò)誤碼,表示寫入操作失敗。這種模式由函數(shù)調(diào)用時(shí)傳入的阻塞時(shí)間參數(shù)決定。
3.從隊(duì)列讀取數(shù)據(jù)API函數(shù)(出隊(duì)): xQueueReceive( )
xQueueReceive()函數(shù)是一個(gè)FreeRTOS中用于從隊(duì)列讀取數(shù)據(jù)的API函數(shù),也被稱為出隊(duì)操作。它的內(nèi)部實(shí)現(xiàn)過程如下:
首先,函數(shù)會(huì)檢查傳入的隊(duì)列指針和接收數(shù)據(jù)的指針是否合法。如果隊(duì)列指針或接收數(shù)據(jù)的指針為空,函數(shù)會(huì)返回一個(gè)錯(cuò)誤碼,表示讀取操作失敗。
接著,函數(shù)會(huì)嘗試獲取隊(duì)列的信號(hào)量。這是為了確保只有一個(gè)任務(wù)在訪問隊(duì)列的時(shí)候,避免多個(gè)任務(wù)同時(shí)讀取隊(duì)列導(dǎo)致數(shù)據(jù)混亂。
如果成功獲取到隊(duì)列的信號(hào)量,函數(shù)會(huì)從隊(duì)列的存儲(chǔ)區(qū)中讀取數(shù)據(jù),并將讀取到的數(shù)據(jù)復(fù)制到接收數(shù)據(jù)的指針中。具體的復(fù)制方式取決于隊(duì)列的類型。例如,如果是一個(gè)字節(jié)隊(duì)列,直接從存儲(chǔ)區(qū)中讀取數(shù)據(jù)即可;如果是一個(gè)結(jié)構(gòu)體隊(duì)列,需要按照結(jié)構(gòu)體的大小逐個(gè)成員進(jìn)行復(fù)制。
讀取數(shù)據(jù)后,函數(shù)會(huì)更新隊(duì)列的相關(guān)屬性,例如隊(duì)列中的元素?cái)?shù)量、讀取和寫入指針等。
最后,函數(shù)會(huì)釋放隊(duì)列的信號(hào)量,表示讀取操作完成。
需要注意的是,xQueueReceive()函數(shù)在讀取數(shù)據(jù)時(shí),有兩種讀取模式可以選擇:阻塞模式和非阻塞模式。在阻塞模式下,如果隊(duì)列為空,讀取操作將會(huì)阻塞當(dāng)前任務(wù),直到隊(duì)列有數(shù)據(jù)可讀取;在非阻塞模式下,如果隊(duì)列為空,讀取操作將會(huì)立即返回一個(gè)錯(cuò)誤碼,表示讀取操作失敗。這種模式由函數(shù)調(diào)用時(shí)傳入的阻塞時(shí)間參數(shù)決定。