freeRTOS之隊列(queue)

一.概述

1.介紹

??隊列(queue)可以用于"任務到任務"、“任務到中斷”、"中斷到任務"直接傳輸信息。

2.核心功能

線程安全:自動處理多任務訪問時的互斥問題。

數據復制:入隊時復制數據(而非引用),避免內存共享風險。

阻塞機制:當隊列滿 / 空時,任務可選擇阻塞等待。

優先級支持:高優先級任務優先獲取隊列資源。

3.關鍵屬性

固定長度:創建時需指定隊列長度(元素個數)。

固定大小:每個元素的字節數固定(如sizeof(int))。

先進先出(FIFO):默認按入隊順序出隊(也支持 LIFO)。

4.原理圖

二.隊列的基本操作

1.創建

隊列的創建有兩種方法:動態分配內存、靜態分配內存,

(1)動態分配內存:xQueueCreate,隊列的內存在函數內部動態分配

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );

(2)靜態分配內存:xQueueCreateStatic,隊列的內存要事先分配好

QueueHandle_t xQueueCreateStatic(

???????????????????????????UBaseType_t uxQueueLength,

???????????????????????????UBaseType_t uxItemSize,

???????????????????????????uint8_t *pucQueueStorageBuffer,

???????????????????????????StaticQueue_t *pxQueueBuffer

???????????????????????);

(3)示例代碼:

// 示例代碼

?#define QUEUE_LENGTH 10

?#define ITEM_SIZE sizeof( uint32_t )

?// xQueueBuffer用來保存隊列結構體

?StaticQueue_t xQueueBuffer;

?// ucQueueStorage 用來保存隊列的數據

?// 大小為:隊列長度 * 數據大小

?uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ];

?void vATask( void *pvParameters )

?{

QueueHandle_t xQueue1;

// 創建隊列: 可以容納QUEUE_LENGTH個數據,每個數據大小是ITEM_SIZE

xQueue1 = xQueueCreateStatic( QUEUE_LENGTH,

??ITEM_SIZE,

??ucQueueStorage,

??&xQueueBuffer );

?}

2.復位

隊列剛被創建時,里面沒有數據;使用過程中可以調用xQueueReset()把隊列恢復為初始狀態,此函數原型為:

/* pxQueue : 復位哪個隊列;

?* 返回值: pdPASS(必定成功)

?*/

BaseType_t xQueueReset( QueueHandle_t pxQueue);

3.刪除

刪除隊列的函數為vQueueDelete(),只能刪除使用動態方法創建的隊列,它會釋放內存。原型如下:

void vQueueDelete( QueueHandle_t xQueue );

4.寫隊列

可以把數據寫到隊列頭部,也可以寫到尾部,這些函數有兩個版本:在任務中使用、在ISR中使用。函數原型如下:

/* 等同于xQueueSendToBack

?* 往隊列尾部寫入數據,如果沒有空間,阻塞時間為xTicksToWait

?*/

BaseType_t xQueueSend(

????????????????????????????????QueueHandle_t ???xQueue,

????????????????????????????????const void ??????*pvItemToQueue,

????????????????????????????????TickType_t ??????xTicksToWait

????????????????????????????);

/*

?* 往隊列尾部寫入數據,如果沒有空間,阻塞時間為xTicksToWait

?*/

BaseType_t xQueueSendToBack(

????????????????????????????????QueueHandle_t ???xQueue,

????????????????????????????????const void ??????*pvItemToQueue,

????????????????????????????????TickType_t ??????xTicksToWait

????????????????????????????);

/*

?* 往隊列尾部寫入數據,此函數可以在中斷函數中使用,不可阻塞

?*/

BaseType_t xQueueSendToBackFromISR(

??????????????????????????????????????QueueHandle_t xQueue,

??????????????????????????????????????const void *pvItemToQueue,

??????????????????????????????????????BaseType_t *pxHigherPriorityTaskWoken

???????????????????????????????????);

/*

?* 往隊列頭部寫入數據,如果沒有空間,阻塞時間為xTicksToWait

?*/

BaseType_t xQueueSendToFront(

????????????????????????????????QueueHandle_t ???xQueue,

????????????????????????????????const void ??????*pvItemToQueue,

????????????????????????????????TickType_t ??????xTicksToWait

????????????????????????????);

/*

?* 往隊列頭部寫入數據,此函數可以在中斷函數中使用,不可阻塞

?*/

BaseType_t xQueueSendToFrontFromISR(

??????????????????????????????????????QueueHandle_t xQueue,

??????????????????????????????????????const void *pvItemToQueue,

??????????????????????????????????????BaseType_t *pxHigherPriorityTaskWoken

???????????????????????????????????);

5 讀隊列

使用xQueueReceive()函數讀隊列,讀到一個數據后,隊列中該數據會被移除。這個函數有兩個版本:在任務中使用、在ISR中使用。函數原型如下:

BaseType_t xQueueReceive( QueueHandle_t xQueue,

??????????????????????????void * const pvBuffer,

??????????????????????????TickType_t xTicksToWait );

BaseType_t xQueueReceiveFromISR(

????????????????????????????????????QueueHandle_t ???xQueue,

????????????????????????????????????void ????????????*pvBuffer,

????????????????????????????????????BaseType_t ??????*pxTaskWoken

????????????????????????????????);

6.查詢

可以查詢隊列中有多少個數據、有多少空余空間。函數原型如下:

/*

?* 返回隊列中可用數據的個數

?*/

UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );

/*

?* 返回隊列中可用空間的個數

?*/

UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );

7.覆蓋/偷看

當隊列長度為1時,可以使用xQueueOverwrite()或xQueueOverwriteFromISR()來覆蓋數據。

注意,隊列長度必須為1。當隊列滿時,這些函數會覆蓋里面的數據,這也以為著這些函數不會被阻塞。

函數原型如下:

/* 覆蓋隊列

?* xQueue: 寫哪個隊列

?* pvItemToQueue: 數據地址

?* 返回值: pdTRUE表示成功, pdFALSE表示失敗

?*/

BaseType_t xQueueOverwrite(

???????????????????????????QueueHandle_t xQueue,

???????????????????????????const void * pvItemToQueue

??????????????????????);

BaseType_t xQueueOverwriteFromISR(

???????????????????????????QueueHandle_t xQueue,

???????????????????????????const void * pvItemToQueue,

???????????????????????????BaseType_t *pxHigherPriorityTaskWoken

??????????????????????);

8.隊列的基本使用

本程序會創建一個隊列,然后創建2個發送任務、1個接收任務:

發送任務優先級為1,分別往隊列中寫入100、200

接收任務優先級為2,讀隊列、打印數值

(1)main函數中創建的隊列、創建了發送任務、接收任務,代碼如下:

/* 隊列句柄, 創建隊列時會設置這個變量 */

QueueHandle_t xQueue;

int main( void )

{

prvSetupHardware();

????/* 創建隊列: 長度為5,數據大小為4字節(存放一個整數) */

????xQueue = xQueueCreate( 5, sizeof( int32_t ) );

if( xQueue != NULL )

{

/* 創建2個任務用于寫隊列, 傳入的參數分別是100、200

?* 任務函數會連續執行,向隊列發送數值100、200

?* 優先級為1

?*/

xTaskCreate( vSenderTask, "Sender1", 1000, ( void * ) 100, 1, NULL );

xTaskCreate( vSenderTask, "Sender2", 1000, ( void * ) 200, 1, NULL );

/* 創建1個任務用于讀隊列

?* 優先級為2, 高于上面的兩個任務

?* 這意味著隊列一有數據就會被讀走

?*/

xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );

/* 啟動調度器 */

vTaskStartScheduler();

}

else

{

/* 無法創建隊列 */

}

/* 如果程序運行到了這里就表示出錯了, 一般是內存不足 */

return 0;

}

(2)發送任務的函數中,不斷往隊列中寫入數值,代碼如下:

static void vSenderTask( void *pvParameters )

{

int32_t lValueToSend;

BaseType_t xStatus;

/* 我們會使用這個函數創建2個任務

?* 這些任務的pvParameters不一樣

? ?*/

lValueToSend = ( int32_t ) pvParameters;

/* 無限循環 */

for( ;; )

{

/* 寫隊列

?* xQueue: 寫哪個隊列

?* &lValueToSend: 寫什么數據? 傳入數據的地址, 會從這個地址把數據復制進隊列

?* 0: 不阻塞, 如果隊列滿的話, 寫入失敗, 立刻返回

?*/

xStatus = xQueueSendToBack( xQueue, &lValueToSend, 0 );

if( xStatus != pdPASS )

{

printf( "Could not send to the queue.\r\n" );

}

}

}

(3)接收任務的函數中,讀取隊列、判斷返回值、打印,代碼如下:

static void vReceiverTask( void *pvParameters )

{

/* 讀取隊列時, 用這個變量來存放數據 */

int32_t lReceivedValue;

BaseType_t xStatus;

const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );

/* 無限循環 */

for( ;; )

{

/* 讀隊列

?* xQueue: 讀哪個隊列

?* &lReceivedValue: 讀到的數據復制到這個地址

?* xTicksToWait: 如果隊列為空, 阻塞一會

?*/

xStatus = xQueueReceive( xQueue, &lReceivedValue, xTicksToWait );

if( xStatus == pdPASS )

{

/* 讀到了數據 */

printf( "Received = %d\r\n", lReceivedValue );

}

else

{

/* 沒讀到數據 */

printf( "Could not receive from the queue.\r\n" );

}

}

}

三.郵箱

1.FreeRTOS的郵箱概念跟別的RTOS不一樣,這里的郵箱稱為"櫥窗"也許更恰當:

它是一個隊列,隊列長度只有1;

寫郵箱:新數據覆蓋舊數據,在任務中使用xQueueOverwrite(),在中斷中使用xQueueOverwriteFromISR()。

既然是覆蓋,那么無論郵箱中是否有數據,這些函數總能成功寫入數據。

讀郵箱:讀數據時,數據不會被移除;在任務中使用xQueuePeek(),在中斷中使用xQueuePeekFromISR()。

這意味著,第一次調用時會因為無數據而阻塞,一旦曾經寫入數據,以后讀郵箱時總能成功。

2.代碼示例

main函數中創建了隊列(隊列長度為1)、創建了發送任務、接收任務:

發送任務的優先級為2,它先執行

接收任務的優先級為1

代碼如下:

/* 隊列句柄, 創建隊列時會設置這個變量 */

QueueHandle_t xQueue;

int main( void )

{

prvSetupHardware();

????/* 創建隊列: 長度為1,數據大小為4字節(存放一個char指針) */

????xQueue = xQueueCreate( 1, sizeof(uint32_t) );

if( xQueue != NULL )

{

/* 創建1個任務用于寫隊列

?* 任務函數會連續執行,構造buffer數據,把buffer地址寫入隊列

?* 優先級為2

?*/

xTaskCreate( vSenderTask, "Sender", 1000, NULL, 2, NULL );

/* 創建1個任務用于讀隊列

?* 優先級為1

?*/

xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, NULL );

/* 啟動調度器 */

vTaskStartScheduler();

}

else

{

/* 無法創建隊列 */

}

/* 如果程序運行到了這里就表示出錯了, 一般是內存不足 */

return 0;

}

發送任務、接收任務的代碼和執行流程如下:

運行結果如下圖所示:

四.隊列與其他 FreeRTOS 對象的對比

五.隊列的性能考慮

1.內存開銷:

隊列本身的控制結構(約 40 字節)。

數據緩沖區(長度 × 元素大小)。

3.復制開銷:

入隊 / 出隊時復制數據,大元素(如結構體)會影響性能。

優化方案:傳遞指針而非完整數據(需確保內存安全)。

3.阻塞喚醒開銷:

任務從阻塞到就緒的上下文切換成本。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/909729.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/909729.shtml
英文地址,請注明出處:http://en.pswp.cn/news/909729.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【python】typing用法

一、基礎類型提示 1. 基本類型注解 # 變量類型注解 age: int 30 name: str "Alice" is_student: bool False height: float 1.752. 函數注解 def greet(name: str, age: int) -> str:return f"Hello {name}, you are {age} years old!"二、組合類…

web前端開發核心基礎:Html結構分析,head,body,不同標簽的作用

前端技術協同關系 協作流程:HTML構建頁面框架—>css美化樣式(選擇器屬性)—>JavaScript實現交互(類似于python的腳本語言)擴展基礎:在上面三項基礎上學習Vue\React、構建工具WePack和瀏覽器工作原理…

精益數據分析(105/126):移動應用核心指標解析與用戶分層營收策略

精益數據分析(105/126):移動應用核心指標解析與用戶分層營收策略 在移動應用市場競爭白熱化的今天,單純追求下載量已無法保證商業成功,精細化運營核心指標成為盈利關鍵。本文將深入解析每日活躍用戶平均營收&#xff…

被CC攻擊了,對服務器有什么影響?

博客正文: 最近,不少網站管理員和運維人員反映遭遇了CC攻擊,導致服務器性能異常甚至癱瘓。那么,CC攻擊究竟會對服務器造成哪些影響?本文將為你簡要解析CC攻擊的原理及其帶來的危害,幫助你更好地理解并應對…

Tensorflow安裝出現dependency conflict錯誤

Python版本: 3.11.4 pip版本已升到最新 電腦上有mac的原裝Python2.x,我裝的3.11.4,還有個什么依賴的3.9 運行 pip3 install tensorflow 出現類似以下錯誤 (我報錯的是另一個不是tensorflow—estimator,但基本就是…

2025年HTTP半開與錯誤攻擊防御指南:原理拆解與實戰防護

你以為限流就能防住HTTP攻擊?黑客用協議畸形包AI調度正在撕裂傳統防線! 一、HTTP半開攻擊:慢速絞殺服務器資源 ? 攻擊原理剖析 HTTP半開攻擊(如Slowloris)是一種應用層DoS攻擊,通過建立大量半開連接耗盡…

Mybatis(XML映射文件、動態SQL)

目錄 基礎操作 準備: 刪除: 新增: 更新: 查詢: 條件查詢: XML映射文件 動態SQL if foreach sql&include 基礎操作 準備: 準備數據庫表 創建一個新的springboot工程&#xff0…

python校園拼團系統

目錄 技術棧介紹具體實現截圖系統設計研究方法:設計步驟設計流程核心代碼部分展示研究方法詳細視頻演示試驗方案論文大綱源碼獲取/詳細視頻演示 技術棧介紹 Django-SpringBoot-php-Node.js-flask 本課題的研究方法和研究步驟基本合理,難度適中&#xf…

多模態大語言模型arxiv論文略讀(127)

When SAM2 Meets Video Camouflaged Object Segmentation: A Comprehensive Evaluation and Adaptation ?? 論文標題:When SAM2 Meets Video Camouflaged Object Segmentation: A Comprehensive Evaluation and Adaptation ?? 論文作者:Yuli Zhou, …

劍指offer32_二叉搜索樹的后序遍歷序列

二叉搜索樹的后序遍歷序列 輸入一個整數數組,判斷該數組是不是某二叉搜索樹的后序遍歷的結果。 如果是則返回true,否則返回false。 假設輸入的數組的任意兩個數字都互不相同。 數據范圍 數組長度 [ 0 , 1000 ] [0,1000] [0,1000]。 樣例 輸入&…

《仿盒馬》app開發技術分享-- 訂單結合優惠券結算(端云一體)

技術棧 Appgallery connect 開發準備 上一節我們已經實現了優惠券的選擇,并且成功的把券后的價格也展示給用戶,不能使用的優惠券我們也用友好的方式告知用戶,這一節我們來實現優惠券內容的下一步,優惠券內容結合訂單進行結算提…

Python+Selenium+Pytest+POM自動化測試框架封裝

🍅 點擊文末小卡片 ,免費獲取軟件測試全套資料,資料在手,漲薪更快 1、測試框架簡介 1)測試框架的優點 代碼復用率高,如果不使用框架的話,代碼會顯得很冗余。可以組裝日志、報告、郵件等一些…

宋代大模型:智能重構下的文明再發現

引言:當汴京城遇見生成式AI 一幅動態的《清明上河圖》正通過全息投影技術演繹汴京城的市井百態。這個虛實交融的場景,恰似宋代大模型技術的隱喻——以人工智能為紐帶,連接起東京夢華的繁盛圖景與數字時代的文明重構。作為人工智能與歷史學交…

K-means++:讓K-means“聰明”地選擇初始中心點

大家好!歡迎來到我的技術分享博客~ 👋 在前兩篇博客中,我們深入探討了經典的 K-means 算法 以及它的優化方案 Canopy K-means。如果你還沒有看過,強烈建議先回顧一下,因為今天的主題 K-means 和它們有著千絲萬縷的聯系…

Langchain學習筆記(1)——如何調用Huggingface的模型并實現實時返回生成結果

Langchain支持很方便的OpenAI模型的調用,可以做到快速開發大模型應用。但是要使用Huggingface上的開源模型就沒有那么方便了,本文就詳細闡述如何用Langchain開發基于Huggingface上的模型,并實時返回生成結果。 實時返回生成結果是LLM很關鍵的…

Java安全-常規漏洞問題(SQL注入,XXE,SSRF,RCE)

靶場搭建 靶場下載 : https://github.com/whgojp/JavaSecLab這個靶場是使用Springboot搭建的所以不要下載 jar 文件運行,要使用IDEA運行他的文件夾 先打開pom 然后進行maven一下 改一下端口 配置完成之后修改一下 運行的模式 使用phpstudy搞一個sql數…

基于視頻的 AI 內存庫,極速語義檢索

簡介 在大模型應用里,將文本數據分塊嵌入存儲在向量數據庫已經是標準做法。然而,傳統向量數據庫雖然功能強大,但其高昂的RAM和存儲需求,以及復雜的部署運維,常常讓開發者望而卻步。今天,介紹一個名為 Memv…

接口適配器模式實現令牌桶算法和漏桶算法

以下是令牌桶算法、漏桶算法和雪花算法的清晰對比解析。它們屬于完全不同的技術領域,前兩者用于流量控制,后者用于分布式ID生成: 1. 令牌桶算法(Token Bucket) 領域:流量整形 / 速率限制核心目標&#xff…

618背后的電商邏輯重構:從價格血戰到價值共生

“今年終于沒做數學題。” 618進行到一半,行云已經買了很多,大件的有iPad、iWatch,小件的有運動鞋、面膜、紙巾。往年她要湊湊減減,經常要找個店鋪湊單,下完單再馬上退掉,今年她沒廢太多腦細胞&#xff0c…

解決 PyTorch 與 Python 3.12 的兼容性問題:`operator torchvision::nms does not exist` 深度解析

解決 PyTorch 與 Python 3.12 的兼容性問題 問題現象錯誤根源分析終極解決方案?? 推薦方案:創建 Python 3.11 虛擬環境? 備選方案:使用 PyTorch 夜間構建版(Python 3.12)驗證修復技術深度解析最佳實踐建議問題現象 當在 Python 3.12 環境中運行以下代碼時: from tran…