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。

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