基于STM32F103的FreeRTOS系列(八)·任务管理API详细解析

目录

1.  简介

1.1  任务的简介

1.2  任务调度器

1.3  任务状态

1.4  任务状态转换

2.  常用任务API函数

2.1  任务的挂起函数

2.1.1  挂起指定任务vTaskSuspend()

2.1.2  挂起全部任务vTaskSuspendALL()

2.2  任务的恢复函数

2.2.1  恢复指定任务vTaskResume()

2.2.2  恢复全部任务vTaskResumeALL()

2.2.3  中断恢复任务Type_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)

2.3  删除任务函数 vTaskDelete()

2.4  任务的延时函数

2.4.1  相对延时函数 vTaskDelay()

2.4.2  绝对延时函数 vTaskDelayUntil()


1.  简介

1.1  任务的简介

        在裸机系统中,系统的主体就是 main 函数里面顺序执行无限循环,这个无限循环里面 CPU 按照顺序完成各种事情。
        在FreeRTOS中,根据功能的不同,把整个系统分割成一个个独立无限循环无法返回的函数,这个函数就称为任务

1.2  任务调度器

        在FreeRTOS中任何时刻,只有一个任务得到运行,对于哪一个任务先执行是由FreeRTOS调度器决定的。调度器会不断的启动、停止每一个任务,宏观看上去所有的任务都在同时在执行。
        FreeRTOS 中的任务是抢占式调度机制,优先级的任务可打断低优先级任务,低优先级任务必须在高优先级任务阻塞或结束后才能得到调度。

1.3  任务状态

        任务的状态通常分为以下四种状态:就绪(Ready)、运行(Running)、阻塞(Blocked)、挂起态(Suspended)。

就绪态 (ready):该任务在就绪列表中,就绪的任务已经具备执行的能力,只等待调度器进行调度,新创建的任务会初始化为就绪态。

运行态 (running):该状态表明任务正在执行,此时它占用处理器,FreeRTOS调度器选择运行的永远是处于最高优先级的就绪态任务,当任务被运行的一刻,它的任务状态就变成了运行态。

阻塞态 (blocked):正在运行的任务发生阻塞 (延时、读信号量等待、读写队列或者等待读写事件) 时,该任务会从就绪列表中删除,任务状态由运行态变成阻塞态。这种状态下,进程或线程不会主动进行CPU调度,但是它仍然处于调度器的管理下,并且可以在事件发生后立即被重新调度到运行态。

挂起态 (suspended):处于挂起态的任务对调度器而言是不可见的,让一个任务进入挂起状态的唯一办法就是调用 vTaskSuspend() 函数;而把一个挂起状态的任务恢复 的唯一途径就是调用 vTaskResume() 或 vTaskResumeFromISR() 函数。

挂起态阻塞态的区别:
        当任务有较长的时间不允许运行的时候,我们可以挂起任务,这样调度器就不会管这个任务的任何信息,直到我们调用恢复任务的 API 函数;
        而任务处于阻塞态的时候,系统还需要判断阻塞态的任务是否超时,是否可以解除阻塞。

        简单来说,

        挂起态:任务的挂起和恢复,需要主动调用相关API,一旦挂起程序就不在管这个任务,直到主动调取API恢复。

        阻塞态:阻塞态是由于某些事件的等待而被动进入的,进入后程序还要时不时查看一下他(调度器可以追踪其状态),并在事件发生时尽快唤醒。

1.4  任务状态转换

① 创建任务→就绪态 (ready):任务创建完成后进入就绪态,表明任务已准备就绪,随时可以运行,只等待调度器进行调度。
② 就绪态→运行态 (running):发生任务切换时,就绪列表中最高优先级的任务被执行,从而进入运行态。
③ 运行态→就绪态:更高优先级任务创建或者恢复后,会发生任务调度,此刻就绪列表中最高优先级任务变为运行态,那么原先运行的任务由运行态变为就绪态,依然在就绪列表中,等待最高优先级的任务运行完毕,继续运行原来的任务 (此处可以看做是CPU使用权被更高优先级的任务抢占了)。
④ 运行态→阻塞态 (blocked):正在运行的任务发生阻塞 (延时、读信号量等待) 时,该任务会从就绪列表删除,任务状态由运行态变成阻塞态,然后发生任务切换,运行就绪列表中当前最高优先级任务。
⑤ 阻塞态→就绪态:阻塞的任务被恢复后 (任务恢复、延时时间超时、读信号量超时 或读到信号量等),此时被恢复的任务会被加入就绪列表,从而由阻塞态变成就绪态;如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,将该任务将再次转换任务状态,由就绪态变成运行态。
⑥⑦⑧ 就绪态、阻塞态、运行态→挂起态 (suspended):任务可以通过调用 vTaskSuspend() API 函数都可以将处于任何状态的任务挂起,被挂起的任务得不到CPU的使用权,也不会参与调度,除非它从挂起态中解除。
⑨ 挂起态→就绪态:把一个挂起状态的任务恢复的唯一途径就是调用 vTaskResume() 或 vTaskResumeFromISR() API 函数,如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生 任务切换,将该任务将再次转换任务状态,由就绪态变成运行态。

2.  常用任务API函数

2.1  任务的挂起函数

2.1.1  挂起指定任务vTaskSuspend()

void vTaskSuspend(TaskHandle_t xTaskToSuspend)
参数:xTaskToSuspend

要挂起的任务的任务句柄,创建任务的时候会为每个任务分配一个任务句柄。如果使用函数xTaskCreate()创建任务的话那么函数的参数pxCreatedTask就是此任务的任务句柄,如果使用函数 xTaskCreateStatic()创建任务的话那么函数的返回值就是此任务的任务句柄。也可以通过函数xTaskGetHandle()来根据任务名字来获取某个任务的任务句柄。

注意!如果参数为NULL的话表示挂起任务自己。

返回值

        任务可以通过调用 vTaskSuspend() 函数都可以将处于任何状态的任务挂起,被挂起的任务得不到 CPU 的使用权,也不会参与调度,它相对于调度器而言是不可见的,除非它从挂起态中解除。

//KEY任务函数
void key_task(void *pvParameters)
{
    u8 key=0;
	
	while(1)
    {
		key=KEY_Scan(0);
		if(key==KEY_UP_PRESS)
		{
			printf("挂起LED任务!\n");
			vTaskSuspend(LED2Task_Handler);/* 挂起LED任务 */
			printf("挂起LED任务成功!\n");
		}
		else if(key==KEY1_PRESS)
		{
			printf("恢复LED任务!\n");
			vTaskResume(LED2Task_Handler);/* 恢复LED任务!*/
			printf("恢复LED任务成功!\n");
		}
		vTaskDelay(20);
    }
}

        记住是任何状态,其中也包括自己本身,但是在挂起自身的时候会进行一次任务上下文切换,需要挂起自身就将 xTaskToSuspend 设置为NULL传递进来即可。

 static TaskHandle_t LED_Task_Handle = NULL;	/*LED任务句柄*/
 
 static void KEY_Task(void* parameter)
 {
     while (1) {
         if ( Key_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == KEY_ON ) {
             /*K1被按下*/
             printf("挂起 LED 任务!\n");
             vTaskSuspend(LED_Task_Handle);	/*挂起LED任务*/
         }
         vTaskDelay(20);			/*延时20个tick*/
     }
 }

2.1.2  挂起全部任务vTaskSuspendALL()

        挂起全部任务,实际上就是挂起任务调度器。需要搭配vTaskResumeALL()一起使用,vTaskSuspendALL()被调用多少次,vTaskResumeALL()就要调用多少次。

void vTaskSuspendALL(void)
{
    ++uxSchedulerSuspended;
}

2.2  任务的恢复函数

2.2.1  恢复指定任务vTaskResume()

void vTaskResume(TaskHandle_t xTaskToResume)
参数:xTaskToResume要恢复任务的任务句柄
返回值:

        任务恢复就是让挂起的任务重新进入就绪状态,恢复的任务会保留挂起前的状态信息,在恢复的时候根据挂起时的状态,继续运行。
        如果被恢复任务在所有就绪态任务中,处于最高优先级列表的第一位,那么系统将进行任务上下文的切换。

//KEY任务函数
void key_task(void *pvParameters)
{
    u8 key=0;
	
	while(1)
    {
		key=KEY_Scan(0);
		if(key==KEY_UP_PRESS)
		{
			printf("挂起LED任务!\n");
			vTaskSuspend(LED2Task_Handler);/* 挂起LED任务 */
			printf("挂起LED任务成功!\n");
		}
		else if(key==KEY1_PRESS)
		{
			printf("恢复LED任务!\n");
			vTaskResume(LED2Task_Handler);/* 恢复LED任务!*/
			printf("恢复LED任务成功!\n");
		}
		vTaskDelay(20);
    }
}

2.2.2  恢复全部任务vTaskResumeALL()

        需要搭配vTaskSuspendALL()使用,vTaskSuspendALL()调用多少次,vTaskResumeALL()就要调用多少次。

2.2.3  中断恢复任务Type_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)

Type_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)
参数:xTaskToResume要恢复任务的任务句柄
返回值:

pdTRUE:恢复运行的任务的任务优先级等于或者高于正在运行的任务(被中断打断的任务),这意味着在退出中断服务函数以后必须进行一次上下文切换。

pdFALSE:恢复运行的任务的任务优先级低于当前正在运行的任务(被中断打断的

任务),这意味着在退出中断服务函数的以后不需要进行上下文切换。

2.3  删除任务函数 vTaskDelete()

对于删除任务函数:

(1) 当一个任务删除另外一个任务时,形参为 要删除任务 创建时返回的任务句柄;
(2) 如果是删除自身, 则形参为 NULL。


/*  删除任务本身 */
vTaskDelete( NULL );

/*  在其他任务删除 DeleteTask 任务 */
vTaskDelete( DeleteHandle );

2.4  任务的延时函数

2.4.1  相对延时函数 vTaskDelay()

        阻塞延时的阻塞,是指任务调用该延时函数后,任务会被剥离 CPU 使用权,然后进入阻塞状态,直到延时结束,任务重新获取 CPU 使用权才可以继续运行。在任务阻塞的这段时间,CPU可以去执行其它的任务,如果其它的任务也在延时状态,那么 CPU就将运行空闲任务。

void vTaskDelay(const TickType_t xTicksToDelay)

        延时的时长由形参xTicksToDelay决定,单位为系统节拍周期, 比如系统的时钟节拍周期为 1ms,那么调用 vTaskDelay(1) 的延时时间则为 1ms。

        相对延时的意思是:想要延时100ms,那么这100ms是调用vTaskDelay() 之后开始计时的,经过100个系统节拍周期完成计时。因此,vTaskDelay() 并不适用与周期性执行任务的场合。

        除此之外,其它任务和中断活动,也会影响到 vTaskDelay() 的调用(比如调用前,高优先级任务抢占了当前任务),进而影响到任务的下一次执行的时间。

void vTaskA( void * pvParameters )
 {
     while (1) {
       // ...
       // 这里为任务主体代码
       // ...

       /*  调用相对延时函数, 阻塞 1000 个 个 tick */
       vTaskDelay( 1000 );
     }
 }

2.4.2  绝对延时函数 vTaskDelayUntil()

        绝对延时函数,常用于较精确的周期运行任务,比如我有一个任务,希望它以固定频率定期执行,而不受外部的影响,任务从上一次运行开始到下一次运行开始的时间间隔是绝对的,而不是相对的。

void vTaskDelayUntil(TickType_t* const pxPreviousWakeTime,const TickType_t xTimeIncrement);
void vTaskA( void * pvParameters )
 {
   /*  用于保存上次时间。调用后系统自动更新 */
   static portTickType PreviousWakeTime;
   /*  设置延时时间,将时间转为节拍数 */
   const portTickType TimeIncrement = pdMS_TO_TICKS(1000);

   /*  获取当前系统时间 */
   PreviousWakeTime = xTaskGetTickCount();

   while (1) {
     /*  调用绝对延时函数, 任务时间间隔为 1000 个 个 tick */
     vTaskDelayUntil( &PreviousWakeTime ,TimeIncrement );

     // ...
     // 这里为任务主体代码
     // ...
   }
}

FreeRTOS_时光の尘的博客-CSDN博客

  • 23
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时光の尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值