FreeRTOS 任务管理

FreeRTOS 任务管理

任务函数

在这里插入图片描述

  • 我们可以按照以上方式定义任务函数,可以更改函数名、变量名。
  • 每一个任务都有一个进入点,任务是一个死循环,一旦启动不会自己停止。
  • 注意,任务函数是没有返回值的,而且一定不能有return。任务可以被显式删除。
  • 一个任务函数可以创建任意个的任务

任务函数定义如下:
在这里插入图片描述

API

创建任务 (Creating Tasks)

The xTaskCreate()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

示例1 任务创建

*pvTaskCode 是一个函数指针,也就是任务函数名称。比如说,创建一个名为vTask1的函数,注意,一定是个死循环!

void vTask1(void *pvParameters)
{
	for(;;)
	{
		vPrintString( "Hello world!" );
	}
}
void vTask2(void *pvParameters)
{
	for(;;)
	{
		// 此处省略
	}
}

在main()函数中创建任务,

int main( void )
{
	xTaskCreate( vTask1, // 函数指针
			 	"Task 1", // 任务名称,实际作用不大,可用来Debug
			 	1000, // 堆栈大小,根据需要调整
			 	NULL, // 需要传递到任务的参数,没有就设置为NULL
			 	1, // 任务优先级
			 	NULL); // 任务句柄
	xTaskCreate(vTask2,"Task 2",1000,NULL,1,NULL);
	// xTaskCreate()的返回值是 pdPASS/pdFAIL
	vTaskStartScheduler();
	for(;;); // 我们的main()函数不能返回,开启调度器后,FreeRTOS会接管系统。
}

如下图所示:
在这里插入图片描述
我们也可以直接在一个任务中创建另一个任务:
在这里插入图片描述

示例 2 参数传递

我们也可以通过参数传递实现两个不同的任务用同一个任务函数
任务函数:
在这里插入图片描述
创建任务:
在这里插入图片描述

任务优先级 (Task Priorities)

在创建任务时,FreeRTOS会根据xTaskCreate()中定义的初始值,确定任务优先级。在开启调度之后,任务的优先级也可以通过vTaskPrioritySet()函数重新设定。
任务的最大优先级定义为configMAX_PRIORITIES,最小优先级是0,所以选取 (configMAX_PRIORITIES – 1)范围内的任意一个优先级都是被允许的。而且不同的任务可以拥有相同的优先级。当优先级相同时,调度器将交替执行任务。

FreeRTOS调度器允许以两种方式调度。

  • 通用方式 Generic Method
    在这种情况下,FreeRTOS不限制最大优先级的数值,但是还是建议将最大优先级数设置在“够用”的数值上,减少内存消耗。
    可以通过将configUSE_PORT_OPTIMISED_TASK_SELECTION设置为0启用这个方式。
  • 根据架构优化方式 Architecture Optimized Method
    这个方式比通用方式要快,因为它是用来部分汇编代码编写。
    在这种情况下,configMAX_PRIORITIES不能超过32,同样的,建议将最大优先级数设置在“够用”的数值上,减少内存消耗。
    可以通过将configUSE_PORT_OPTIMISED_TASK_SELECTION设置为1启用这个模式。但是,并不是所有的FreeRTOS接口都支持这个模式。

时间测量和滴答中断 (Time Measurement and the Tick Interrupt)

时间片 Time Slice:
时间片作为最小的执行时间也叫滴答周期(Tick Period),可以通过configTICK_RATE_HZ设置。
为了选择下个需要运行的任务,调度器在每个时间片结束时都会执行调度,通过滴答中断实现。如图所示:
在这里插入图片描述
可以看到,黑色箭头表示的是,任务结束->触发滴答中断->进行调度 的过程。

FreeRTOS通常以滴答周期作为单位,表示为 “xx ticks”, 可以通过pdMS_TO_TICKS()函数进行ms到ticks的单位换算。
在这里插入图片描述
注意,一般不建议直接用数值指定ticks,考虑到可移植性,使用pdMS_TO_TICKS()函数能避免很多麻烦。

示例

在这里插入图片描述
在这里插入图片描述
由于Task2的优先级比Task1高,所以只有Task2能执行

非运行状态的情况 Expanding the ‘Not Running’ State

阻塞态 Blocked State

阻塞态有两种可能情况:

  • 跟时间相关的事件(比如说,延时)
  • 同步事件(比如说,任务需要等待数据等)
    在FreeRTOS中,队列,信号量,互斥量,事件组,任务通知都会产生同步事件

挂起态 Suspended State

挂起态的任务不会被调度,唯一可以导致挂起的操作是调用vTaskSuspend()函数,从挂起态跳出可以调用vTaskResume()和xTaskResumeFromISR(),大部分的应用不需要挂起态。

就绪态 Ready State

就绪态的任务处于等待调度上CPU的状态

状态机模型

在这里插入图片描述

延时函数 vTaskDelay()

在这里插入图片描述
在这里插入图片描述
示例程序:
在这里插入图片描述
vTaskDelay() 的延迟时间是相对的,他是 本次调用到再次调用 所经过的时间。所以,前提是需要调用vTaskDelay() 才可以,如果这段时间中有别的任务抢占CPU,就无法保证延迟是固定频率的。

图解:
在这里插入图片描述

延时函数 vTaskDelayUntil()

在这里插入图片描述
在这里插入图片描述
vTaskDelayUntil() 的延时时间是个固定值,参数代表了精确的延时时间。这样,延时的执行频率是固定的。在想让任务以固定频率执行时,应使用这种方式。

示例程序:
在这里插入图片描述
图解:
在这里插入图片描述

  • t1时刻,Periodic任务刚执行完成,xLastWakeTime记录下此时的tick count(tick count是从调度开始不断递增的一个计数器);Continuous1开始运行
  • t5时刻,调度器发现Periodic任务的延时时间已经到了,所以将其设置为就绪态。同时,由于此任务优先级最高,于是开始执行。执行完成后切换到Continuous1任务。

空闲任务和空闲钩子 The Idle Task and the Idle Task Hook

空闲任务

空闲任务在开启调度时会自动被系统创建,空闲任务拥有最低优先级

空闲钩子 Idle Task Hook Functions

空闲钩子会被空闲任务自动调用,它常应用于:

  • 运行连续处理的功能,后台功能,比如说状态指示灯
  • 检测处理器余量
  • 低功耗运行

注意:

  1. 空闲钩子不能被阻塞或挂起
  2. 如果使用vTaskDelete()函数,钩子函数必须在合理的时间内返回到调用。

示例:
在这里插入图片描述

修改任务优先级 Changing the Priority of a Task

设置优先级 vTaskPrioritySet()

在这里插入图片描述
在这里插入图片描述
如果第一个参数设置为NULL,表明修改自身优先级

获取优先级 uxTaskPriorityGet()

在这里插入图片描述
在这里插入图片描述

示例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
图解:
在这里插入图片描述

  • Task1提高了Task2优先级,此时Task2抢占CPU,开始运行
  • Task2运行完降低自己的优先级,此时Task1抢占CPU,开始运行

删除任务 Deleting a Task

vTaskDelete()

在这里插入图片描述
示例程序:
在这里插入图片描述
在这里插入图片描述
图解
在这里插入图片描述
Task1创建Task2任务,Task2删除自己,just it~

发布了85 篇原创文章 · 获赞 17 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/lun55423/article/details/105674182
今日推荐