目录
- 任务状态理论
- 任务的状态切换图
- 任务状态实验
1.任务状态理论
根据上一篇文章任务创建的基础,在原始的任务创建的基础上加上任务状态的判断,程序如下:
//main.c
//定义任务句柄
TaskHandle_t xHandleTask1;
//定义任务标识符
static int task1flag=0;
static int task2flag=0;
static int task3flag=0;
//创建任务
void Task1Function(void *param)
{
while(1)
{
task1flag=1;
task2flag=0;
task3flag=0;
printf("1");
}
}
void Task2Function(void *param)
{
while(1)
{
task1flag=0;
task2flag=1;
task3flag=0;
printf("2");
}
}
void Task3Function(void *param)
{
while(1)
{
task1flag=0;
task2flag=0;
task3flag=1;
printf("3");
}
}
StackType_t xTask3Stack[100];
StaticTask_t xTask3TCB;
StackType_t xIdleTaskStack[100];;
StaticTask_t xIdleTaskTCB;
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer=&xIdelTaskTCB;
*ppxIdleTaskStackBuffer=xIdleTaskStack;
*pulIdleTaskStackSize=100;
}
int main(void)
{
printf("Hello World!");
xTaskCreat(Task1Function,
"Task1",
100,
NULL,
1,
&xHandleTask1
);
xTaskCreat(Task2Function,
"Task2",
100,
NULL,
1,
NULL
);
xTaskCreatStatic(Task3Function,
"Task3",
100,
NULL,
1,
xTask3Stack,
&xTask3TCB
);
vTaskStartScheduler();
}
打开魔法棒->Debug->进行勾选
然后将程序进入到Debug,将task1flag,task2flag,task3flag三个变量add xxx to Logic Analyzer
从上面的图可以看出,task3、task1、task2是交替执行的,他们的执行时间大致为1ms,执行的顺序为task3、task1、task2。任务切换的基础为tick中断,打开FreeRTOSConfig.h可以查看到,FreeRTOS基准的Tick为1ms,也就对应了他们的执行时间大致为1ms。
FreeRTOS的每个任务都有单独的堆栈空间,当任务切出的时候它的执行环境会被保存在该任务的栈空间中,这样当任务再次运行时,就能从堆栈中正确的恢复上次的运行环境,任务越多,需要的堆栈空间就越大,而一个系统能运行多少个任务,取决于系统的可用的 SRAM
FreeRTOS的任务执行可以是抢占式调度机制,优先级高的任务可以打断优先级低的任务的执行,优先级低的任务必须在优先级高的任务阻塞或者结束后得到调度。
FreeRTOS的任务执行也可以进行时间片流转调度方式,时间片流转的方式不允许抢占任务的CPU使用权。 任务通常运行在死循环里。
2.任务的状态切换图
任务一共有四个状态:
1.Running(运行)
2.Ready(就绪)
3.Blocked(阻塞):等待某些事情发生,例如vTaskDelay()函数
4.Suspened(暂停):主动或者被动休息,例如vTaskSuspended/vTaskResume
如何来管理不同状态的任务:将它们放在不同的链表里
3.任务状态实验
TickType_t xTaskGetTickCount( void )函数:获取系统当前运行的时钟节拍数
void vTaskDelay( const TickType_t xTicksToDelay )函数:用于任务的延时,至少等待指定个数的tick才能变成就绪状态
void vTaskSuspend( TaskHandle_t xTaskToSuspend )函数:任务进入暂停状态
void vTaskResume( TaskHandle_t xTaskToResume )函数:任务恢复就绪状态
1.任务状态暂停和恢复函数
//任务句柄
TaskHandle_t xHandleTask1;
TaskHandle_t xHandleTask3;
//任务状态标志
static int task1flag=0;
static int task2flag=0;
static int task3flag=0;
//Task1函数
void Task1Function(void *param)
{
TickType_t tstart=xTaskGetTickCount();//获取当前任务的时钟节拍
TickType_t t;
int flag=0;
while(1)
{
t=xTaskGetTickCount();//获取当前任务的时钟节拍
task1flag=1;
task2flag=0;
task3flag=0;
printf("1");
if(!flag && (t-tstart>10))
{
//task3进入到suspended状态
vTaskSuspend( xHandleTask3 );
falg=1;
}
if((t-start>20))
{
//任务3恢复就绪态
vTaskResume( xHandleTask3 );
}
}
}
//Task2函数
void Task2Function(void *param)
{
while(1)
{
task2flag=1;
task1flag=0;
task3flag=0;
printf("2");
//task2进入阻塞状态
vTaskDelay(10);//用于任务延时,每执行10个tick才能从阻塞状态变为就绪状态
}
}
//Task3
void Task3Function(void *param)
{
while(1)
{
task1flag=0;
task2flag=0;
task3flag=1;
printf("3");
}
}
StackType_t xTask3Stack[100];
StaticTask_t xTask3TCB;
StackType_t xIdleTaskStack[100];
StaticTask_t xIdelTaskTCB;
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer=&xIdelTaskTCB;
*ppxIdleTaskStackBuffer=xIdleTaskStack;
*pulIdleTaskStackSize=100;
}
int main( void )
{
printf("Hello World!\r\n");
xTaskCreate(Task1Function,"Task1",100,NULL,1,&xHandleTask1);
xTaskCreate(Task2Function,"Task2",100,NULL,1,NULL);
xHandleTask3=xTaskCreateStatic(Task3Function,"Task3",100,NULL,1,xTask3Stack,&xTask3TCB);
vTaskStartScheduler();
}
开始时task3进入到running状态,执行1ms后进入ready状态->task1进入running状态执行1ms后进入ready状态->task2进入running状态,然后开始blocked,需要阻塞10ms->task1和task3交替执行,直到task1执行10个tick,task3进入suspended状态,等task1再执行10个tick,task3恢复ready状态->之后task1和task3可以交替执行,但是task2依然会进入blocked状态
程序执行的结果如下:
2.vTaskDelay和vTaskDelayUtil函数
void vTaskDelay( const TickType_t xTicksToDelay ):至少等待指定的tick数才能进入到就绪状态,能保证休眠的时间一样,但不能保证从启动到下一次启动的时间一样
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ):等待到指定的绝对时刻变为就绪态,能保证这一次启动到下一次启动的时间一样,但不能保证休眠的时间一样
程序如下:
TaskHandle_t xHandleTask1;
TaskHandle_t xHandleTask3;
static int task1flag=0;
static int task2flag=0;
static int task3flag=0;
static int rands[]={3,56,12,37,96,45};
void Task1Function(void *param)
{
TickType_t start = xTaskGetTickCount();
int i=0;
int j=0;
while(1)
{
task1flag=1;
task2flag=0;
task3flag=0;
for(i=0;i<rands[j];i++)
printf("1");
j++;
if(j == 6)
j=0;
#if 0
vTaskDelay(20);
#else 1
vTaskDelayUntil(&start,20);
#endif
}
//接下来在main函数中,将task1的优先级设置为2,其余不变
xTaskCreate(Task1Function,"Task1",100,NULL,2,&xHandleTask1);
vTaskDelay()函数的运行结果,可以看出,任务执行的时间间隔是一样的,也就是休眠时间是一样的.
vTaskDelayUntil()函数可以看出这一次启动到下一次启动的时间间隔是一样的,但是休眠时间是不一定的。