任務通知是一個可選的功能,需要配置宏
#define configUSE_TASK_NOTIFICATIONS 1
freertos中的每個TCB,都有一個32位的通知量。
ulNotifiedValue。
任務通知量也是一個信號量事件,也是用於進程同步的方式。
所以,進程也是向OS申請PV操作的。
freertos提供了一系列的API。
typedef enum{
eNoAction = 0,
eSetBits,
eIncrement,
eSetValueWithOverwrite,
eSetValueWithoutOverwrite
}eNotifyAction;
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
);
BaseType_t
xTaskNotifyGive(
TaskHandle_t xTaskToNotify
);
BaseType_t
xTaskNotifyGiveFromISR(
TaskHandle_t xTaskToNotify,
BaseType_t* pxHigherPriorityTaskWoken
);
BaseType_t
xTaskNotifyAndQuery(
TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction,
uint32_t* pulPreviousNotificationValue
);
BaseType_t
xTaskNotifyAndQueryFromISR(
TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction,
uint32_t* pulPreviousNotificationValue,
BaseType_t* pxHigherPriorityTaskWoken
);
uint32_t ulTaskNotifyTake(BaseType_t xClearCountOnExit, TickType_t xTicksToWait);
BastType_t
xTaskNotifyWait(
uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t pulNotificationValue,
TickType_t xTicksToWait
);
當進程A向OS申請一個Wait操作時,OS嘗試對進程A執行P操作。將進程A阻塞。
當另一個進程B向OS申請一個針對進程A的Notify操作時,OS會找到進程A的TCB,並嘗試對進程A執行V操作。將進程A喚醒。並將NotifyValue寫入進程A的TCB。
通知量是TCB自帶的資源,所以使用起來很方便,而且,通知量可以點對點的精準喚醒TASK。
這讓執行喚醒的進程,參與到進程調度過程中來,如果是semaphore,喚醒進程只知道有某個進程被喚醒,但是並不知道具體是哪一個。
點對點喚醒,在一對多的進程同步中,具有非常多的優勢。
通知量也有它的缺點,進程必須清楚,它需要向誰發出通知量。
這個問題在semaphore中是不存在的,進程不需要了解究竟誰需要資源,它只管告訴OS,它生產了資源。
當進程A明確的知道,它作爲生產者,對應的消費者是進程B時,通知量就是一種功能強大的進程同步方式。
來看幾種典型的應用。
1)替代BinarySemaphore。
void taskAFunction(void* pvParameters)
{
while(1){
NotifyValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(NotifyValue == 1){
...//do your action
}
}
}
void xxx_IRQHandler(void)
{
...
vTaskNotifyGiveFromISR(TaskA_Handler, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
在ISR中使用give,在TaskA中使用take。
如果TASKA被阻塞,那麼當OS發送通知時,將喚醒TaskA。
2)替代CountSemaphore。
void SemGive_task(void* pvParameters)
{
while(1){
...
xTaskNotifyGive(SemTakeTaskHandle);
...
}
}
void SemTake_task(void* pvParameters)
{
while(1){
NotifyValue = xTaskNotifyTake(pdFALSE, portMAX_DELAY);
...
}
}
在taskA中使用give,在taskB中使用take,
如果TASKB被阻塞,那麼當OS發送通知時,將喚醒TaskB。
3)替代MSG
void taskAFunction(void* pvParameters)
{
...
while(1){
...
err = xTaskNotify(
(TaskHandle_t)TaskB_Handle,
(uint32_t)key,
(eNotifyAction)eSetValueWithOverwrite
);
...
}
}
void taskBFunction(void* pvParameters)
{
uint32_t NotifyValue;
...
while(1){
err = xTaskNotifyWait(
(uint32_t)0x00,
(uint32_t)ULONG_MAX,
(uint32_t*)&NotifyValue,
(TickType_t)portMAX_DELAY
);
if(err == pdTRUE){
switch(NotifyValue){
case WKUP:
...
break;
case KEY1:
...
break;
case KEY2:
...
break;
}
}
}
}
在taskA中使用notify,在taskB中使用wait,
如果TASKB被阻塞,那麼當OS發送通知時,將喚醒TaskB。
4)替代eventgroup
void xxx_IRQHandler(void)
{
...
xTaskNotifyFromISR(
(TaskHandle_t)TaskB_Handle,
(uint32_t)EVENTBIT_WKUP,
(eNotifyAction)eSetBits,
(BaseType_t*)xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
...
}
void taskAFunction(void* pvParameters)
{
...
while(1){
...
switch(key){
case KEY1:
xTaskNotify(
(TaskHandle_t)TaskB_Handle,
(uint32_t)EVENTBIT_KEY1,
(eNotifyAction)eSetBits);
break;
}
}
}
void taskCFunction(void* pvParameters)
{
...
while(1){
...
switch(key){
case KEY2:
xTaskNotify(
(TaskHandle_t)TaskB_Handle,
(uint32_t)EVENTBIT_KEY2,
(eNotifyAction)eSetBits);
break;
}
}
}
void taskBFunction(void* pvParameters)
{
uint32_t NotifyValue;
...
while(1){
err = xTaskNotifyWait(
(uint32_t)0x00,
(uint32_t)ULONG_MAX,
(uint32_t*)&NotifyValue,
(TickType_t)portMAX_DELAY
);
if(err == pdPASS){
if((NotifyValue&EVENTBIT_WKUP) != 0){
event0flag = 1;
}
if((NotifyValue&EVENTBIT_KEY1) != 0){
event1flag = 1;
}
if((NotifyValue&EVENTBIT_KEY2) != 0){
event2flag = 1;
}
eventvalue = event0flag | (event1flag << 1) | (event2flag << 2);
if(event0flag && event1flag && event2flag ){
...//do your action
}
event0flag = 0;
event1flag = 0;
event2flag = 0;
}
}
}
在taskA,taskC,ISR中使用notify,在taskB中使用wait,
如果TASKB被阻塞,那麼當OS發送通知時,將喚醒TaskB。