一.创建任务函数
xTaskCreate( (TaskFunction_t )start_task, /*要执行的函数,开始任务*/(const char* )"start_task", /*任务名字,建议个函数名一样*/(uint16_t )START_STK_SIZE, (void* )NULL,(UBaseType_t )START_TASK_PRIO, /*任务优先级*/(TaskHandle_t* )&StartTask_Handler); /*任务句柄*/
TaskHandle_t TCB结构体,保存任务的基本信息
创建一个巨大的数组,用来模拟栈。r15保存函数地址,r0保存函数参数
二.任务调度机制
1.优先级
在使用操作系统中的实时操作系统(RTOS)如FreeRTOS时,任务调度器负责根据任务的优先级来决定优先级最高的任务是否应该抢占当前正在执行的任务。如果优先级最高的任务需要运行,则它会中断当前任务的执行,并立即执行高优先级任务。
在FreeRTOS中,默认情况下,相同优先级的任务是按照“时间片轮转”的方式进行调度,即每个任务都会被分配一个时间片(时间片长度可以配置),当时间片用完后,调度器会将控制权转移到下一个相同优先级的任务上。这种方式确保了相同优先级的任务平等地共享系统资源。
而当有优先级更高的任务准备运行时,调度器会立即抢占当前正在执行的任务,将执行权交给高优先级任务。这种抢占式调度机制确保了高优先级任务的及时响应和执行,以满足实时性的要求。
需要注意的是,任务的优先级越高,任务被执行的频率也会越高,因此在任务的优先级设置上要慎重,以避免导致低优先级任务饥饿(Starvation)的情况发生。
在FreeRTOS中,可以使用任务API提供的函数,如vTaskPrioritySet()
来设置任务的优先级,以及vTaskDelay()
来实现任务延迟等操作来控制任务的调度行为。
- 高优先级的任务,优先执行,可以抢占低优先级的任务。
- 高优先级的任务不停止,低优先级的任务永远无法执行。
- 同等优先级的任务,轮流执行:时间片轮转 I
2.状态
在FreeRTOS中,任务(task)可以处于不同的状态,这些状态反映了任务当前的运行状况和调度状态。常见的任务状态包括以下几种:
-
Ready(就绪):
- 任务已经被创建,具备运行的条件,但还未被调度执行。一般而言,当任务处于就绪状态时,它只需要等待CPU分配执行时间就可以开始运行。
-
Running(运行):
- 当前正在执行的任务状态。每个时刻只能有一个任务处于运行状态。
-
Blocked(阻塞):
- 任务由于某些原因而无法继续执行,例如等待某个事件的发生、等待定时器超时、等待信号量等。任务在阻塞状态下不会被调度执行,直到条件满足才会转为就绪状态。
-
Suspended(挂起):
- 任务被明确挂起,暂时停止执行。挂起的任务不会被调度执行,直到被明确恢复为止。
-
Deleted(删除):
- 任务已被删除,但其资源尚未被释放。任务在被删除后,其占用的资源会在内存中保留一段时间,以便RTOS后续做清理工作。
这些任务状态可以帮助开发人员了解任务当前的运行状况,有助于调试和排查问题。在FreeRTOS中,可以使用任务管理API提供的函数来获取和控制任务的状态,例如eTaskState()
函数可以获取任务的当前状态,vTaskSuspend()
函数可以将任务挂起,vTaskResume()
函数可以恢复挂起的任务等。
3.怎么管理
在FreeRTOS中,任务调度器会根据任务的优先级来选择要运行的任务。当调度器需要选择下一个要运行的任务时,它会采取以下步骤:
-
首先,调度器会检查就绪态的任务中是否存在优先级更高的任务。如果有,调度器会选择优先级最高的任务,并立即切换到该任务的上下文继续执行。
-
如果就绪态的任务中没有优先级更高的任务,则调度器会遍历具有相同最高优先级的任务,并按照轮询的方式选择下一个要运行的任务。这样,任务会按照队列的顺序依次运行。
-
当一个任务被调度器选中运行后,它会被分配一个时间片(称为时间片轮转),即一定的运行时间。当时间片用完后,调度器会将控制权转移到下一个相同优先级的任务上,以保证所有平级任务都有机会运行。
-
如果一个任务在运行期间被阻塞或被挂起,它将不参与调度,直到改变其状态并重新进入就绪态。
需要注意的是,任务的调度行为和策略可以通过RTOS的配置选项进行调整和优化。例如,可以通过更改任务优先级、调整时间片长度、使用抢占式调度器等来满足特定的应用需求。
4.谁进行调度
在FreeRTOS中,滴答定时器(tick timer)是用于触发任务调度的关键组件。滴答定时器负责以固定的时间间隔触发中断,并在中断服务例程(ISR)中调用任务调度器。
以下是执行调度函数(滴答定时器)的基本步骤:
-
配置滴答定时器:
- 首先,你需要配置滴答定时器的时钟源和计数周期。这个配置通常在FreeRTOS的配置文件中进行,如
FreeRTOSConfig.h
。 - 确保滴答定时器的时钟源和计数周期设置合适,以满足任务调度的需求。
- 首先,你需要配置滴答定时器的时钟源和计数周期。这个配置通常在FreeRTOS的配置文件中进行,如
-
实现滴答定时器中断处理程序:
- 在滴答定时器中断处理程序中,你需要调用任务调度器的函数。这个函数通常是
xTaskIncrementTick()
或vTaskIncrementTick()
。 - 该函数会在每次滴答定时器中断发生时执行,对系统的时基进行更新,并决定是否进行任务切换。
- 在滴答定时器中断处理程序中,你需要调用任务调度器的函数。这个函数通常是
-
启用滴答定时器中断:
- 在系统初始化阶段,你需要启用滴答定时器中断。
- 这通常涉及到设置中断优先级、使能中断以及将中断处理程序(即步骤2中实现的函数)注册到中断向量表中。
-
启动滴答定时器:
- 在系统初始化完成后,你需要启动滴答定时器,使其开始计数。
- 这通常涉及到调用启动滴答定时器的函数,如
xTickCountStart()
或vTaskStartScheduler()
。
一旦滴答定时器启动并配置正确,它将以设置的时间间隔进行计数,并在每次计数满时触发中断。中断处理程序中会执行任务调度器的调度函数,根据任务的优先级和调度策略来决定下一个要运行的任务。
5.状态切换
高优先级的任务优先执行。
这些状态之间的转换是通过任务管理 API 函数来实现的,例如 vTaskDelay() 可以使任务从 “Running” 状态转换为 “Blocked” 状态,vTaskResume() 可以从 “Suspended” 状态恢复任务。
任务的状态转换在 FreeRTOS 中由调度器自动管理,开发人员只需根据应用程序的需要,使用适当的任务管理 API 函数进行状态转换。正确的状态转换是实现多任务应用程序的关键,它可以提供任务之间的协调和合作。