石獅建設局網站網絡營銷比較成功的企業(yè)
文章目錄
- 目的
- 基礎說明
- 使用演示
- 作為二進制信號量
- 作為計數(shù)信號量
- 作為事件組
- 作為隊列或郵箱
- 相關函數(shù)
- 總結
目的
任務通知(TaskNotify)是RTOS中相對常用的用于任務間交互的功能,這篇文章將對相關內容做個介紹。
本文代碼測試環(huán)境見前面的文章:《FreeRTOS入門(01):基礎說明與使用演示》
基礎說明
前面介紹的隊列、信號量、互斥量、隊列集、事件組等功能都需要有個獨立于任務的對象,任務通過主動去訪問對象來使用相關的功能。事實上目前FreeRTOS的任務句柄本身就帶有一個對象,用于任務間交互使用,這就是任務通知。
任務通知因為上面的原因有兩大優(yōu)勢:一是輕量(因為不需要額外的對象);二是可以直接通知某個任務(也是因為沒有中間商)。
任務中的任務通知結構包含狀態(tài)和一個32位的數(shù)據(jù)。FreeRTOS v10.4.0 起支持單任務多條通知( 任務通知數(shù)組 ),所以現(xiàn)在很多函數(shù)都是因為兼容性冗余存在的。使用任務通知時需要設置下面參數(shù):
configUSE_TASK_NOTIFICATIONS // 為1才能使用任務通知(默認為1)
configTASK_NOTIFICATION_ARRAY_ENTRIES // 為任務通知的每個任務數(shù)組中的索引數(shù)量(默認為1)
任務通知根據(jù)使用的不同可以當作二進制信號量、計數(shù)信號量、事件組、隊列、郵箱等功能來使用。
使用演示
作為二進制信號量
任務通知作為二進制信號量使用就和真正的信號量一樣,使用 give
和 take
函數(shù)來操作:
#include "debug.h"
#include "FreeRTOS.h" // 引入頭文件
#include "task.h" // 引入頭文件TaskHandle_t Task1_Handler; // 任務句柄
TaskHandle_t Task2_Handler; // 任務句柄void task1(void *pvParameters) {while(1) {uint32_t data = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // 等待任務通知(注意第一個參數(shù))printf("%u task1: %u\r\n", xTaskGetTickCount(), data); // 打印任務通知的值vTaskDelete(NULL);}
}void task2(void *pvParameters) {while(1) {vTaskDelay(500);xTaskNotifyGive(Task1_Handler); // 向task1給出任務通知vTaskDelete(NULL);}
}int main(void) {NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);SystemCoreClockUpdate();Delay_Init();USART_Printf_Init(115200);xTaskCreate(task1, "task1", 256, NULL, 5, &Task1_Handler);xTaskCreate(task2, "task2", 256, NULL, 5, &Task2_Handler);vTaskStartScheduler(); // 任務調度,任務將在這里根據(jù)情況開始運行,程序將在這里無序循環(huán)while(1) {} // 程序不會運行到這里
}
作為計數(shù)信號量
任務通知作為計數(shù)信號量使用和作為二進制信號量一樣,使用 give
和 take
函數(shù),唯一的區(qū)別就是 take
函數(shù)的參數(shù)不同:
#include "debug.h"
#include "FreeRTOS.h" // 引入頭文件
#include "task.h" // 引入頭文件TaskHandle_t Task1_Handler; // 任務句柄
TaskHandle_t Task2_Handler; // 任務句柄void task1(void *pvParameters) {vTaskDelay(500);while(1) {uint32_t data = ulTaskNotifyTake(pdFALSE, portMAX_DELAY); // 等待任務通知(注意第一個參數(shù))printf("%u task1: %u\r\n", xTaskGetTickCount(), data); // 打印任務通知的值}
}void task2(void *pvParameters) {while(1) {xTaskNotifyGive(Task1_Handler); // 向task1給出任務通知xTaskNotifyGive(Task1_Handler); // 向task1給出任務通知xTaskNotifyGive(Task1_Handler); // 向task1給出任務通知vTaskDelete(NULL);}
}int main(void) {NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);SystemCoreClockUpdate();Delay_Init();USART_Printf_Init(115200);xTaskCreate(task1, "task1", 256, NULL, 5, &Task1_Handler);xTaskCreate(task2, "task2", 256, NULL, 5, &Task2_Handler);vTaskStartScheduler(); // 任務調度,任務將在這里根據(jù)情況開始運行,程序將在這里無序循環(huán)while(1) {} // 程序不會運行到這里
}
作為事件組
任務通知作為事件組使用時,任務通知的 notify
函數(shù)相當于事件組的 setBits
函數(shù),任務通知的 wait
函數(shù)相當于事件組的 waitBits
函數(shù)。
#include "debug.h"
#include "FreeRTOS.h" // 引入頭文件
#include "task.h" // 引入頭文件TaskHandle_t Task1_Handler; // 任務句柄void task1(void *pvParameters) {while(1) {uint32_t value = 0;BaseType_t ret = xTaskNotifyWait(0xfffffffa, 0, &value, portMAX_DELAY); // 在等待前清除bit2和bit0之外的值,事件觸發(fā)后不清除任何位if (ret == pdPASS ) {printf("%u task1: %u\r\n", xTaskGetTickCount(), value); // 打印任務通知的值}}
}void task2(void *pvParameters) {while(1) {vTaskDelay(500);xTaskNotify(Task1_Handler, 0b0100, eSetBits ); // bit2設置為1vTaskDelete(NULL);}
}void task3(void *pvParameters) {while(1) {vTaskDelay(1000);xTaskNotify(Task1_Handler, 0b0001, eSetBits ); // bit0設置為1vTaskDelete(NULL);}
}int main(void) {NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);SystemCoreClockUpdate();Delay_Init();USART_Printf_Init(115200);xTaskCreate(task1, "task1", 256, NULL, 5, &Task1_Handler);xTaskCreate(task2, "task2", 256, NULL, 5, NULL);xTaskCreate(task3, "task3", 256, NULL, 5, NULL);vTaskStartScheduler(); // 任務調度,任務將在這里根據(jù)情況開始運行,程序將在這里無序循環(huán)while(1) {} // 程序不會運行到這里
}
作為隊列或郵箱
任務通知不管是作為隊列還是作為郵箱使用都相當于一個長度只有 1
的隊列,使用的是 notify
和 setBits
函數(shù),對這兩個參數(shù)使用不同的方式會相當于不同的功能。
#include "debug.h"
#include "FreeRTOS.h" // 引入頭文件
#include "task.h" // 引入頭文件TaskHandle_t Task1_Handler; // 任務句柄void task1(void *pvParameters) {while(1) {uint32_t value = 0;BaseType_t ret = xTaskNotifyWait(0xffffffff, 0, &value, portMAX_DELAY); // 事件觸發(fā)后清除數(shù)據(jù)相當于隊列
// BaseType_t ret = xTaskNotifyWait(0xffffffff, 0xffffffff, &value, portMAX_DELAY); // 事件觸發(fā)后不清除數(shù)據(jù)相當于郵箱if (ret == pdPASS ) {printf("%u task1: %u\r\n", xTaskGetTickCount(), value); // 打印任務通知的值}}
}void task2(void *pvParameters) {while(1) {vTaskDelay(500);xTaskNotify(Task1_Handler, 233, eSetValueWithoutOverwrite); // 寫入數(shù)據(jù)// eSetValueWithoutOverwrite相當于隊列,eSetValueWithOverwrite相當于郵箱}
}int main(void) {NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);SystemCoreClockUpdate();Delay_Init();USART_Printf_Init(115200);xTaskCreate(task1, "task1", 256, NULL, 5, &Task1_Handler);xTaskCreate(task2, "task2", 256, NULL, 5, NULL);vTaskStartScheduler(); // 任務調度,任務將在這里根據(jù)情況開始運行,程序將在這里無序循環(huán)while(1) {} // 程序不會運行到這里
}
相關函數(shù)
// 給出通知(索引0)
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify )
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken );// 給出通知,uxIndexToNotify為指定索引值
BaseType_t xTaskNotifyGiveIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify )
void vTaskNotifyGiveIndexedFromISR( TaskHandle_t xTaskHandle, UBaseType_t uxIndexToNotify, BaseType_t *pxHigherPriorityTaskWoken );// 等待通知(索引0)
// xClearCountOnExit為pdFALSE則任務通知的值在該函數(shù)退出時遞減,為pdTRUE則在退出時清零
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
// 等待通知,uxIndexToWaitOn為指定索引值
uint32_t ulTaskNotifyTakeIndexed( UBaseType_t uxIndexToWaitOn, BaseType_t xClearCountOnExit, TickType_t xTicksToWait );// 給出通知(索引0)
// ulValue為通知的值
// eAction可選值如下:
// eNoAction - 目標任務接收事件,但不用更新值,此時不關心ulValue
// eSetBits - 目標通知的值使用按位或與ulValue進行運算(這通常被當作事件組使用)
// eIncrement - 目標通知的值自增,此時不關心ulValue(這通常被當作信號量使用)
// eSetValueWithOverwrite - 使用ulValue覆蓋目標通知的值(這通常被當作郵箱使用)
// eSetValueWithoutOverwrite - (這通常被當作長度為1的隊列使用)
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );// 給出通知,uxIndexToNotify為指定索引值
BaseType_t xTaskNotifyIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction );
BaseType_t xTaskNotifyIndexedFromISR( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );// 等待通知(索引0)
// ulBitsToClearOnEntry表示等待前清零的任務通知值的位
// ulBitsToClearOnExit表示通知發(fā)生后清零的任務通知值的位
// pulNotificationValue用來接收任務通知發(fā)生時的值
// 返回值pdPASS表示成功,pdFAIL表示超時
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );
// 等待通知,uxIndexToWaitOn為指定索引值
BaseType_t xTaskNotifyWaitIndexed( UBaseType_t uxIndexToWaitOn, uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );// 給出通知
// 功能類似xTaskNotify
// pulPreviousNotifyValue可以用來接收修改前的目標任務通知的值
BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue );
BaseType_t xTaskNotifyAndQueryFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue, BaseType_t *pxHigherPriorityTaskWoken );
BaseType_t xTaskNotifyAndQueryIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue );
BaseType_t xTaskNotifyAndQueryIndexedFromISR( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue, BaseType_t *pxHigherPriorityTaskWoken );// 將通知的狀態(tài)設置為默認
BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask );
BaseType_t xTaskNotifyStateClearIndexed( TaskHandle_t xTask, UBaseType_t uxIndexToClear );
// 將通知的值設置為默認
uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear );
uint32_t ulTaskNotifyValueClearIndexed( TaskHandle_t xTask, UBaseType_t uxIndexToClear, uint32_t ulBitsToClear );
總結
任務通知功能豐富、又輕量,如果可以滿足業(yè)務功能需求的話,使用任務通知是個不錯的選擇。