2.33 vTaskSuspend()
[[FreeRTOS_Reference_Manual_V10.0.0.pdf#page=151&selection=2,0,4,14|FreeRTOS_Reference_Manual_V10.0.0, page 151]]
任务的状态:
vTaskDelay(),它将被置于阻塞态,直到延迟结束。任务也可以通过阻塞来等待队列、信号量、事件组、通知或信号量事件。处于阻塞态的任务通常有一个”超时“周期,超时后任务将被超时,并被解除阻塞,即使该任务等待的事件没有发生。vTaskSuspend()和xTaskResume()API调用明确命令时,才会进入或退出挂起状态。
^9b7449
vTaskSuspend()#include "FreeRTOS.h"
#include "task.h"
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
必须将
INCLUDE_vTaskSuspend定义为1才能使用此函数。
此函数的作用是暂停任意任务,无论优先级如何,任务被暂停后将永远无法获取任何微控制器处理时间。
对vTaskSuspend()的调用不会累计次数,若多次使用vTaskSuspend()将任务挂起,也仅需一次vTaskResume()函数即可将任务恢复。
参数:
xTaskToSuspend:被挂起的任务句柄。传递空句柄将导致调用任务被暂停。在双核
ESP32处理器中,需要勾选menuconfig中的Component config -> FreeRTOS -> Kernel -> Run FreeRTOS only on first core,设置只在第一个核心上运行FreeRTOS,才可以使挂起示例挂起成功。
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
void myTask1(void *pvPara)
{
int delay = 5;
while (delay--) {
printf("Task1\n");
vTaskDelay(200 / portTICK_PERIOD_MS);
}
vTaskDelay(5000 / portTICK_PERIOD_MS);
vTaskDelete(NULL);
}
void myTask2(void *pvPara)
{
int delay = 5;
while (delay--) {
printf("Task2\n");
vTaskDelay(200 / portTICK_PERIOD_MS);
}
vTaskDelay(5000 / portTICK_PERIOD_MS);
vTaskDelete(NULL);
}
void app_main(void)
{
printf("Hello world! It's ESP32!\n");
TaskHandle_t myHandle1 = NULL;
TaskHandle_t myHandle2 = NULL;
xTaskCreate(myTask1, "myTask1", 2048, NULL, 1, &myHandle1);
xTaskCreate(myTask2, "myTask2", 2048, NULL, 2, &myHandle2);
vTaskSuspend(myHandle1); // 设置挂起 task1
}

输出结果表明,task1在创建之后运行了一瞬间(即创建task2所用的时间),然后就被挂起,直到主函数返回。
vTaskResume()#include "FreeRTOS.h"
#include "task.h"
void vTaskResume( TaskHandle_t xTaskToResume );
必须将
INCLUDE_vTaskSuspend定义为1才能使用此函数。
函数功能:恢复已挂起的任务。
由一次或多次调用vTaskSuspend()而挂起的任务可以通过单词调用vTaskResume()来恢复任务。
参数:
xTaskToResume:要恢复的任务句柄。#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
void myTask1(void *pvPara)
{
int delay = 5;
while (delay--) {
printf("Task1\n");
vTaskDelay(200 / portTICK_PERIOD_MS);
}
// vTaskDelay(5000 / portTICK_PERIOD_MS);
vTaskDelete(NULL);
}
void myTask2(void *pvPara)
{
int delay = 5;
while (delay--) {
printf("Task2\n");
vTaskDelay(200 / portTICK_PERIOD_MS);
}
// vTaskDelay(5000 / portTICK_PERIOD_MS);
vTaskDelete(NULL);
}
void app_main(void)
{
printf("Hello world! It's ESP32!\n");
TaskHandle_t myHandle1 = NULL;
TaskHandle_t myHandle2 = NULL;
xTaskCreate(myTask1, "myTask1", 2048, NULL, 1, &myHandle1);
xTaskCreate(myTask2, "myTask2", 2048, NULL, 2, &myHandle2);
vTaskSuspend(myHandle1); // 挂起 task1
printf("task1 suspended!\n");
vTaskDelay(1000 / portTICK_PERIOD_MS);
vTaskResume(myHandle1); // 恢复 task1
printf("task1 resumed!\n");
}

可以看到task1被挂起后,等待1s的时间,task1被恢复。
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
void myTask1(void *pvPara)
{
while (true) {
printf("Task1\n");
vTaskDelay(200 / portTICK_PERIOD_MS);
vTaskSuspend(NULL); // 将自己挂起
}
vTaskDelete(NULL);
}
void myTask2(void *pvPara)
{
while (true) {
printf("Task2\n");
vTaskDelay(200 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
void app_main(void)
{
printf("Hello world! It's ESP32!\n");
TaskHandle_t myHandle1 = NULL;
TaskHandle_t myHandle2 = NULL;
xTaskCreate(myTask1, "myTask1", 2048, NULL, 1, &myHandle1);
xTaskCreate(myTask2, "myTask2", 2048, NULL, 2, &myHandle2);
vTaskDelay(1000 / portTICK_PERIOD_MS);
vTaskResume(myHandle1); // 恢复 task1
printf("task1 resumed!\n");
}

两个任务被创建后,task1被自己挂起后,被app_main()恢复,然后又把自己挂起了。
由此可以知道,自我挂起,当外部将任务恢复后,只能执行一次,然后又被自己挂起
骚操作:
vTaskSuspendAll()#include "FreeRTOS.h"
#include "task.h"
void vTaskSuspendAll( void );
函数作用:
特殊要求:
xTaskResumeAll()#include "FreeRTOS.h"
#include "task.h"
void xTaskResumeAll( void );
函数作用:
vTaskSuspend()和xTaskResumeAll()使用注意事项:
- 两个函数需搭配使用,并且两个函数中间不能使用会引起上下文切换的函数,例如
printf,因为在 ESP32 的 FreeRTOS 中,printf内部会调用锁或其他 RTOS 服务,这在调度器暂停时是不允许的。- 在多核情况下,可以使用 FreeRTOS 提供的临界区。临界区只会屏蔽中断,不会完全停止调度器,因此可以安全地调用
printf函数。
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
void task1(void *pvPara)
{
while (true) {
printf("task1 begin!\n");
vTaskSuspendAll();
// 调度器暂停时,不允许调用影响 FreeRTOS 断言的函数,否则会断言失败
for (int i = 9999; i > 0; i--) {
for (int j = 3000; j > 0; j--) {
}
}
xTaskResumeAll();
printf("task1 end.\n");
}
}
void task2(void *pvPara)
{
while (true) {
printf("task2 is running...\n");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
xTaskCreate(task1, "task1", 2048, NULL, 1, NULL);
xTaskCreate(task2, "task2", 2048, NULL, 2, NULL);
}

实际运行测试中,task1 begin会先被打印,然后等待一会儿,task2 is running和task1 end.几乎一起被打印出来。
这是因为在代码中,xTaskResumeAll()执行之后,task2会立即执行,之后再执行printf("task1 end!\n");。
以上,vTaskSuspendAll函数和xTaskResumeAll函数测试成功。