任务通知是一个可选的功能,需要配置宏
#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。