freertos(第六課,TASK, Scheduler,taskdelay)

freertos用task來表示一個進程。
task擁有自己的stack,這是進程最明顯的標誌。
task被表示爲4種狀態。run, ready, suspend, block。
run態是當前佔據CPU的task的狀態。
ready態是當前不需要等待資源到位,只需要排隊獲得CPU的task的狀態。
block態是當前需要等待事件到位的task的狀態,具有超時時間,不會一直block。
suspend態是當前需要等待時間到位的task的狀態,但是不具有超時時間,會一直suspend。

來看看任務控制塊。

typedef struct tskTaskControlBlock{
	char pcTaskName[configMAX_TASK_NAME_SIZE];
	volatile StackType_t * pxTopOfStack;
	ListItem_t xStateListItem;
	ListItem_t xEventListItem;
	UBaseType_t uxPriority;
	StackType_t * pxStack;
	...
}tskTCB;
typedef tskTCB TCB_t; 

每個TASK,要指定一個任務函數,這是創建任務時的入口函數。

void vTaskFunction(void* pvParameters);

taskfunction有一些約束,
返回類型必須是void,
函數過程必須包含一個無限循環,一般不允許退出這個無限循環,如果要跳出這個無限循環,則最後一定要用vTaskDelete(NULL)來刪除這個TASK。

freertos中有一個特殊的任務,IDLETASK,它是在startscheduler時被系統自動創建的。
IDLETASK使用最低的優先級0.
我們知道,任務可以刪除其他任務,但是如果有任務刪除自身,那麼IDLETASK就要負責釋放這個被刪除的任務的資源。

freertos提供了一系列TASK API。

BaseType_t 
xTaskCreate(
	TaskFunction_t pxTaskFunction,
	const char* const pcName,
	const uint16_t usStackDepth,
	void* const pvParamters,
	UBaseType_t uxPriority,
	TaskHandle_t * const pxCreatedTask
	);

void vTaskDelete(TaskHandle_t xTaskToDelete);

void vTaskSuspend(TaskHandle_t xTaskToSuspend);
void vTaskResume(TaskHandle_t xTaskToResume);
void vTaskResumeFromISR(TaskHandle_t xTaskToResume);

當用戶程序中調用TASK API時,OS會利用調度器對進程進行管理。
在main中,使用
vTaskStartScheduler(),
就啓動了調度器。
調度器中,會首先創建一個IDLETASK。
然後會創建一個TIMERTASK。
最後,會啓動SYSTICKTIMER,由SYSTICK中斷來進行事件驅動。
在每個SYSTICK中斷髮生時,OS會進行任務調度。

除了SYSTICK中斷中會發生任務切換,還可以執行一個SYSCALL,觸發SWI,來進行任務切換。
有些API會觸發任務切換。例如taskYIELD()。
有些API會調用taskYIELD,所以這些API都會引起任務切換。

如果需要使用TIMESLICING調度。那麼

#define configUSE_PREEMPTION 1
#define configUSE_TIME_SLICING 1

這兩個宏必須設置爲1.

除此之外,OS提供了其他一些API。

UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask);
UBaseType_t uxTaskPrioritySet(TaskHandle_t xTask, UBaseType_t uxNewPriority);

TaskHandle_t xTaskGetCurrentTaskHandle(void);
TaskHandle_t xTaskGetHandle(const char* pcNameToQuery);
TaskHandle_t xTaskGetIdleTaskHandle(void);

char* pcTaskGetName(TaskHandle_t xTaskToQuery);

TickType_t xTaskGetTickCount(void);
TickType_t xTaskGetTickCountFromISR(void);

freertos中,經常需要在一個任務中使用延時,對任務延時。
延時函數執行時,由於需要等待時間事件的到來,所以會將任務設置爲block態,這就意味着,將發生任務切換。
當延時時間到後,任務才解除block,重新進入ready態。
OS提供了DELAY API。

void vTaskDelay(const TickType_t xTicksToDelay);
void vTaskDelayUntil(TickType_t* const pxPreviousWakeTime, const TickType_t xTimeIncrement);

如果延時時間爲0,則相當於直接使用了taskYIELD進行任務切換。
如果延時時間爲portMAX_DELAY,則直接將任務設置爲suspend,而不是block。
taskdelayuntil用於週期性喚醒的任務。它使用的是絕對時間。如果是對週期性要求比較高的任務,則需要使用這個函數。
典型代碼如下:

void vTaskA(void* pvParameters)
{
	static TickType_t PreviousWakeTime;
	const TickType_t TimeIncrement = pdMS_TO_TICKS(100);

	for(;;){
		vTaskDelayUntil(&previousWakeTime, TimeIncrement);
		...//when wake up ,do your action 
	}
}

當TASKA被喚醒後,進入ready態,如果獲得了CPU,則進入run態,在進入Run態時,會從之前的斷點返回,即vTaskDelayUntil。
從taskdelayuntil返回後,進開始執行主體代碼,當主體代碼執行完時,又會回到循環開頭,再次被阻塞到時間事件上。

freertos的啓動流程,

int main(void){
	BSP_init();
	rtos_init();
	start_task_create(start_task_function);
	start_scheduler();
	while(1);
}


void start_task_function(void* arg)
{
	task_create(task1);
	task_create(task2);
	...
	taskdelete)(start_task);
}

void task1(void* arg)
{
	while(1)
	{
		...
	}
}

void task2(void* arg)
{
	while(1)
	{
		...
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章