天天看點

【FreeRTOS】FreeRTOS隊列

使用RTOS 需要注意的問題:像中斷優先級、任務堆棧配置設定、可重入等,都是更容易出錯的地方。

讀隊列時阻塞

當某個任務試圖讀一個隊列時,其可以指定一個阻塞逾時時間。在這段時間中,如

果隊列為空,該任務将保持阻塞狀态以等待隊列資料有效。當其它任務或中斷服務例程

往其等待的隊列中寫入了資料,該任務将自動由阻塞态轉移為就緒态。當等待的時間超

過了指定的阻塞時間,即使隊列中尚無有效資料,任務也會自動從阻塞态轉移為就緒态。

由于隊列可以被多個任務讀取,是以對單個隊列而言,也可能有多個任務處于阻塞

狀态以等待隊列資料有效。這種情況下,一旦隊列資料有效,隻會有一個任務會被解除

阻塞,這個任務就是所有等待任務中優先級最高的任務。而如果所有等待任務的優先級

相同,那麼被解除阻塞的任務将是等待最久的任務。

寫隊列時阻塞

同讀隊列一樣,任務也可以在寫隊列時指定一個阻塞逾時時間。這個時間是當被寫

隊列已滿時,任務進入阻塞态以等待隊列空間有效的最長時間。

由于隊列可以被多個任務寫入,是以對單個隊列而言,也可能有多個任務處于阻塞

狀态以等待隊列空間有效。這種情況下,一旦隊列空間有效,隻會有一個任務會被解除

【FreeRTOS】FreeRTOS隊列

2.3 使用隊列

xQueueCreate() API 函數

隊列在使用前必須先被建立。

隊列由聲明為 xQueueHandle 的變量進行引用。 xQueueCreate()用于建立一個隊

列,并傳回一個 xQueueHandle 句柄以便于對其建立的隊列進行引用。

當建立隊列時, FreeRTOS 從堆空間中配置設定記憶體空間。配置設定的空間用于存儲隊列數

據結構本身以及隊列中包含的資料單元。如果記憶體堆中沒有足夠的空間來建立隊列,

xQueueCreate()将傳回 NULL。

【FreeRTOS】FreeRTOS隊列

 xQueueSendToBack()和xQueueSendToFront()

xQueueSendToBack()用于将資料發送到隊列尾;

xQueueSendToFront()用于将資料發送到隊列首。

xQueueSend()完全等同于 xQueueSendToBack()。

但 切 記 不 要 在 中 斷 服 務 例 程 中 調 用 xQueueSendToFront() 或xQueueSendToBack()。

系統提供中斷安全版本的 xQueueSendToFrontFromISR()與xQueueSendToBackFromISR()用于在中斷服務中實作相同的功能。

【FreeRTOS】FreeRTOS隊列
【FreeRTOS】FreeRTOS隊列

xQueueReceive()與 xQueuePeek() 

xQueueReceive()用于從隊列中接收(讀取)資料單元。接收到的單元同時會從隊列中删除。

xQueuePeek()也是從從隊列中接收資料單元,不同的是并不從隊列中删出接收到的單元。

xQueuePeek()從隊列首接收到資料後,不會修改隊列中的資料,也不會改變資料在隊列中的存儲序順。

切記不要在中斷服務例程中調用 xQueueRceive()和 xQueuePeek()。

【FreeRTOS】FreeRTOS隊列
【FreeRTOS】FreeRTOS隊列
【FreeRTOS】FreeRTOS隊列

uxQueueMessagesWaiting() 

uxQueueMessagesWaiting()用于查詢隊列中目前有效資料單元個數。

切記不要在中斷服務例程中調用 uxQueueMessagesWaiting()。應當在中斷服務中

使用其中斷安全版本 uxQueueMessagesWaitingFromISR()。

【FreeRTOS】FreeRTOS隊列

本章節為大家講解 FreeRTOS 的一個重要的通信機制----消息隊列,初學者要熟練掌握,因為消息隊

列在實際項目中應用較多。

消息隊列的概念及其作用

消息隊列就是通過 RTOS 核心提供的服務,任務或中斷服務子程式可以将一個消息(注意,FreeRTOS

消息隊列傳遞的是實際資料,并不是資料位址,RTX,uCOS-II 和 uCOS-III 是傳遞的位址)放入到隊列。

同樣,一個或者多個任務可以通過 RTOS 核心服務從隊列中得到消息。通常,先進入消息隊列的消息先傳

給任務,也就是說,任務先得到的是最先進入到消息隊列的消息,即先進先出的原則(FIFO),FreeRTOS

的消息隊列支援 FIFO 和 LIFO 兩種資料存取方式。

也許有不了解的初學者會問采用消息隊列多麻煩,搞個全局數組不是更簡單,其實不然。在裸機程式設計

時,使用全局數組的确比較友善,但是在加上 RTOS 後就是另一種情況了。 相比消息隊列,使用全局數組

主要有如下四個問題:

 使用消息隊列可以讓 RTOS 核心有效地管理任務,而全局數組是無法做到的,任務的逾時等機制需要使用者自己去實作。

 使用了全局數組就要防止多任務的通路沖突,而使用消息隊列則處理好了這個問題,使用者無需擔心。

 使用消息隊列可以有效地解決中斷服務程式與任務之間消息傳遞的問題。

 FIFO 機制更有利于資料的處理。

FreeRTOS 任務間消息隊列的實作

任務間消息隊列的實作是指各個任務之間使用消息隊列實作任務間的通信。 下面我們通過如下的框圖

來說明一下 FreeRTOS 消息隊列的實作,讓大家有一個形象的認識。

【FreeRTOS】FreeRTOS隊列

運作條件:

 建立消息隊列,可以存放 10 個消息。

 建立 2 個任務 Task1 和 Task2,任務 Task1 向消息隊列放資料,任務 Task2 從消息隊列取資料。

 FreeRTOS 的消息存取采用 FIFO 方式。

運作過程主要有以下兩種情況:

 任務 Task1 向消息隊列放資料,任務 Task2 從消息隊列取資料,如果放資料的速度快于取資料的速

度,那麼會出現消息隊列存放滿的情況,FreeRTOS 的消息存放函數 xQueueSend 支援逾時等待,

使用者可以設定逾時等待,直到有空間可以存放消息或者設定的逾時時間溢出。

 任務 Task1 向消息隊列放資料,任務 Task2 從消息隊列取資料,如果放資料的速度慢于取資料的速

度,那麼會出現消息隊列為空的情況,FreeRTOS 的消息擷取函數 xQueueReceive 支援逾時等待,

使用者可以設定逾時等待,直到消息隊列中有消息或者設定的逾時時間溢出。

FreeRTOS 中斷方式消息隊列的實作

FreeRTOS 中斷方式消息隊列的實作是指中斷函數和 FreeRTOS 任務之間使用消息隊列。 下面我們通

過如下的框圖來說明一下 FreeRTOS 消息隊列的實作,讓大家有一個形象的認識。

【FreeRTOS】FreeRTOS隊列

 建立 1 個任務 Task1 和一個序列槽接收中斷。

 中斷服務程式向消息隊列放資料,任務 Task1 從消息隊列取資料,如果放資料的速度快于取資料的速

度,那麼會出現消息隊列存放滿的情況。由于中斷服務程式裡面的消息隊列發送函數

xQueueSendFromISR 不支援逾時設定,是以發送前要通過函數 xQueueIsQueueFullFromISR 檢測

消息隊列是否滿。

 中斷服務程式向消息隊列放資料,任務 Task1 從消息隊列取資料,如果放資料的速度慢于取資料的速

度,那麼會出現消息隊列存為空的情況。在 FreeRTOS 的任務中可以通過函數 xQueueReceive 擷取

消息,因為此函數可以設定逾時等待,直到消息隊列中有消息存放或者設定的逾時時間溢出。

上面就是一個簡單的 FreeRTOS 中斷方式消息隊列通信過程。 實際應用中,中斷方式的消息機制要注意以

下四個問題:

 中斷函數的執行時間越短越好,防止其它低于這個中斷優先級的異常不能得到及時響應。

 實際應用中,建議不要在中斷中實作消息處理,使用者可以在中斷服務程式裡面發送消息通知任務,在

任務中實作消息處理,這樣可以有效地保證中斷服務程式的實時響應。同時此任務也需要設定為高優

先級,以便退出中斷函數後任務可以得到及時執行。

 中斷服務程式中一定要調用專用于中斷的消息隊列函數,即以 FromISR 結尾的函數。

 在作業系統中實作中斷服務程式與裸機程式設計的差別。

 如果 FreeRTOS 工程的中斷函數中沒有調用 FreeRTOS 的消息隊列 API 函數,與裸機程式設計是一

樣的。

 如果 FreeRTOS 工程的中斷函數中調用了 FreeRTOS 的消息隊列的 API 函數,退出的時候要檢

測是否有高優先級任務就緒,如果有就緒的,需要在退出中斷後進行任務切換,這點與裸機程式設計

稍有差別,詳見 20.4 小節實驗例程說明(中斷方式):

 另外強烈推薦使用者将 Cortex-M3 核心的 STM32F103 和 Cortex-M4 核心的 STM32F407, F429

的 NVIC 優先級分組設定為 4,即:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);這樣中

斷優先級的管理将非常友善。

 使用者要在 FreeRTOS 多任務開啟前就設定好優先級分組,一旦設定好切記不可再修改。

消息隊列 API 函數

使用如下 23 個函數可以實作 FreeRTOS 的消息隊列:

 xQueueCreateStatic()

 vQueueDelete()

 xQueueSend()

 xQueueSendFromISR()

 xQueueSendToBack()

 xQueueSendToBackFromISR()

 xQueueSendToFront()

 xQueueSendToFrontFromISR()

 xQueueReceive()

 xQueueReceiveFromISR()

 uxQueueMessagesWaiting()

 uxQueueMessagesWaitingFromISR()

 uxQueueSpacesAvailable()

 xQueueReset()

 xQueueOverwrite()

 xQueueOverwriteFromISR()

 xQueuePeek()

 xQueuePeekFromISR()

 vQueueAddToRegistry()

 vQueueUnregisterQueue()

 pcQueueGetName()

 xQueueIsQueueFullFromISR()

 xQueueIsQueueEmptyFromISR()

關于這 23 個函數的講解及其使用方法可以看 FreeRTOS 線上版手冊 。

這裡我們重點的說以下 4 個函數:

 xQueueCreate ()

 xQueueSend ()

 xQueueSendFromISR ()

 xQueueReceive ()

因為本章節配套的例子使用的是這 4 個函數。

函數 xQueueCreate 

【FreeRTOS】FreeRTOS隊列

函數描述:

函數 xQueueCreate 用于建立消息隊列。

 第 1 個參數是消息隊列支援的消息個數。

 第 2 個參數是每個消息的大小,機關位元組。

 傳回值, 如果建立成功會傳回消息隊列的句柄,如果由于 FreeRTOSConfig.h 檔案中 heap 大小不足,

無法為此消息隊列提供所需的空間會傳回 NULL。

使用這個函數要注意以下問題:

1. FreeRTOS 的消息傳遞是資料的複制,而不是傳遞的資料位址,這點要特别注意。 每一次傳遞都是

uxItemSize 個位元組。

【FreeRTOS】FreeRTOS隊列

函數 xQueueSend

【FreeRTOS】FreeRTOS隊列

函數 xQueueSend 用于任務中消息發送。

 第 1 個參數是消息隊列句柄。

 第 2 個參數要傳遞資料位址, 每次發送都是将消息隊列建立函數 xQueueCreate 所指定的單個消息大

小複制到消息隊列空間中。

 第 3 個參數是當消息隊列已經滿時,等待消息隊列有空間時的最大等待時間,機關系統時鐘節拍。

 傳回值,如果消息成功發送傳回 pdTRUE,否則傳回 errQUEUE_FULL。

1. FreeRTOS 的消息傳遞是資料的複制,而不是傳遞的資料位址。

2. 此函數是用于任務代碼中調用的,故不可以在中斷服務程式中調用此函數,中斷服務程式中使用的是

xQueueSendFromISR。

3. 如果消息隊列已經滿且第三個參數為 0,那麼此函數會立即傳回。

4. 如果使用者将 FreeRTOSConfig.h 檔案中的宏定義 INCLUDE_vTaskSuspend 配置為 1 且第三個參數配

置為 portMAX_DELAY,那麼此發送函數會永久等待直到消息隊列有空間可以使用。

5. 消息隊列還有兩個函數 xQueueSendToBack 和 xQueueSendToFront,函數 xQueueSendToBack

實作的是 FIFO 方式的存取,函數 xQueueSendToFront 實作的是 LIFO 方式的讀寫。我們這裡說的函

數 xQueueSend 等效于 xQueueSendToBack,即實作的是 FIFO 方式的存取。

函數 xQueueSendFromISR

【FreeRTOS】FreeRTOS隊列

函數 xQueueSendFromISR 用于中斷服務程式中消息發送。

 第 3 個參數用于儲存是否有高優先級任務準備就緒。如果函數執行完畢後,此參數的數值是 pdTRUE,

說明有高優先級任務要執行,否則沒有。

1. FreeRTOS 的消息傳遞是資料的複制,而不是傳遞的資料位址。 正因為這個原因,使用者在建立消息隊

列時單個消息大小不可太大,因為一定程度上面會增加中斷服務程式的執行時間。

2. 此函數是用于中斷服務程式中調用的,故不可以在任務代碼中調用此函數,任務代碼中使用的是

xQueueSend。

3. 消息隊列還有兩個函數 xQueueSendToBackFromISR 和 xQueueSendToFrontFromISR,函數

xQueueSendToBackFromISR 實作的是 FIFO 方式的存取,函數 xQueueSendToFrontFromISR 實

現的是 LIFO 方式的讀寫。我們這裡說的函數 xQueueSendFromISR 等效于

xQueueSendToBackFromISR,即實作的是 FIFO 方式的存取。

【FreeRTOS】FreeRTOS隊列
【FreeRTOS】FreeRTOS隊列

 函數 xQueueReceive

【FreeRTOS】FreeRTOS隊列

函數 xQueueReceive 用于接收消息隊列中的資料。

 第 2 個參數是從消息隊列中複制出資料後所儲存的緩沖位址,緩沖區空間要大于等于消息隊列建立函

數 xQueueCreate 所指定的單個消息大小,否則取出的資料無法全部存儲到緩沖區,進而造成記憶體溢

出。

 第 3 個參數是消息隊列為空時,等待消息隊列有資料的最大等待時間,機關系統時鐘節拍。

 傳回值,如果接到到消息傳回 pdTRUE,否則傳回 pdFALSE。

1. 此函數是用于任務代碼中調用的,故不可以在中斷服務程式中調用此函數,中斷服務程式使用的是

xQueueReceiveFromISR。

2. 如果消息隊列為空且第三個參數為 0,那麼此函數會立即傳回。

3. 如果使用者将 FreeRTOSConfig.h 檔案中的宏定義 INCLUDE_vTaskSuspend 配置為 1 且第三個參數配

置為 portMAX_DELAY,那麼此函數會永久等待直到消息隊列有資料。

STM32F429 開發闆實驗 ,關鍵代碼實作及解釋:

【FreeRTOS】FreeRTOS隊列
static void AppObjCreate (void)
{
    /* 建立10個uint8_t型消息隊列 */
    xQueue1 = xQueueCreate(10, sizeof(uint8_t));
    if( xQueue1 == 0 )
    {
        /* 沒有建立成功,使用者可以在這裡加入建立失敗的處理機制 */
    }

    /* 建立10個存儲指針變量的消息隊列,由于CM3/CM4核心是32位機,一個指針變量占用4個位元組 */
    xQueue2 = xQueueCreate(10, sizeof(struct Msg *));
    if( xQueue2 == 0 )
    {
        /* 沒有建立成功,使用者可以在這裡加入建立失敗的處理機制 */
    }
}      
【FreeRTOS】FreeRTOS隊列
【FreeRTOS】FreeRTOS隊列
static void vTaskWork(void *pvParameters)
{

        MSG_T   *ptMsg;
    uint8_t ucCount = 0;

    /* 初始化結構體指針 */
    ptMsg = &g_tMsg;

    /* 初始化數組 */
    ptMsg->ucMessageID = 0;
    ptMsg->ulData[0] = 0;
    ptMsg->usData[0] = 0;

    while(1)
    {

        if (key1_flag==1)
        {
            key1_flag=0;

                    ucCount++;

                    /* 向消息隊列發資料,如果消息隊列滿了,等待10個時鐘節拍 */
                    if( xQueueSend(xQueue1,
                                   (void *) &ucCount,
                                   (TickType_t)10) != pdPASS )
                    {
                        /* 發送失敗,即使等待了10個時鐘節拍 */
                        printf("K1鍵按下,向xQueue1發送資料失敗,即使等待了10個時鐘節拍\r\n");
                    }
                    else
                    {
                        /* 發送成功 */
                        printf("K1鍵按下,向xQueue1發送資料成功\r\n");                        
                    }
        }
        if(key2_flag==1)
        {
            key2_flag=0;
                    ptMsg->ucMessageID++;
                    ptMsg->ulData[0]++;;
                    ptMsg->usData[0]++;

                    /* 使用消息隊列實作指針變量的傳遞 */
                    if(xQueueSend(xQueue2,                  /* 消息隊列句柄 */
                                 (void *) &ptMsg,           /* 發送結構體指針變量ptMsg的位址 */
                                 (TickType_t)10) != pdPASS )
                    {
                        /* 發送失敗,即使等待了10個時鐘節拍 */
                        printf("K2鍵按下,向xQueue2發送資料失敗,即使等待了10個時鐘節拍\r\n");
                    }
                    else
                    {
                        /* 發送成功 */
                        printf("K2鍵按下,向xQueue2發送資料成功\r\n");                        
                    }

        }


        vTaskDelay(20);
    }
}      
【FreeRTOS】FreeRTOS隊列
【FreeRTOS】FreeRTOS隊列
void vTaskBeep(void *pvParameters)
{
    BaseType_t xResult;
    const TickType_t xMaxBlockTime = pdMS_TO_TICKS(3000); /* 設定最大等待時間為300ms */
    uint8_t ucQueueMsgValue;

    while(1)
    {
        xResult = xQueueReceive(xQueue1,                   /* 消息隊列句柄 */
                                (void *)&ucQueueMsgValue,  /* 存儲接收到的資料到變量ucQueueMsgValue中 */
                                (TickType_t)xMaxBlockTime);/* 設定阻塞時間 */

        if(xResult == pdPASS)
        {
            /* 成功接收,并通過序列槽将資料列印出來 */
            printf("接收到消息隊列資料ucQueueMsgValue = %d\r\n", ucQueueMsgValue);
        }
        else
        {
            /* 逾時 */
            BEEP_TOGGLE; 
        }
    }



}      
【FreeRTOS】FreeRTOS隊列
【FreeRTOS】FreeRTOS隊列
void vTaskLed1(void *pvParameters)
{
    MSG_T *ptMsg;
    BaseType_t xResult;
    const TickType_t xMaxBlockTime = pdMS_TO_TICKS(2000); /* 設定最大等待時間為200ms */

    while(1)
    {
            xResult = xQueueReceive(xQueue2,                   /* 消息隊列句柄 */
                                                            (void *)&ptMsg,             /* 這裡擷取的是結構體的位址 */
                                                            (TickType_t)xMaxBlockTime);/* 設定阻塞時間 */


            if(xResult == pdPASS)
            {
                /* 成功接收,并通過序列槽将資料列印出來 */
                printf("接收到消息隊列資料ptMsg->ucMessageID = %d\r\n", ptMsg->ucMessageID);
                printf("接收到消息隊列資料ptMsg->ulData[0] = %d\r\n", ptMsg->ulData[0]);
                printf("接收到消息隊列資料ptMsg->usData[0] = %d\r\n", ptMsg->usData[0]);
            }
            else
            {
                    LED2_TOGGLE;
            }
    }
}      
【FreeRTOS】FreeRTOS隊列

 實驗通過AppObjCreate函數建立兩個隊列消息,容量都是10個消息,隊列1,2分别為uint8_t和struct Msg *類型,按鍵K1,實作隊列1一個計數的增加,然後在Beep任務中接收這個變化的值,任務2實作結構體元素的增加,在LED任務中接收這個增量并列印出來。需要說明的是,freertos消息隊列是通過副本機制傳遞的,而不是引用,

【FreeRTOS】FreeRTOS隊列

我們檢視底層實作,

【FreeRTOS】FreeRTOS隊列

 freertos通過使用memcpy複制的内容。以簡單的資料元素為例:

uint8_t ucCount = 0;

xQueueSend(xQueue1,(void *) &ucCount,(TickType_t)10)

這裡是發送隊列消息函數,下面看接收:

uint8_t ucQueueMsgValue;

xQueueReceive(xQueue1, /* 消息隊列句柄 */

(void *)&ucQueueMsgValue, /* 存儲接收到的資料到變量ucQueueMsgValue中 */

(TickType_t)xMaxBlockTime)

這裡是最簡單的uint_8類型元素,要想把發送函數的uint_8定義的資料,包括該資料在發送函數之前被更改後的值發送給接收函數,我們需要傳遞給發送函數send一個uint_8定義資料的位址,這樣可以通過位址傳遞到memcpy函數,實作複制,這也就是為什麼上面說的freertos的消息隊列不是引用而是複制,要是引用的話,可以直接傳這個uint_8類型的資料,而我們此時在freertos作業系統上,是副本傳遞,通過memcpy,是以需要給uint_8類型資料的位址。

這個或許并不具有什麼迷惑性,但是,官方的參考demo,要是不認真了解一下,是想不通的。

在本次實驗中傳遞一個結構體就是官方的參考曆程:

發送函數:

MSG_T   *ptMsg;//MSG是個結構體

ptMsg = &g_tMsg;//g_tMsg是一個結構實體而且是全局區定義的

/* 初始化數組 */

ptMsg->ucMessageID = 0;

ptMsg->ulData[0] = 0;

ptMsg->usData[0] = 0;

xQueueSend(xQueue2, /* 消息隊列句柄 */

(void *) &ptMsg, /* 發送結構體指針變量ptMsg的位址 */

(TickType_t)10)

 接收函數:

MSG_T *ptMsg;

xQueueReceive(xQueue2, /* 消息隊列句柄 */

(void *)&ptMsg, /* 這裡擷取的是結構體的位址 */

(TickType_t)xMaxBlockTime);/* 設定阻塞時間 */

這裡的關鍵就在第二個參數ptMsg,它已經是指針了,為什麼還要取位址,這樣不是一個二級指針了嗎,而它的參數是void *,給人的感覺應該就是傳一個位址,雖然二級指針也是位址,但是總覺得不應該設計成二級指針指派給一個一級指針,哪怕你是void*。但是我們既然使用了freertos,就要遵循别人的設計,别人這樣做,肯定有自己的道理,我們做到熟練應用即可。試想,消息發送函數,要發送資料,要得到這個資料的位址以給memcopy,如果傳遞的資料本身就是位址(指針),那麼我們要把這個位址傳到接收函數去,就應該得到此時指針的位址才行,也就是傳遞一個指針的值,注意不是指針指向的值。關鍵我們要通過memcpy函數,傳遞一個指針的值通過memcpy必然是需要二級指針的,這樣才可以操作一級指針的值,這樣也就可以了解為什麼ptMsg已經是指針了,卻還是要傳遞ptMsg的位址,因為隻有這樣,才可以通過memcpy函數把ptMsg指針的值給到接收函數的指針,這樣在接收函數中操作這個結構體類型的指針,就可以得到發送端的資料。這樣做的好處是,避免了大資料的拷貝,隻拷貝指針,提高了效率,但是使用指針,一定不要在棧空間開辟,這也是為什麼我們定義g_tMsg結構體實體在全局區。但是freertos任務中一直有while(1),元素生命周期一直都在,此時還是可以使用局部變量做資料傳遞工具,但是這樣的程式設計模式應該摒棄,我們采用全局區開辟的空間。更多參見下一篇随筆。

那麼你可能會問了,那我直接給指針ptMsg看看行不行呢,不給指針的位址即&ptMsg。答案是肯定的,不行。給ptMsg,相當于把ptMsg指向的資料給了接收端,而freertos要求是的,你傳一個你想要發送消息的位址,我們想要發送的消息是ptMsg,它的位址是&ptMsg,是以我們必須傳遞&ptMsg。并不能簡單看類型是否完全貼切,要看源碼内部實作,畢竟強制類型轉換太霸道。

再者,你還是覺得這樣很詫異,那麼你可以使用結構,而不要使用結構體指針,這樣你傳遞的時候就是這個結構的指針了。但是注意,使用結構本身不使用結構體指針的時候,建立消息隊列裡面的siezof要改成結構體而不再是上面的結構體指針:

xQueue2 = xQueueCreate(10, sizeof(struct Msg ));

當然後面的->操作,要改成 . 操作。

實驗現象如下:

【FreeRTOS】FreeRTOS隊列

 最後說兩句:

【FreeRTOS】FreeRTOS隊列

而我測試了,深度給1,但我發送兩個消息,程式還是可以工作,(并不是我給隊列深度為1,就隻能有一個隊列消息發送函數)這和發送接收的允許阻塞時間有關。

【FreeRTOS】FreeRTOS隊列

是以,在等待時間合适的情況下,深度隻給1,還是可以發送多次的。