今天我们将探讨FreeRTOS中的两个非常重要的函数:任务挂起和恢复函数。在实际的嵌入式系统开发中,我们常常需要在特定条件下暂停某些任务的执行,而在满足某些条件后再恢复这些任务的执行。这就像我们日常生活中的“暂停”和“继续”按钮。无论是为了节省资源,还是为了确保系统的稳定性,这两个操作都是至关重要的。通过理解和掌握任务的挂起与恢复,我们可以更灵活和高效地管理系统中的任务,确保各个任务能够协调运作,从而实现更稳定、更高效的系统表现。接下来,我们将深入了解如何使用这两个函数,以及它们在实际应用中的场景和注意事项。
目录
一、任务挂起与恢复函数
二、任务挂起函数
三、任务恢复函数(任务中恢复)
四、FreeRTOS 中断优先级概述
五、任务恢复函数(中断中恢复)
六、任务挂起与恢复API函数解析(选学)
6.1 任务挂起函数vTaskSuspend( )
6.2 (任务中调用)任务恢复函数vTaskResume( )
6.3(中断中调用)任务恢复函数xTaskResumeFromISR( )
七、任务挂起与恢复实验
八、任务挂起与任务恢复函数的使用场景
8.1 .临界区保护
8.2 资源管理
8.3 任务间同步
一、任务挂起与恢复函数
二、任务挂起函数
使用前先使能API函数
三、任务恢复函数(任务中恢复)
使用前先使能API函数
四、FreeRTOS 中断优先级概述
FreeRTOS 的配置文件 FreeRTOSConfig.h
中有两个关键宏定义:
configMAX_SYSCALL_INTERRUPT_PRIORITY //定义了可以使用 FreeRTOS API 的最高中断优先级。
configKERNEL_INTERRUPT_PRIORITY //定义了 FreeRTOS 内核的优先级
我们知道,Stm32一共有10个系统中断(内核中断),FreeRTOS便可以对这些系统中断进行管理;
第一个宏其实代表的是FreeRTOS可以管理的中断的最高的优先级;在 ARM Cortex-M 微控制器上,优先级数值越低,优先级越高。
configMAX_SYSCALL_INTERRUPT_PRIORITY
通常设置为一个中等优先级数值。例如,如果 FreeRTOS 管理的最高优先级为 5,则configMAX_SYSCALL_INTERRUPT_PRIORITY
可以设置为 5。
解释:中断优先级与 FreeRTOS API
如果在中断服务程序(ISR)中调用 FreeRTOS 的 API 函数,该 ISR 的优先级必须小于或者等于
configMAX_SYSCALL_INTERRUPT_PRIORITY
。这意味着中断优先级数值必须大于等于configMAX_SYSCALL_INTERRUPT_PRIORITY
的值。如果 ISR 的优先级高于
configMAX_SYSCALL_INTERRUPT_PRIORITY
(即数值低于configMAX_SYSCALL_INTERRUPT_PRIORITY
),在该 ISR 中调用 FreeRTOS API 函数可能会导致不可预期的行为,例如任务切换不正确或系统崩溃。这是因为 FreeRTOS 的调度器和其他 API 函数不是为在高优先级中断上下文中执行而设计的,即该中断不受FreeRTOS的管理。
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 5// 假设这是一个中断服务程序,优先级为6
void MyISR(void) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;// 恢复任务,只有优先级为6或更低的中断才能调用该函数vTaskResumeFromISR(xTaskHandle);// 如果恢复的任务优先级较高,可以请求上下文切换portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
五、任务恢复函数(中断中恢复)
使用前先使能API函数
六、任务挂起与恢复API函数解析(选学)
6.1 任务挂起函数vTaskSuspend( )
6.2 (任务中调用)任务恢复函数vTaskResume( )
6.3(中断中调用)任务恢复函数xTaskResumeFromISR( )
七、任务挂起与恢复实验
创建任务及实现任务函数:
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"/**********************START_TASK任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/#define START_TASK_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define START_TASK_PRIO 1 //定义任务优先级,0-31根据任务需求
TaskHandle_t start_task_handler; //定义任务句柄(结构体指针)
void start_task(void* args);/**********************TASK1任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define TASK1_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define TASK1_PRIO 2 //定义任务优先级,0-31根据任务需求
TaskHandle_t task1_handler; //定义任务句柄(结构体指针)
void task1(void* args);/**********************TASK2任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define TASK2_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define TASK2_PRIO 3 //定义任务优先级,0-31根据任务需求
TaskHandle_t task2_handler; //定义任务句柄(结构体指针)
void task2(void* args);/**********************TASK3任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define TASK3_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define TASK3_PRIO 4 //定义任务优先级,0-31根据任务需求
TaskHandle_t task3_handler; //定义任务句柄(结构体指针)
void task3(void* args);/*********开始任务用来创建其他三个任务,只创建一次,不能是死循环,同时创建完3个任务后删除任务1本身***********/
void start_task(void* args)
{taskENTER_CRITICAL(); /*进入临界区*/xTaskCreate( (TaskFunction_t) task1,(char *) "task1", ( configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,(void *) NULL,(UBaseType_t) TASK1_PRIO ,(TaskHandle_t *) &task1_handler );xTaskCreate( (TaskFunction_t) task2,(char *) "task2", ( configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,(void *) NULL,(UBaseType_t) TASK2_PRIO ,(TaskHandle_t *) &task2_handler ); xTaskCreate( (TaskFunction_t) task3,(char *) "task3", ( configSTACK_DEPTH_TYPE) TASK3_STACK_SIZE,(void *) NULL,(UBaseType_t) TASK3_PRIO ,(TaskHandle_t *) &task3_handler ); vTaskDelete(NULL); //删除开始任务自身,传参NULLtaskEXIT_CRITICAL(); /*退出临界区*///临界区内不会进行任务的调度切换,出了临界区才会进行任务调度,抢占式
}/********其余三个任务的任务函数,无返回值且是死循环***********//***任务1:实现LED0每500ms翻转一次*******/
void task1(void* args)
{uint32_t task1_num=0;while(1){printf("task1_num:%d\n",++task1_num);GPIO_ToggleBits(GPIOF,GPIO_Pin_9 );vTaskDelay(500); //FreeRTOS自带的延时函数}}/***任务2:实现LED1每500ms翻转一次*******/
void task2(void* args)
{uint32_t task2_num=0;while(1){printf("task2_num:%d\n",++task2_num);GPIO_ToggleBits(GPIOF,GPIO_Pin_10 );vTaskDelay(500); //FreeRTOS自带的延时函数}}/***任务3:判断按键KEY0,按下KEY0,任务1删除*******/
void task3(void* args)
{while(1){if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0) //表示按键Key0按下{vTaskSuspend(task1_handler); //挂起任务1} else if (GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0) //表示按键Key1按下{vTaskResume(task1_handler); //恢复任务1}vTaskDelay(10); }}//FreeRTO入口例程函数,无参数,无返回值
void freertos_demo(void)
{xTaskCreate( (TaskFunction_t) start_task,(char *) "start_task", ( configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,(void *) NULL,(UBaseType_t) START_TASK_PRIO ,(TaskHandle_t *) &start_task_handler );vTaskStartScheduler(); //开启任务调度器}
主函数调用入口函数,操作系统开始进行任务的切换和调度
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "myled.h"
#include "mykey.h"
#include "myusart.h"#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"int main(void)
{//硬件初始化My_UsartInit();LED_Init();KEY_Init();//调用入口函数freertos_demo();}
八、任务挂起与任务恢复函数的使用场景
8.1 .临界区保护
在某些情况下,需要确保一段代码不被中断或其他任务打断。例如,在对共享资源进行操作时,可以使用任务挂起和恢复函数来实现临界区保护。
void vTaskSuspendAll( void )
可将所有任务挂起,实质是将任务调度器锁定。
// 共享资源操作示例
void critical_section(void) {// 挂起其他任务vTaskSuspendAll();// 临界区代码shared_resource++;// 恢复任务调度xTaskResumeAll();
}
8.2 资源管理
在访问某些硬件资源(如SPI总线、I2C总线等)时,需要确保在使用资源期间不会被其他任务干扰。这时可以挂起其他可能访问该资源的任务,使用完毕后再恢复它们.
void access_resource_task(void *pvParameters) {// 挂起可能使用相同资源的任务vTaskSuspend(task_handle);// 操作硬件资源use_hardware_resource();// 恢复任务vTaskResume(task_handle);
}
8.3 任务间同步
在某些场景下,可能需要一个任务等待另一个任务完成特定操作后再继续执行。可以通过挂起和恢复任务实现这种同步机制。
// 任务A
void taskA(void *pvParameters) {// 执行某些操作// 挂起任务A自己vTaskSuspend(NULL);// 当任务B完成操作后任务A将被恢复
}// 任务B
void taskB(void *pvParameters) {// 执行任务A需要等待的操作// 恢复任务AvTaskResume(task_handleA);
}
需要注意的是,滥用任务挂起和恢复函数可能导致系统响应变慢或引入难以调试的错误。在实际应用中,应尽量通过其他FreeRTOS提供的同步机制(如信号量、消息队列等)来实现任务间的协调和同步。
至此,任务挂起与恢复函数就已经讲解完毕!初次学习,循序渐进,一步步掌握即可!以上就是全部内容!请务必掌握,创作不易,欢迎大家点赞加关注评论,您的支持是我前进最大的动力!下期再见!