天天看點

FreeRTOS任務通知實驗 11.1 任務通知簡介 11.2 常用任務通知 API 函 數11.3整體代碼1.任務通知代替消息隊列

從 V8.2.0 版本開始,FreeRTOS 新增了任務通知這個功能,可以使用任務通 知來代替信号量、消息隊列、事件組等這些東西。使用任務通知的話效率會更高。 本章要實作的功能是:使用任務通知方式實作消息隊列、二值信号量、計數信号 量、事件标記功能實驗。本章分為如下幾部分内容: 11.1 任務通知簡介 11.2 常用任務通知 API 函數 11.3 硬體設計 11.4 軟體設計 11.5 實驗現象

11.1 任務通知簡介

FreeRTOS 從 V8.2.0 版本開始提供任務通知這個功能,每個任務都有一個 32 位的通知值,在大多數情況下,任務通知可以替代二值信号量、計數信号量、 事件組,也可以替代長度為 1 的隊列(可以儲存一個 32 位整數或指針值)。 相對于以前使用 FreeRTOS 核心通信的資源,必須建立隊列、二進制信号量、 計數信号量或事件組的情況,使用任務通知顯然更靈活。按照 FreeRTOS 官方的 說法,使用任務通知比通過信号量等 IPC 通信方式解除阻塞的任務要快 45%, 并且更加省 RAM 記憶體空間(使用 GCC 編譯器,-o2 優化級别),任務通知的使 用無需建立隊列。想要使用任務通知,必須将 FreeRTOSConfig.h 中的宏定義 configUSE_TASK_NOTIFICATIONS 設定為 1,其實 FreeRTOS 預設是為 1 的,所 以任務通知是預設使能的。 FreeRTOS 提供以下幾種方式發送通知給任務 : ●發送通知給任務,如果有通知未讀,不覆寫通知值。 ●發送通知給任務,直接覆寫通知值。 ●發送通知給任務,設定通知值的一個或者多個位,可以當做事件組來使用。 ●發送通知給任務,遞增通知值,可以當做計數信号量使用。 通過對以上任務通知方式的合理使用,可以在一定場合下替代 FreeRTOS 的 信号量,隊列、事件組等。 當然,凡是都有利弊,不然的話 FreeRTOS 還要核心的 IPC 通信機制幹嘛, 消息通知雖然處理更快,RAM 開銷更小,但也有以下限制 : ●隻能有一個任務接收通知消息,因為必須指定接收通知的任務。 ●隻有等待通知的任務可以被阻塞,發送通知的任務,在任何情況下都不會 因為發送失敗而進入阻塞态

11.2 常用任務通知 API 函 數

11.2.1.1 xTaskNotifyGive()

xTaskNotifyGive()是一個宏,宏展開是調用函數 xTaskGenericNotify( ( xTaskToNotify ), ( 0 ), eIncrement ,NULL),即向 一個任務發送通知,并将對方的任務通知值加 1。該函數可以作為二值信号量和 計數信号量的一種輕量型的實作,速度更快,在這種情況下對象任務在等待任務 通知的時候應該是使用函數 ulTaskNotifyTake() 而不是 xTaskNotifyWait()。 xTaskNotifyGive()不能在中斷裡面使用,而是使用具有中斷保護功能的 vTaskNotifyGiveFromISR()來代替。該函數的具體說明如下

FreeRTOS任務通知實驗 11.1 任務通知簡介 11.2 常用任務通知 API 函 數11.3整體代碼1.任務通知代替消息隊列

11.2.1.2 vTaskNotifyGiveFromISR()

vTaskNotifyGiveFromISR()是 vTaskNotifyGive()的中斷保護版本。用于在 中斷中向指定任務發送任務通知,并更新對方的任務通知值(加 1 操作),在 某些場景中可以替代信号量操作,因為這兩個通知都是不帶有通知值的。該函數 的具體說明如下

FreeRTOS任務通知實驗 11.1 任務通知簡介 11.2 常用任務通知 API 函 數11.3整體代碼1.任務通知代替消息隊列

從上面的函數說明我們大概知道 vTaskNotifyGiveFromISR()函數作用,每 次調用該函數都會增加任務的通知值,任務通過接收函數傳回值是否大于零,判 斷是否擷取到了通知,任務通知值初始化為 0,(如果與信号量做對比)則對應 為信号量無效。當中斷調用 vTaskNotifyGiveFromISR()通知函數給任務的時候, 任務的通知值增加,使其大于零,使其表示的通知值變為有效,任務擷取有效的 通知值将會被恢複

11.2.1.3 xTaskNotify()

FreeRTOS 每個任務都有一個 32 位的變量用于實作任務通知,在任務建立 的時候初始化為 0。這個 32 位的通知值在任務控制塊 TCB 裡面定義。 xTaskNotify()用于在任務中直接向另外一個任務發送一個事件,接收到該任務 通知的任務有可能解鎖。如果你想使用任務通知來實作二值信号量和計數信号 量,那麼應該使用更加簡單的函數 xTaskNotifyGive(),而不是使用 xTaskNotify(),xTaskNotify()函數在發送任務通知的時候會指定一個通知值, 并且使用者可以指定通知值發送的方式。 注意:該函數不能在中斷裡面使用,而是使用具體中斷保護功能的版本函數 xTaskNotifyFromISR()。xTaskNotify()函數的具體說明如下

FreeRTOS任務通知實驗 11.1 任務通知簡介 11.2 常用任務通知 API 函 數11.3整體代碼1.任務通知代替消息隊列
FreeRTOS任務通知實驗 11.1 任務通知簡介 11.2 常用任務通知 API 函 數11.3整體代碼1.任務通知代替消息隊列

11.2.1.4 xTaskNotifyFromISR( )

用于在中斷中向指定的任務發送一個任務通知,該任務通知是帶有通知值并 且使用者可以指定通知的發送方式,不傳回上一個任務在的通知值。函數的具體說 明如下

FreeRTOS任務通知實驗 11.1 任務通知簡介 11.2 常用任務通知 API 函 數11.3整體代碼1.任務通知代替消息隊列

11.2.2.1 xTaskNotifyAndQuery()

xTaskNotifyAndQuery()與 xTaskNotify()很像,都是調用通用的任務通知 發送函數 xTaskGenericNotify() 來實作通知的發送,不同的是多了一個附加的 參數 pulPreviousNotifyValue 用于回傳接收任務的上一個通知值,函數原型具 體見代碼

FreeRTOS任務通知實驗 11.1 任務通知簡介 11.2 常用任務通知 API 函 數11.3整體代碼1.任務通知代替消息隊列

11.2.2.2 xTaskNotifyAndQueryFromISR()

xTaskNotifyAndQueryFromISR()是 xTaskNotifyAndQuery()的中斷版本,用 于向指定的任務發送一個任務通知,并傳回對象任務的上一個通知值,該函數也 是一個宏定義,真正實作發送通知的是 xTaskGenericNotifyFromISR()。 xTaskNotifyAndQueryFromISR()函數說明如下。

FreeRTOS任務通知實驗 11.1 任務通知簡介 11.2 常用任務通知 API 函 數11.3整體代碼1.任務通知代替消息隊列

11.2.3 擷取任務通知函數 既然 FreeRTOS 中發送任務的函數有那麼多個,那麼任務怎麼擷取到通知 呢?我們說了,任務通知在某些場景可以替代信号量、消息隊列、事件等。擷取 任務通知函數隻能用在任務中,沒有帶中斷保護版本,是以隻有兩個 API 函數: ulTaskNotifyTake()和 xTaskNotifyWait()。前者是為代替二值信号量和計數信 号量而專門設計的,它和發送通知 API 函數 xTaskNotifyGive()、 vTaskNotifyGiveFromISR()配合使用;後者是全功能版的等待通知,可以根據不 同的參數實作輕量級二值信号量、計數信号量、事件組和長度為 1 的隊列。 所有的擷取任務通知 API 函數都帶有指定阻塞逾時時間參數,當任務因為 等待通知而進入阻塞時,用來指定任務的阻塞時間,這些逾時機制與 FreeRTOS 的消息隊列、信号量、事件等的逾時機制一緻

11.2.3.1 ulTaskNotifyTake()

ulTaskNotifyTake()作為二值信号量和計數信号量的一種輕量級實作,速度 更快。如果 FreeRTOS 中使用函數 xSemaphoreTake() 來擷取信号量,這個時候 則可以試試使用函數 ulTaskNotifyTake()來代替。 對于這個函數,任務通知值為 0,對應信号量無效,如果任務設定了阻塞等 待,任務被阻塞挂起。當其他任務或中斷發送了通知值使其不為 0 後,通知變 為有效,等待通知的任務将擷取到通知,并且在退出時候根據使用者傳遞的第一個 參數 xClearCountOnExit 選擇清零通知值或者執行減一操作。 xTaskNotifyTake()在退出的時候處理任務的通知值的時候有兩種方法,一 種是在函數退出時将通知值清零,這種方法适用于實作二值信号量;另外一種是 在函數退出時将通知值減 1,這種方法适用于實作計數信号量。當一個任務使用 其自身的任務通知值作為二值信号量或者計數信号量時,其他任務應該使用函數 xTaskNotifyGive()或者 xTaskNotify( ( xTaskToNotify ), ( 0 ),eIncrement ) 來向其發送信号量。如果是在中斷中,則應該使用他們的中斷版本函數。該函數 的具體說明如下

FreeRTOS任務通知實驗 11.1 任務通知簡介 11.2 常用任務通知 API 函 數11.3整體代碼1.任務通知代替消息隊列

11.2.3.2 xTaskNotifyWait()

xTaskNotifyWait()函數用于實作全功能版的等待任務通知,根據使用者指定 的參數的不同,可以靈活的用于實作輕量級的消息隊列隊列、二值信号量、計數 信号量和事件組功能,并帶有逾時等待。函數的具體說明如下。

FreeRTOS任務通知實驗 11.1 任務通知簡介 11.2 常用任務通知 API 函 數11.3整體代碼1.任務通知代替消息隊列

11.3整體代碼

1.任務通知代替消息隊列

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "limits.h"

//任務優先級
#define START_TASK_PRIO        1
//任務堆棧大小    
#define START_STK_SIZE         128  
//任務句柄
TaskHandle_t StartTask_Handler;
//任務函數
void start_task(void *pvParameters);

//任務優先級
#define LED1_TASK_PRIO        2
//任務堆棧大小    
#define LED1_STK_SIZE         50  
//任務句柄
TaskHandle_t LED1Task_Handler;
//任務函數
void led1_task(void *pvParameters);

//任務優先級
#define Receive1_TASK_PRIO        3
//任務堆棧大小    
#define Receive1_STK_SIZE         512  
//任務句柄
TaskHandle_t Receive1Task_Handler;
//任務函數
void Receive1_task(void *pvParameters);

//任務優先級
#define Receive2_TASK_PRIO        4
//任務堆棧大小    
#define Receive2_STK_SIZE         512  
//任務句柄
TaskHandle_t Receive2Task_Handler;
//任務函數
void Receive2_task(void *pvParameters);

//任務優先級
#define Send_TASK_PRIO        5
//任務堆棧大小    
#define Send_STK_SIZE         512  
//任務句柄
TaskHandle_t SendTask_Handler;
//任務函數
void Send_task(void *pvParameters);


#define  USE_CHAR  0  /* 測試字元串的時候配置為 1 ,測試變量配置為 0  */

/*******************************************************************************
* 函 數 名         : main
* 函數功能           : 主函數
* 輸    入         : 無
* 輸    出         : 無
*******************************************************************************/
int main()
{
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//設定系統中斷優先級分組4
    LED_Init();
    KEY_Init();
    USART1_Init(115200);
    printf("FreeRTOS任務通知代替消息隊列實驗\r\n");
    printf("按下KEY_UP或者KEY1進行任務消息通知發送 \n");
    //建立開始任務
    xTaskCreate((TaskFunction_t )start_task,            //任務函數
                (const char*    )"start_task",          //任務名稱
                (uint16_t       )START_STK_SIZE,        //任務堆棧大小
                (void*          )NULL,                  //傳遞給任務函數的參數
                (UBaseType_t    )START_TASK_PRIO,       //任務優先級
                (TaskHandle_t*  )&StartTask_Handler);   //任務句柄              
    vTaskStartScheduler();          //開啟任務排程
}

//開始任務任務函數
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //進入臨界區
                             
    //建立LED1任務
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler); 
    
    //建立Receive1任務
    xTaskCreate((TaskFunction_t )Receive1_task,     
                (const char*    )"Receive1_task",   
                (uint16_t       )Receive1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Receive1_TASK_PRIO,
                (TaskHandle_t*  )&Receive1Task_Handler);
                
    //建立Receive2任務
    xTaskCreate((TaskFunction_t )Receive2_task,     
                (const char*    )"Receive2_task",   
                (uint16_t       )Receive2_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Receive2_TASK_PRIO,
                (TaskHandle_t*  )&Receive2Task_Handler);
    
    //建立Send任務
    xTaskCreate((TaskFunction_t )Send_task,     
                (const char*    )"Send_task",   
                (uint16_t       )Send_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Send_TASK_PRIO,
                (TaskHandle_t*  )&SendTask_Handler);
            
    vTaskDelete(StartTask_Handler); //删除開始任務
    taskEXIT_CRITICAL();            //退出臨界區
} 

//LED1任務函數
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//Receive1任務函數
void Receive1_task(void *pvParameters)
{
    BaseType_t xReturn = pdTRUE;
#if USE_CHAR
  char *r_char;
#else
  uint32_t r_num;
#endif
    
    while(1)
    {
        //擷取任務通知 ,沒擷取到則一直等待
        xReturn=xTaskNotifyWait(0x0,            //進入函數的時候不清除任務bit
                                ULONG_MAX,      //退出函數的時候清除所有的bit
#if USE_CHAR
                                (uint32_t *)&r_char,          //儲存任務通知值
#else
                                &r_num,          //儲存任務通知值
#endif                        
                                portMAX_DELAY);    //阻塞時間
        if( pdTRUE == xReturn )
#if USE_CHAR
            printf("Receive1_Task 任務通知消息為 %s \n",r_char);                      
#else
            printf("Receive1_Task 任務通知消息為 %d \n",r_num);                      
#endif  
        
    }
}

//Receive2任務函數
void Receive2_task(void *pvParameters)
{
    BaseType_t xReturn = pdTRUE;
#if USE_CHAR
  char *r_char;
#else
  uint32_t r_num;
#endif
    
    while(1)
    {
        //擷取任務通知 ,沒擷取到則一直等待
        xReturn=xTaskNotifyWait(0x0,            //進入函數的時候不清除任務bit
                                ULONG_MAX,      //退出函數的時候清除所有的bit
#if USE_CHAR
                                (uint32_t *)&r_char,          //儲存任務通知值
#else
                                &r_num,          //儲存任務通知值
#endif                        
                                portMAX_DELAY);    //阻塞時間
        if( pdTRUE == xReturn )
#if USE_CHAR
            printf("Receive2_Task 任務通知消息為 %s \n",r_char);                      
#else
            printf("Receive2_Task 任務通知消息為 %d \n",r_num);                      
#endif  
        
    }
}

//Send任務函數
void Send_task(void *pvParameters)
{
    BaseType_t xReturn = pdPASS;
    u8 key=0;
#if USE_CHAR
    char test_str1[] = "this is a mail test 1";/* 郵箱消息test1 */
    char test_str2[] = "this is a mail test 2";/* 郵箱消息test2 */
#else
    uint32_t send1 = 1;
    uint32_t send2 = 2;
#endif
    
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY_UP_PRESS)
        {
            xReturn = xTaskNotify(Receive1Task_Handler, /*任務句柄*/
#if USE_CHAR 
                                (uint32_t)&test_str1, /* 發送的資料,最大為4位元組 */
#else
                                send1, /* 發送的資料,最大為4位元組 */
#endif
                                eSetValueWithOverwrite );/*覆寫目前通知*/
            if( xReturn == pdPASS )
                printf("Receive1_Task_Handle 任務通知消息發送成功!\r\n");
        }
        else if(key==KEY1_PRESS)
        {
            xReturn = xTaskNotify(Receive2Task_Handler, /*任務句柄*/
#if USE_CHAR 
                                (uint32_t)&test_str2, /* 發送的資料,最大為4位元組 */
#else
                                send2, /* 發送的資料,最大為4位元組 */
#endif
                                eSetValueWithOverwrite );/*覆寫目前通知*/
            if( xReturn == pdPASS )
                printf("Receive2_Task_Handle 任務通知消息發送成功!\r\n");
        }
        vTaskDelay(20);
    }
}
           

2.任務通知代替二值信号量

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"

//任務優先級
#define START_TASK_PRIO        1
//任務堆棧大小    
#define START_STK_SIZE         128  
//任務句柄
TaskHandle_t StartTask_Handler;
//任務函數
void start_task(void *pvParameters);

//任務優先級
#define LED1_TASK_PRIO        2
//任務堆棧大小    
#define LED1_STK_SIZE         50  
//任務句柄
TaskHandle_t LED1Task_Handler;
//任務函數
void led1_task(void *pvParameters);

//任務優先級
#define Receive1_TASK_PRIO        3
//任務堆棧大小    
#define Receive1_STK_SIZE         512  
//任務句柄
TaskHandle_t Receive1Task_Handler;
//任務函數
void Receive1_task(void *pvParameters);

//任務優先級
#define Receive2_TASK_PRIO        4
//任務堆棧大小    
#define Receive2_STK_SIZE         512  
//任務句柄
TaskHandle_t Receive2Task_Handler;
//任務函數
void Receive2_task(void *pvParameters);

//任務優先級
#define Send_TASK_PRIO        5
//任務堆棧大小    
#define Send_STK_SIZE         512  
//任務句柄
TaskHandle_t SendTask_Handler;
//任務函數
void Send_task(void *pvParameters);



/*******************************************************************************
* 函 數 名         : main
* 函數功能           : 主函數
* 輸    入         : 無
* 輸    出         : 無
*******************************************************************************/
int main()
{
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//設定系統中斷優先級分組4
    LED_Init();
    KEY_Init();
    USART1_Init(115200);
    printf("FreeRTOS任務通知代替二值信号量實驗\r\n");
    printf("按下KEY_UP或者KEY1進行任務與任務間的同步\n");
    //建立開始任務
    xTaskCreate((TaskFunction_t )start_task,            //任務函數
                (const char*    )"start_task",          //任務名稱
                (uint16_t       )START_STK_SIZE,        //任務堆棧大小
                (void*          )NULL,                  //傳遞給任務函數的參數
                (UBaseType_t    )START_TASK_PRIO,       //任務優先級
                (TaskHandle_t*  )&StartTask_Handler);   //任務句柄              
    vTaskStartScheduler();          //開啟任務排程
}

//開始任務任務函數
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //進入臨界區
                             
    //建立LED1任務
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler); 
    
    //建立Receive1任務
    xTaskCreate((TaskFunction_t )Receive1_task,     
                (const char*    )"Receive1_task",   
                (uint16_t       )Receive1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Receive1_TASK_PRIO,
                (TaskHandle_t*  )&Receive1Task_Handler);
                
    //建立Receive2任務
    xTaskCreate((TaskFunction_t )Receive2_task,     
                (const char*    )"Receive2_task",   
                (uint16_t       )Receive2_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Receive2_TASK_PRIO,
                (TaskHandle_t*  )&Receive2Task_Handler);
    
    //建立Send任務
    xTaskCreate((TaskFunction_t )Send_task,     
                (const char*    )"Send_task",   
                (uint16_t       )Send_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Send_TASK_PRIO,
                (TaskHandle_t*  )&SendTask_Handler);
            
    vTaskDelete(StartTask_Handler); //删除開始任務
    taskEXIT_CRITICAL();            //退出臨界區
} 

//LED1任務函數
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//Receive1任務函數
void Receive1_task(void *pvParameters)
{
    while(1)
    {
        //擷取任務通知 ,沒擷取到則一直等待
        ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
        printf("Receive1_Task 任務通知擷取成功!\n\n");
    }
}

//Receive2任務函數
void Receive2_task(void *pvParameters)
{    
    while(1)
    {
        //擷取任務通知 ,沒擷取到則一直等待
        ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
        printf("Receive2_Task 任務通知擷取成功!\n\n");
    }
}

//Send任務函數
void Send_task(void *pvParameters)
{
    BaseType_t xReturn = pdPASS;
    u8 key=0;
    
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY_UP_PRESS)
        {
            xReturn = xTaskNotifyGive(Receive1Task_Handler);
            if( xReturn == pdTRUE )
                printf("Receive1_Task_Handle 任務通知發送成功!\r\n");
        }
        else if(key==KEY1_PRESS)
        {
            xReturn = xTaskNotifyGive(Receive2Task_Handler);
            if( xReturn == pdTRUE )
                printf("Receive2_Task_Handle 任務通知發送成功!\r\n");
        }
        vTaskDelay(20);
    }
}
           

3.任務通知代替計數信号量

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"


//任務優先級
#define START_TASK_PRIO        1
//任務堆棧大小    
#define START_STK_SIZE         128  
//任務句柄
TaskHandle_t StartTask_Handler;
//任務函數
void start_task(void *pvParameters);

//任務優先級
#define LED1_TASK_PRIO        2
//任務堆棧大小    
#define LED1_STK_SIZE         50  
//任務句柄
TaskHandle_t LED1Task_Handler;
//任務函數
void led1_task(void *pvParameters);

//任務優先級
#define Take_TASK_PRIO        3
//任務堆棧大小    
#define Take_STK_SIZE         512  
//任務句柄
TaskHandle_t TakeTask_Handler;
//任務函數
void Take_task(void *pvParameters);

//任務優先級
#define Give_TASK_PRIO        4
//任務堆棧大小    
#define Give_STK_SIZE         512  
//任務句柄
TaskHandle_t GiveTask_Handler;
//任務函數
void Give_task(void *pvParameters);



/*******************************************************************************
* 函 數 名         : main
* 函數功能           : 主函數
* 輸    入         : 無
* 輸    出         : 無
*******************************************************************************/
int main()
{
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//設定系統中斷優先級分組4
    LED_Init();
    KEY_Init();
    USART1_Init(115200);
    printf("FreeRTOS任務通知代替計數信号量實驗\r\n");
    printf("車位預設值為0個,按下KEY1申請車位,按下KEY2釋放車位!\n\n");
    //建立開始任務
    xTaskCreate((TaskFunction_t )start_task,            //任務函數
                (const char*    )"start_task",          //任務名稱
                (uint16_t       )START_STK_SIZE,        //任務堆棧大小
                (void*          )NULL,                  //傳遞給任務函數的參數
                (UBaseType_t    )START_TASK_PRIO,       //任務優先級
                (TaskHandle_t*  )&StartTask_Handler);   //任務句柄              
    vTaskStartScheduler();          //開啟任務排程
}

//開始任務任務函數
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //進入臨界區
                             
    //建立LED1任務
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler); 
    
    //建立Take任務
    xTaskCreate((TaskFunction_t )Take_task,     
                (const char*    )"Take_task",   
                (uint16_t       )Take_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Take_TASK_PRIO,
                (TaskHandle_t*  )&TakeTask_Handler);
                
    //建立Give任務
    xTaskCreate((TaskFunction_t )Give_task,     
                (const char*    )"Give_task",   
                (uint16_t       )Give_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Give_TASK_PRIO,
                (TaskHandle_t*  )&GiveTask_Handler);
    
    vTaskDelete(StartTask_Handler); //删除開始任務
    taskEXIT_CRITICAL();            //退出臨界區
} 

//LED1任務函數
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//Give任務函數
void Give_task(void *pvParameters)
{    
    BaseType_t xReturn = pdPASS;
    u8 key=0;
    
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY2_PRESS)
        {
            xTaskNotifyGive(TakeTask_Handler);//發送任務通知
            if ( pdPASS == xReturn ) 
                printf( "KEY2被按下,釋放1個停車位。\n" );
        }
        vTaskDelay(20);
    }
}

//Take任務函數
void Take_task(void *pvParameters)
{
    uint32_t take_num = pdTRUE;
    u8 key=0;
    
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY1_PRESS)
        {
            //擷取任務通知 ,沒擷取到則不等待
            take_num=ulTaskNotifyTake(pdFALSE,0);//pdFALSE,讓值減1,有一個任務占用,就減1
            if(take_num > 0)
                printf( "KEY1被按下,成功申請到停車位,目前車位為 %d \n", take_num - 1);
            else
                printf( "KEY1被按下,車位已經沒有了,請按KEY2釋放車位\n" );  
        }
        vTaskDelay(20);
    }
}
           

4.任務通知代替事件組

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "limits.h"

//任務優先級
#define START_TASK_PRIO        1
//任務堆棧大小    
#define START_STK_SIZE         128  
//任務句柄
TaskHandle_t StartTask_Handler;
//任務函數
void start_task(void *pvParameters);

//任務優先級
#define LED1_TASK_PRIO        2
//任務堆棧大小    
#define LED1_STK_SIZE         50  
//任務句柄
TaskHandle_t LED1Task_Handler;
//任務函數
void led1_task(void *pvParameters);

//任務優先級
#define LED2_TASK_PRIO        3
//任務堆棧大小    
#define LED2_STK_SIZE         50  
//任務句柄
TaskHandle_t LED2Task_Handler;
//任務函數
void led2_task(void *pvParameters);

//任務優先級
#define KEY_TASK_PRIO        4
//任務堆棧大小    
#define KEY_STK_SIZE         512  
//任務句柄
TaskHandle_t KEYTask_Handler;
//任務函數
void key_task(void *pvParameters);


#define KEY1_EVENT  (0x01 << 0)//設定事件掩碼的位0
#define KEY2_EVENT  (0x01 << 1)//設定事件掩碼的位1



/*******************************************************************************
* 函 數 名         : main
* 函數功能           : 主函數
* 輸    入         : 無
* 輸    出         : 無
*******************************************************************************/
int main()
{
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//設定系統中斷優先級分組4
    LED_Init();
    KEY_Init();
    USART1_Init(115200);
    printf("FreeRTOS任務通知代替事件組實驗\r\n");
    printf("按下KEY1|KEY2發送任務事件通知!\n");
    //建立開始任務
    xTaskCreate((TaskFunction_t )start_task,            //任務函數
                (const char*    )"start_task",          //任務名稱
                (uint16_t       )START_STK_SIZE,        //任務堆棧大小
                (void*          )NULL,                  //傳遞給任務函數的參數
                (UBaseType_t    )START_TASK_PRIO,       //任務優先級
                (TaskHandle_t*  )&StartTask_Handler);   //任務句柄              
    vTaskStartScheduler();          //開啟任務排程
}

//開始任務任務函數
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //進入臨界區
                             
    //建立LED1任務
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler); 
    
    //建立LED2任務
    xTaskCreate((TaskFunction_t )led2_task,     
                (const char*    )"led2_task",   
                (uint16_t       )LED2_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED2_TASK_PRIO,
                (TaskHandle_t*  )&LED2Task_Handler);
                
    //建立KEY任務
    xTaskCreate((TaskFunction_t )key_task,     
                (const char*    )"key_task",   
                (uint16_t       )KEY_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )KEY_TASK_PRIO,
                (TaskHandle_t*  )&KEYTask_Handler);
    
    vTaskDelete(StartTask_Handler); //删除開始任務
    taskEXIT_CRITICAL();            //退出臨界區
} 

//LED1任務函數
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//LED2任務函數
void led2_task(void *pvParameters)
{
    BaseType_t xReturn = pdTRUE;
    uint32_t r_event = 0;  /* 定義一個事件接收變量 */
    uint32_t last_event = 0;/* 定義一個儲存事件的變量 */
    
    while(1)
    {
        //擷取任務通知 ,沒擷取到則一直等待
        xReturn = xTaskNotifyWait(0x0,            //進入函數的時候不清除任務bit
                                ULONG_MAX,      //退出函數的時候清除所有的bitR
                                &r_event,          //儲存任務通知值                    
                                portMAX_DELAY);    //阻塞時間
                        
        if( pdTRUE == xReturn )
        {
            last_event |= r_event;
            if(last_event == (KEY1_EVENT|KEY2_EVENT))
            {
                last_event=0;
                printf ( "KEY1與KEY2都按下\n");
                LED2=!LED2;
            }
            else
                last_event = r_event;
        }
    }
}

//KEY任務函數
void key_task(void *pvParameters)
{
    u8 key=0;
    
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY1_PRESS)
        {
            printf ( "KEY1被按下\n" );
            /* 觸發一個事件1 */
            xTaskNotify((TaskHandle_t    )LED2Task_Handler,//接收任務通知的任務句柄
                        (uint32_t        )KEY1_EVENT,//要觸發的事件
                        (eNotifyAction)eSetBits);//設定任務通知值中的位
        }
        else if(key==KEY2_PRESS)
        {
            printf ( "KEY2被按下\n" );    
            /* 觸發一個事件2 */
            xTaskNotify((TaskHandle_t    )LED2Task_Handler,//接收任務通知的任務句柄
                        (uint32_t        )KEY2_EVENT,//要觸發的事件
                        (eNotifyAction)eSetBits);//設定任務通知值中的位
        }
        vTaskDelay(20);
    }
}
           

繼續閱讀