FreeRTOS 任务管理:轻松上手指南

一、基本概念

1. 任务

从系统的角度看,任务就是竞争系统资源的最小运行单元。在 FreeRTOS 里,任务就像是一群小伙伴,它们各自做着自己的事情,组成了一个任务集合。每个任务通常都在一个死循环里运行,除非你主动把它删除,不然它就一直“忙”个不停。

FreeRTOS 调度器就像一个超级管理员,它会不停地启动和停止每个任务。虽然实际上同一时间只有一个任务在运行,但从宏观上看,感觉所有任务都在同时执行呢。

每个 FreeRTOS 任务都得有自己的“小仓库”—— 栈空间。当任务暂时不运行时,它当前的执行环境就会被存到这个“小仓库”里。等下次再轮到它运行时,就能从“小仓库”里把之前的状态取出来,接着继续干活。

任务有三个关键要素:

  • 任务主体函数:这就是任务具体要做的事情,好比每个小伙伴的工作内容。
  • 任务堆栈:前面说的“小仓库”,用来存放任务执行过程中的一些临时数据,比如局部变量、函数调用的返回地址、中断现场保护信息等。
  • 任务控制块:可以把它想象成任务的“身份证”,是一个结构体,里面记录了任务的所有信息。

2. 任务调度器

FreeRTOS 任务调度器采用的是基于优先级的全抢占式调度。简单来说,在系统里,除了中断处理函数、调度器上锁部分的代码和禁止中断的代码不能被抢占外,其他部分都可能被“插队”。

高优先级的任务就像VIP客户,它可以随时打断低优先级的任务。低优先级的任务只能等高优先级的任务“休息”(阻塞或结束)了,才有机会被调度执行。如果几个任务的优先级相同,那就采用时间片轮转的方式进行调度,就像大家轮流玩游戏一样。不过,这种时间片轮转调度只有在没有更高优先级的任务准备好运行时才会生效。

3. 任务状态

任务通常有以下四种状态:

  • 就绪(Ready):任务已经准备好了,就像运动员站在起跑线上,只等发令枪响(调度器调度)。新创建的任务一开始就处于就绪态。
  • 运行(Running):任务正在执行,就像运动员正在赛道上奔跑,此时它占用着处理器(CPU)。FreeRTOS 调度器总是会选择最高优先级的就绪态任务来运行,一旦任务开始运行,它的状态就变成了运行态。
  • 阻塞(Blocked):任务正在等待某个时机或者外部中断,就像运动员在等待特定的指令。处于阻塞状态的任务不在就绪列表中,比如任务被挂起、延时、等待信号量、读写队列或者等待读写事件等。
  • 挂起态(Suspended):处于挂起态的任务就像被“冷冻”起来了,对调度器来说是看不见的,也不会参与调度。要让任务进入挂起态或者从挂起态恢复,只能通过调用 vTaskSuspend() 函数和 vTaskResume()(或 vTaskResumeFromISR())函数。当任务有很长时间不需要运行时,就可以把它挂起。

4. 任务状态迁移

FreeRTOS 系统中的每个任务都有好几种运行状态,它们之间的转换关系如下:
任务状态迁移图

  1. 创建任务→就绪态:任务创建好后,就像运动员站到了起跑线上,进入就绪态,随时准备开跑。
  2. 就绪态→运行态:当发生任务切换时,就绪列表中最高优先级的任务就像听到发令枪响的运动员,开始奔跑,进入运行态。
  3. 运行态→就绪态:如果有更高优先级的任务创建或者恢复了,就像突然来了个更厉害的运动员要先跑,原来正在跑的任务(CPU 使用权被抢占)就得回到起跑线,由运行态变为就绪态。
  4. 运行态→阻塞态:正在运行的任务遇到需要等待的情况(比如延时、读信号量等待)时,就像运动员突然被要求暂停,该任务会从就绪列表中移除,状态由运行态变成阻塞态,然后调度器会让就绪列表中当前最高优先级的任务开始运行。
  5. 阻塞态→就绪态:当阻塞的任务满足了恢复条件(比如任务恢复、延时时间到了、读信号量超时或读到信号量等),就像运动员听到可以继续跑的指令,会被重新加入就绪列表,由阻塞态变成就绪态。如果此时这个任务的优先级比正在运行的任务高,那就会再次发生任务切换,它又会从就绪态变成运行态。
  6. 就绪态、阻塞态、运行态→挂起态:可以通过调用 vTaskSuspend() 函数把处于任何状态的任务挂起,被挂起的任务就像被关进了小黑屋,得不到 CPU 的使用权,也不会参与调度,除非把它放出来。
  7. 挂起态→就绪态:通过调用 vTaskResume()vTaskResumeFromISR() 函数把挂起态的任务恢复,就像把运动员从小黑屋里放出来。如果此时被恢复任务的优先级高于正在运行任务的优先级,就会发生任务切换,该任务会从就绪态变成运行态。

5. 空闲任务

空闲任务就像一个“替补队员”,它存在的目的是保证系统里至少有一个任务在运行。当系统中没有其他任务准备好运行时,就会进入空闲任务。空闲任务是 FreeRTOS 系统自己启动的一个任务,它的优先级最低。在 FreeRTOS 中,数值越大优先级越高,0 代表最低优先级。

二、常用的任务函数

1. 任务创建函数

创建任务有两种方式:静态内存创建和动态内存创建。

(1)静态内存创建

  • 这种方式下,任务使用的栈和任务控制块都使用静态内存,也就是预先定义好的全局变量,这些变量都存放在内部的 SRAM 中。
  • 需要实现两个函数:vApplicationGetIdleTaskMemory()vApplicationGetTimerTaskMemory(),这两个函数是用来设定空闲(Idle)任务与定时器(Timer)任务的堆栈大小的,必须由用户自己分配,不能动态分配。
  • 使用 xTaskCreateStatic() 函数创建静态任务。
  • 静态创建任务时,必须把 configSUPPORT_STATIC_ALLOCATION 宏配置为 1。

使用场景:适用于资源受限且内存使用模式固定的嵌入式系统,或者对实时性和确定性要求极高的系统。

(2)动态内存创建

  • 使用动态内存分配方式创建任务时,任务的任务控制块(TCB)和任务栈都是从堆中分配的。
  • 堆的内存空间在运行时可以根据需要动态地分配和释放,非常灵活。
  • 使用 xTaskCreate() 函数动态创建任务。

使用场景:适用于功能复杂且内存需求动态变化的系统,或者需要资源共享和灵活配置的系统,以及需要高效利用内存资源的系统。

2. 任务挂起函数

  • vTaskSuspend():可以挂起指定的任务。被挂起的任务就像被暂停的视频,无论它优先级多高,都得不到 CPU 的使用权。
  • vTaskSuspendAll():这个函数可以把所有的任务都挂起,相当于把任务调度器也暂停了。
//挂起指定任务
void vTaskSuspend( TaskHandle_t xTaskToSuspend )

//挂起全部任务
void vTaskSuspendAll( void )

xTaskToSuspend 就是要挂起的任务句柄,就像要暂停的视频的编号。

3. 任务恢复函数

  • vTaskResume():能让挂起的任务重新回到就绪状态,恢复的任务会保留挂起前的状态信息,就像视频暂停后接着播放一样。
  • xTaskResumeFromISR():这个函数用在中断服务程序中,可以恢复被挂起的任务。
  • xTaskResumeAll():可以把所有的任务都恢复,也就是恢复调度器。
//恢复指定任务
void vTaskResume( TaskHandle_t xTaskToResume )

//在中断服务程序中恢复被挂起的任务
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume )

//恢复所有任务
BaseType_t xTaskResumeAll( void );

xTaskToResume 就是要恢复的任务句柄,就像要接着播放的视频的编号。

4. 任务删除函数

vTaskDelete() 函数可以用来删除指定的任务。

void vTaskDelete( TaskHandle_t xTaskToDelete );

xTaskToDelete 是要删除的任务的句柄。如果传入 NULL,就表示删除当前正在执行的任务。

5. 任务延时函数

  • vTaskDelay():用于阻塞延时,是相对性的延时。它指定的延时时间是从调用 vTaskDelay() 结束后开始计算的,经过指定的时间后延时结束。任务进入阻塞状态时,会让出 CPU 资源。
void vTaskDelay( const TickType_t xTicksToDelay )

xTicksToDelay 表示任务需要阻塞的系统时钟节拍数。在 FreeRTOS 中,系统时钟节拍由 configTICK_RATE_HZ 决定,通常使用 pdMS_TO_TICKS() 函数将毫秒转换为系统时钟节拍数。

  • vTaskDelayUntil():用于较精确的周期运行任务,它从任务上一次解除阻塞的时间点开始计算阻塞时间,是绝对延时的函数。
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement );

pxPreviousWakeTime 是一个指针,指向一个变量,这个变量保存着任务上一次解除阻塞的时间点。首次调用该函数时,需要把这个变量初始化为当前系统时钟节拍计数。xTimeIncrement 表示任务的周期时间,以系统时钟节拍数表示。

应用场景

  • vTaskDelay:适用于简单的延迟任务,对任务执行周期精度要求不高的情况,比如 LED 闪烁、简单的状态更新等。
  • vTaskDelayUntil:适用于对时间精度要求较高的周期性任务,比如电机控制、传感器数据采集、定时通信等,能确保任务在固定的周期内执行。

希望这篇博文能帮助你更好地理解 FreeRTOS 任务管理!如果有任何疑问,欢迎在评论区留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

只争朝夕的奈斯

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

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

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

打赏作者

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

抵扣说明:

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

余额充值