文章目录
- 任务状态
- 任务挂起 vTaskSuspend
- 取消任务挂起 vTaskResume
- 挂起任务调度器 vTaskSuspendAll
- 取消挂起任务调度器 xTaskResumeAll
- 代码示例:任务挂起、取消任务挂起
- 代码示例:挂起任务调度器、取消挂起任务调度器
任务状态
freeRTOS任务的状态有四种:运行、就绪、阻塞、挂起
- 运行态(runnnig):当任务正在运行,此时的状态被称为运行态,即CPU的使用权被这个任务占用;
- 挂起态(suspended):任务被暂时停止,通过调用挂起函数(
vTaskSuspend()
)可以把指定任务挂起,任务挂起后暂时不会运行,只有调用恢复函数(xTaskResume()
)才可以退出挂起状态;- 阻塞态(blocked):任务在等待信号量、消息队列、事件标准组、系统延时时,被称为阻塞态,如果等待的事件到了,就会自动退出阻塞态,准备运行;
- 就绪态(ready):任务已经具备了运行条件(没有被挂起或阻塞),但是又更高优先级或同优先级的任务正在运行,所以需要等待的状态。
一般任务创建完成后,进入就绪态;处于就绪态的任务,如果没有更高优先级或同优先级的任务正在运行,它会自动进入运行态;如果有更高优先级的任务要运行,或者同优先级的任务要轮流运行,它会从运行态返回到就绪态;如果任务需要等待某个事件,或者任务自己进入了系统延时,则会进入阻塞态;当等待的事件达到后,任务又会进入就绪态。
注意事项:在阻塞状态中,任务可以等待两种类型的事件:
- 时间相关的事件。
- 可以是等待一段时间,比如等3秒钟。使用
vTaskDelay
函数实现,等待的时间到了任务就会变为就绪态- 也可以是等到某个绝对的时间点。使用
vTaskDelayUntil
函数实现,等到了某个时间点任务就会进入就绪态- 同步事件,由其他任务或者中断产生,比如任务A给任务B发送数据,或者任务A等待用户按下按键。同步事件的来源很多,如下:
- 队列(queue)
- 二进制信号量(binary semaphores)
- 互斥量(mutexes)
- 递归互斥量、递归锁(recursive muxtexes)
- 事件组(event groups)
- 任务通知(task notifications)
任务挂起 vTaskSuspend
挂起任务,当挂起一个任务时,不管优先级是多少,不需要占用任何微控制器处理器时间。调用
vTaskSuspend
不会累积——即:在统一任务中调用vTaskSuspend
两次,但只需调用一次vTaskResume
来是挂起的任务就绪。
void vTaskSuspend( xTaskHandle pxTaskToSuspend);
parameter | description |
---|---|
pxTaskToSuspend | 处理需要挂起的任务。传递NULL将挂起调用此函数的任务。 |
return | 空 |
取消任务挂起 vTaskResume
取消挂起的任务。 必须是调用
vTaskSuspend
后挂起的任务,才有可能通过调用vTaskResume
重新运行。
void vTaskResume(xTaskHandle pxTaskToResume);
parameter | description |
---|---|
pxTaskToResume | 就绪的任务的句柄 |
return | 空 |
挂起任务调度器 vTaskSuspendAll
挂起任务调度器,挂起任务调度器可以防止发生上下文切换,但保留启用中断的状态。如果一个中断在调度程序挂起时请求上下文切换,那么该请求被保持为挂起状态,并且只有在任务调度器恢复(非挂起)时才执行。注意一下:
- 在挂起任务调度器时,其他FreeRTOS API函数是不能被调用的,自己的函数可以被调用。
- 注意系统看门狗喂狗
void vTaskSuspendAll(void);
parameter | description |
---|---|
None | 空 |
return | 空 |
取消挂起任务调度器 xTaskResumeAll
在使用
vTaskSuspendAll
挂起任务调度器后,使用此函数可恢复任务调度器。
BaseType_t xTaskResumeAll(void);
parameter | description |
---|---|
None | 空 |
return | 是否恢复 |
代码示例:任务挂起、取消任务挂起
创建两个优先级为1的任务,再将task1挂起,延时3秒后取消挂起
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
void task1(void *pvParam)
{
while (1)
{
printf("task1!\n");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void task2(void *pvParam)
{
while (1)
{
printf("task2!\n");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
TaskHandle_t pxTask = NULL;
xTaskCreate(task1, "task1", 4096, NULL, 1, pxTask);
xTaskCreate(task2, "task2", 4096, NULL, 1, NULL);
vTaskSuspend(pxTask);
vTaskDelay(3000 / portTICK_PERIOD_MS);
vTaskResume(pxTask);
}
代码示例:挂起任务调度器、取消挂起任务调度器
创建两个优先级为1的任务,进入task1,准备for循环时,挂起任务调度器,防止其他任务干扰,待for循环结束时,取消挂起任务调度器
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
void task1(void *pvParam)
{
printf("Test begin!\n");
vTaskSuspendAll();
for(int i = 0; i < 9999; i++)
{
for(int j = 0; j < 9999; j++)
{
;
}
}
xTaskResumeAll();
printf("Test end\n");
vTaskDelete(NULL);
}
void task2(void *pvParam)
{
while (1)
{
printf("task2!\n");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
xTaskCreate(task1, "task1", 4096, NULL, 1, NULL);
xTaskCreate(task2, "task2", 4096, NULL, 1, NULL);
}