從 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()來代替。該函數的具體說明如下
11.2.1.2 vTaskNotifyGiveFromISR()
vTaskNotifyGiveFromISR()是 vTaskNotifyGive()的中斷保護版本。用于在 中斷中向指定任務發送任務通知,并更新對方的任務通知值(加 1 操作),在 某些場景中可以替代信号量操作,因為這兩個通知都是不帶有通知值的。該函數 的具體說明如下
從上面的函數說明我們大概知道 vTaskNotifyGiveFromISR()函數作用,每 次調用該函數都會增加任務的通知值,任務通過接收函數傳回值是否大于零,判 斷是否擷取到了通知,任務通知值初始化為 0,(如果與信号量做對比)則對應 為信号量無效。當中斷調用 vTaskNotifyGiveFromISR()通知函數給任務的時候, 任務的通知值增加,使其大于零,使其表示的通知值變為有效,任務擷取有效的 通知值将會被恢複
11.2.1.3 xTaskNotify()
FreeRTOS 每個任務都有一個 32 位的變量用于實作任務通知,在任務建立 的時候初始化為 0。這個 32 位的通知值在任務控制塊 TCB 裡面定義。 xTaskNotify()用于在任務中直接向另外一個任務發送一個事件,接收到該任務 通知的任務有可能解鎖。如果你想使用任務通知來實作二值信号量和計數信号 量,那麼應該使用更加簡單的函數 xTaskNotifyGive(),而不是使用 xTaskNotify(),xTaskNotify()函數在發送任務通知的時候會指定一個通知值, 并且使用者可以指定通知值發送的方式。 注意:該函數不能在中斷裡面使用,而是使用具體中斷保護功能的版本函數 xTaskNotifyFromISR()。xTaskNotify()函數的具體說明如下
11.2.1.4 xTaskNotifyFromISR( )
用于在中斷中向指定的任務發送一個任務通知,該任務通知是帶有通知值并 且使用者可以指定通知的發送方式,不傳回上一個任務在的通知值。函數的具體說 明如下
11.2.2.1 xTaskNotifyAndQuery()
xTaskNotifyAndQuery()與 xTaskNotify()很像,都是調用通用的任務通知 發送函數 xTaskGenericNotify() 來實作通知的發送,不同的是多了一個附加的 參數 pulPreviousNotifyValue 用于回傳接收任務的上一個通知值,函數原型具 體見代碼
11.2.2.2 xTaskNotifyAndQueryFromISR()
xTaskNotifyAndQueryFromISR()是 xTaskNotifyAndQuery()的中斷版本,用 于向指定的任務發送一個任務通知,并傳回對象任務的上一個通知值,該函數也 是一個宏定義,真正實作發送通知的是 xTaskGenericNotifyFromISR()。 xTaskNotifyAndQueryFromISR()函數說明如下。
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 ) 來向其發送信号量。如果是在中斷中,則應該使用他們的中斷版本函數。該函數 的具體說明如下
11.2.3.2 xTaskNotifyWait()
xTaskNotifyWait()函數用于實作全功能版的等待任務通知,根據使用者指定 的參數的不同,可以靈活的用于實作輕量級的消息隊列隊列、二值信号量、計數 信号量和事件組功能,并帶有逾時等待。函數的具體說明如下。
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);
}
}