freertos(第九课,task notifications)

任务通知是一个可选的功能,需要配置宏

#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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章