FreeRTOS消息隊列


FreeRTOS 的一個重要的通信機制----消息隊列,消息隊列在實際項目中應用較多。

一.消息隊列的作用及概念:

    消息隊列就是通過 RTOS 內核提供的服務,任務或中斷服務子程序可以將一個消息(注意,FreeRTOS消息隊列傳遞的是實際數據,並不是數據地址,RTX,uCOS-II 和 uCOS-III 是傳遞的地址)放入到隊列。同樣,一個或者多個任務可以通過 RTOS 內核服務從隊列中得到消息。通常,先進入消息隊列的消息先傳給任務,也就是說,任務先得到的是最先進入到消息隊列的消息,即先進先出的原則(FIFO),FreeRTOS的消息隊列支持 FIFO 和 LIFO 兩種數據存取方式。

    也許有不理解的初學者會問采用消息隊列多麻煩,搞個全局數組不是更簡單,其實不然。在裸機編程時,使用全局數組的確比較方便,但是在加上 RTOS 后就是另一種情況了。相比消息隊列,使用全局數組主要有如下四個問題:

  1. 使用消息隊列可以讓 RTOS 內核有效地管理任務,而全局數組是無法做到的,任務的超時等機制需要用戶自己去實現。
  2. 使用了全局數組就要防止多任務的訪問沖突,而使用消息隊列則處理好了這個問題,用戶無需擔心。
  3. 使用消息隊列可以有效地解決中斷服務程序與任務之間消息傳遞的問題。
  4. FIFO 機制更有利於數據的處理。

 

二. FreeRTOS 任務間消息隊列的實現:

    任務間消息隊列的實現是指各個任務之間使用消息隊列實現任務間的通信。下面我們通過如下的框圖來說明一下 FreeRTOS 消息隊列的實現,讓大家有一個形象的認識。

    運行條件:

  1. 創建消息隊列,可以存放 10 個消息。
  2. 創建 2 個任務 Task1 和 Task2,任務 Task1 向消息隊列放數據,任務 Task2 從消息隊列取數據。
  3. FreeRTOS 的消息存取采用 FIFO 方式。 

   

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

  1. 任務 Task1 向消息隊列放數據,任務 Task2 從消息隊列取數據,如果放數據的速度快於取數據的速度,那么會出現消息隊列存放滿的情況,FreeRTOS 的消息存放函數 xQueueSend 支持超時等待,用戶可以設置超時等待,直到有空間可以存放消息或者設置的超時時間溢出。
  2. 任務 Task1 向消息隊列放數據,任務 Task2 從消息隊列取數據,如果放數據的速度慢於取數據的速度,那么會出現消息隊列為空的情況,FreeRTOS 的消息獲取函數 xQueueReceive 支持超時等待,用戶可以設置超時等待,直到消息隊列中有消息或者設置的超時時間溢出。

  上面就是一個簡單的 FreeRTOS 任務間消息隊列通信過程。

 

三.FreeRTOS 中斷方式消息隊列的實現:

    FreeRTOS 中斷方式消息隊列的實現是指中斷函數和 FreeRTOS 任務之間使用消息隊列。下面我們通過如下的框圖來說明一下 FreeRTOS 消息隊列的實現,讓大家有一個形象的認識。

    運行條件:

  1. 創建消息隊列,可以存放 10 個消息。
  2. 創建 1 個任務 Task1 和一個串口接收中斷。
  3. FreeRTOS 的消息存取采用 FIFO 方式。

 

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

  1. 中斷服務程序向消息隊列放數據,任務 Task1 從消息隊列取數據,如果放數據的速度快於取數據的速度,那么會出現消息隊列存放滿的情況。由於中斷服務程序里面的消息隊列發送函數xQueueSendFromISR 不支持超時設置,所以發送前要通過函數 xQueueIsQueueFullFromISR 檢測消息隊列是否滿。
  2. 中斷服務程序向消息隊列放數據,任務 Task1 從消息隊列取數據,如果放數據的速度慢於取數據的速度,那么會出現消息隊列存為空的情況。在 FreeRTOS 的任務中可以通過函數 xQueueReceive 獲取消息,因為此函數可以設置超時等待,直到消息隊列中有消息存放或者設置的超時時間溢出。

 

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

  1. 中斷函數的執行時間越短越好,防止其它低於這個中斷優先級的異常不能得到及時響應。
  2. 實際應用中,建議不要在中斷中實現消息處理,用戶可以在中斷服務程序里面發送消息通知任務,在任務中實現消息處理,這樣可以有效地保證中斷服務程序的實時響應。同時此任務也需要設置為高優先級,以便退出中斷函數后任務可以得到及時執行。
  3. 中斷服務程序中一定要調用專用於中斷的消息隊列函數,即以 FromISR 結尾的函數。
  4. 在操作系統中實現中斷服務程序與裸機編程的區別。
    • 如果 FreeRTOS 工程的中斷函數中沒有調用 FreeRTOS 的消息隊列 API 函數,與裸機編程是一樣的。
    • 如果 FreeRTOS 工程的中斷函數中調用了 FreeRTOS 的消息隊列的 API 函數,退出的時候要檢測是否有高優先級任務就緒,如果有就緒的,需要在退出中斷后進行任務切換,這點與裸機編程稍有區別,詳見 實驗例程說明(中斷方式):
    • 另外強烈推薦用戶將 Cortex-M3 內核的 STM32F103 和 Cortex-M4 內核的 STM32F407,F429的 NVIC 優先級分組設置為 4,即:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);這樣中斷優先級的管理將非常方便。
    • 用戶要在 FreeRTOS 多任務開啟前就設置好優先級分組,一旦設置好切記不可再修改。

 

四.消息隊列API函數,使用如下 23 個函數可以實現 FreeRTOS 的消息隊列:

  1.  xQueueCreateStatic()
  2.  vQueueDelete()
  3.  xQueueSend()
  4.  xQueueSendFromISR()
  5.  xQueueSendToBack()
  6.  xQueueSendToBackFromISR()
  7.  xQueueSendToFront()
  8.  xQueueSendToFrontFromISR()
  9.  xQueueReceive()
  10.  xQueueReceiveFromISR()
  11.  uxQueueMessagesWaiting()
  12.  uxQueueMessagesWaitingFromISR()
  13.  uxQueueSpacesAvailable()
  14.  xQueueReset()
  15.  xQueueOverwrite()
  16.  xQueueOverwriteFromISR()
  17.  xQueuePeek()
  18.  xQueuePeekFromISR()
  19.  vQueueAddToRegistry()
  20.  vQueueUnregisterQueue()
  21.  pcQueueGetName()
  22.  xQueueIsQueueFullFromISR()
  23.  xQueueIsQueueEmptyFromISR()

 

    關於這 23 個函數的講解及其使用方法可以看 FreeRTOS 在線版手冊,這里重點的說以下 4 個函數:

  • xQueueCreate ()
  • xQueueSend ()
  • xQueueSendFromISR ()
  • xQueueReceive ()

 

    1.1 函 數 xQueueCreate

    函數原型:
    QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, /* 消息個數 */
               UBaseType_t uxItemSize ); /* 每個消息大小,單位字節 */


    函數描述: 函數 xQueueCreate 用於創建消息隊列。

  •  第 1 個參數是消息隊列支持的消息個數。
  •  第 2 個參數是每個消息的大小,單位字節。
  • 返回值,如果創建成功會返回消息隊列的句柄,如果由於 FreeRTOSConfig.h 文件中 heap 大小不足,無法為此消息隊列提供所需的空間會返回 NULL。

   

    使用這個函數要注意以下問題:FreeRTOS 的消息傳遞是數據的復制,而不是傳遞的數據地址,這點要特別注意。每一次傳遞都是uxItemSize 個字節。

    使用舉例:

static QueueHandle_t xQueue1 = NULL;
static QueueHandle_t xQueue2 = NULL;
/*
*********************************************************************************************************
*  函 數 名: AppObjCreate
*  功能說明: 創建任務通信機制
*  形 參: 無
*  返 回 值: 無
*********************************************************************************************************
*/
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 )
  {
    /* 沒有創建成功,用戶可以在這里加入創建失敗的處理機制 */
  }
}

 

 

    1.2函 數 xQueueSend

    函數原型:
    BaseType_t xQueueSend(
            QueueHandle_t xQueue, /* 消息隊列句柄 */
            const void * pvItemToQueue, /* 要傳遞數據地址 */
            TickType_t xTicksToWait /* 等待消息隊列有空間的最大等待時間 */
            );


    函數描述:
    函數 xQueueSend 用於任務中消息發送。

  • 第 1 個參數是消息隊列句柄。
  • 第 2 個參數要傳遞數據地址,每次發送都是將消息隊列創建函數 xQueueCreate 所指定的單個消息大小復制到消息隊列空間中。 
  • 第 3 個參數是當消息隊列已經滿時,等待消息隊列有空間時的最大等待時間,單位系統時鍾節拍。
  • 返回值,如果消息成功發送返回 pdTRUE,否則返回 errQUEUE_FULL。 

   

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

  • FreeRTOS 的消息傳遞是數據的復制,而不是傳遞的數據地址。
  • 此函數是用於任務代碼中調用的,故不可以在中斷服務程序中調用此函數,中斷服務程序中使用的是xQueueSendFromISR。
  • 如果消息隊列已經滿且第三個參數為 0,那么此函數會立即返回。
  • 如果用戶將 FreeRTOSConfig.h 文件中的宏定義 INCLUDE_vTaskSuspend 配置為 1 且第三個參數配置為 portMAX_DELAY,那么此發送函數會永久等待直到消息隊列有空間可以使用。
  • 消息隊列還有兩個函數 xQueueSendToBack 和 xQueueSendToFront,函數 xQueueSendToBack實現的是 FIFO 方式的存取,函數 xQueueSendToFront 實現的是 LIFO 方式的讀寫。我們這里說的函數 xQueueSend 等效於 xQueueSendToBack,即實現的是 FIFO 方式的存取。

 

    使用舉例:

static QueueHandle_t xQueue1 = NULL;
static QueueHandle_t xQueue2 = NULL;
typedef struct Msg
{
  uint8_t ucMessageID;
  uint16_t usData[2];
  uint32_t ulData[2];
}MSG_T;

MSG_T g_tMsg; /* 定義一個結構體用於消息隊列 */
/*
*********************************************************************************************************
*    函 數 名: vTaskTaskUserIF
*    功能說明: 接口消息處理。
*    形    參: pvParameters 是在創建該任務時傳遞的形參
*    返 回 值: 無
*   優 先 級: 1  (數值越小優先級越低,這個跟uCOS相反)
*********************************************************************************************************
*/
static void vTaskTaskUserIF(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(ucKeyCode == 1)
            {
                        
                    ucCount++;
                
                    /* 向消息隊列發數據,如果消息隊列滿了,等待10個時鍾節拍 */
                    if( xQueueSend(xQueue1,
                                   (void *) &ucCount,
                                   (TickType_t)10) != pdPASS )
                    {
                        /* 發送失敗,即使等待了10個時鍾節拍 */
                        printf("K1鍵按下,向xQueue1發送數據失敗,即使等待了10個時鍾節拍\r\n");
                    }
                    else
                    {
                        /* 發送成功 */
                        printf("K1鍵按下,向xQueue1發送數據成功\r\n");                        
                    }
                    
                    ucKeyCode = 0;
                }
                    
                    /* K2鍵按下 啟動單次定時器中斷,50ms后在定時器中斷將任務vTaskLED恢復 */
                if(ucKeyCode == 2)
                {    
                    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");                        
                    }
                        
                        ucKeyCode = 0;
                }
                
            vTaskDelay(20);            
        }                
        
}

   

 

    1.3函 數 xQueueSendFromISR

    函數原型:
    BaseType_t xQueueSendFromISR
               (
                QueueHandle_t xQueue, /* 消息隊列句柄 */
                const void *pvItemToQueue, /* 要傳遞數據地址 */
                BaseType_t *pxHigherPriorityTaskWoken /* 高優先級任務是否被喚醒的狀態保存 */
               );


    函數描述:
    函數 xQueueSendFromISR 用於中斷服務程序中消息發送。

  • 第 1 個參數是消息隊列句柄。
  • 第 2 個參數要傳遞數據地址,每次發送都是將消息隊列創建函數 xQueueCreate 所指定的單個消息大小復制到消息隊列空間中。
  • 第3個參數用於保存是否有高優先級任務准備就緒。如果函數執行完畢后,此參數的數值是pdTRUE,說明有高優先級任務要執行,否則沒有。
  • 返回值,如果消息成功發送返回 pdTRUE,否則返回 errQUEUE_FULL。

   

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

  • FreeRTOS 的消息傳遞是數據的復制,而不是傳遞的數據地址。正因為這個原因,用戶在創建消息隊列時單個消息大小不可太大,因為一定程度上面會增加中斷服務程序的執行時間。
  • 此函數是用於中斷服務程序中調用的,故不可以在任務代碼中調用此函數,任務代碼中使用的是xQueueSend。
  • 消息隊列還有兩個函數 xQueueSendToBackFromISR 和 xQueueSendToFrontFromISR,函數xQueueSendToBackFromISR 實現的是 FIFO 方式的存取,函數 xQueueSendToFrontFromISR 實現的是 LIFO 方式的讀寫。我們這里說的函數 xQueueSendFromISR 等效於xQueueSendToBackFromISR,即實現的是 FIFO 方式的存取。

    使用舉例:

void  BASIC_TIMx_IRQHandler(void)
{
    if(1 == temp)
    {
        TIM_ClearITPendingBit(BASIC_TIMx , TIM_IT_Update);
        
        TIM_ITConfig(BASIC_TIMx,TIM_IT_Update,DISABLE);
        
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
        g_uiCount++;
    
        /* 向消息隊列發數據 */
        xQueueSendFromISR(xQueue1,
                                (void *)&g_uiCount,
                                &xHigherPriorityTaskWoken);

        /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中斷后切到當前最高優先級任務執行 */
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);    
        
    }
    if(2 == temp)
    {
        TIM_ClearITPendingBit(BASIC_TIMx , TIM_IT_Update);
        
        TIM_ITConfig(BASIC_TIMx,TIM_IT_Update,DISABLE);
        
        MSG_T   *ptMsg;
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        
        /* 初始化結構體指針 */
        ptMsg = &g_tMsg;
        
        /* 初始化數組 */
        ptMsg->ucMessageID++;
        ptMsg->ulData[0]++;
        ptMsg->usData[0]++;

        /* 向消息隊列發數據 */
        xQueueSendFromISR(xQueue2,
                                (void *)&ptMsg,
                                 &xHigherPriorityTaskWoken);

        /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中斷后切到當前最高優先級任務執行 */
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);            
        
    }

}

 

 

    1.4函 數 xQueueReceive

    函數原型:
    BaseType_t xQueueReceive(
             QueueHandle_t xQueue, /* 消息隊列句柄 */
             void *pvBuffer, /* 接收消息隊列數據的緩沖地址 */
             TickType_t xTicksToWait /* 等待消息隊列有數據的最大等待時間 */
             );


    函數描述:
    函數 xQueueReceive 用於接收消息隊列中的數據。

  • 第 1 個參數是消息隊列句柄。
  • 第 2 個參數是從消息隊列中復制出數據后所儲存的緩沖地址,緩沖區空間要大於等於消息隊列創建函數 xQueueCreate 所指定的單個消息大小,否則取出的數據無法全部存儲到緩沖區,從而造成內存溢出。
  • 第 3 個參數是消息隊列為空時,等待消息隊列有數據的最大等待時間,單位系統時鍾節拍。
  • 返回值,如果接收到消息返回 pdTRUE,否則返回 pdFALSE。

   

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

  • 此函數是用於任務代碼中調用的,故不可以在中斷服務程序中調用此函數,中斷服務程序使用的是xQueueReceiveFromISR。
  • 如果消息隊列為空且第三個參數為 0,那么此函數會立即返回。
  • 如果用戶將 FreeRTOSConfig.h 文件中的宏定義 INCLUDE_vTaskSuspend 配置為 1 且第三個參數配置為 portMAX_DELAY,那么此函數會永久等待直到消息隊列有數據。

    使用舉例:

/*
*********************************************************************************************************
*    函 數 名: vTaskMsgPro
*    功能說明: 消息處理,使用函xQueueReceive接收任務vTaskTaskUserIF消息隊列中的數據。
*    形    參: pvParameters 是在創建該任務時傳遞的形參
*    返 回 值: 無
*   優 先 級: 3  
*********************************************************************************************************
*/
static void vTaskMsgPro(void *pvParameters)
{
    BaseType_t xResult;
    const TickType_t xMaxBlockTime = pdMS_TO_TICKS(300); /* 設置最大等待時間為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;            
        }
    }
}
void vTaskLed1(void *pvParameters)
{
    
    MSG_T *ptMsg;
    BaseType_t xResult;
    const TickType_t xMaxBlockTime = pdMS_TO_TICKS(200); /* 設置最大等待時間為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
        {
            /* 超時 */
            LED1_TOGGLE;            
        }
    
    }    
}

     實驗通過AppObjCreate函數創建兩個隊列消息,容量都是10個消息,隊列1,2分別為uint8_t和struct Msg *類型,按鍵K1,實現隊列1一個計數的增加,然后在Beep任務中接收這個變化的值,任務2實現結構體元素的增加,在LED任務中接收這個增量並打印出來。需要說明的是,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 ));

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

 

 

原文:地址未知


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM