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)
	{
		...
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章