目录
资料来源于硬件家园:FreeRTOS实时操作系统第四讲 - 任务管理(创建,删除,挂起,恢复) (qq.com)
一、任务的基本概念
1、从系统的角度看,任务是竞争系统资源的最小运行单元。
2、FreeRTOS是一个支持多任务的操作系统,在FreeRTOS中,任务可以使用或等待CPU、使用内存空间等系统资源,并独立于其他任务运行。
3、每个任务在自己的环境中运行,在任何时刻,只有一个任务得到运行,FreeRTOS调度器决定运行哪个任务。调度器会不断的启动、停止每一个任务,宏观上看,所有的任务都在同时进行。
4、在FreeRTOS中,每个任务都有自己的栈空间(一段连续的内存),用于保存任务运行环境。任务越多,需要的栈空间就越大,而一个系统能运行多少个任务,取决于系统可用的内存。
5、任务通常会运行在一个死循环中,不会退出,如果不需要某个任务,可以调用FreeRTOS中的删除任务API函数将任务删除,释放系统资源。
二、任务状态的概念
FreeRTOS系统中的任务有四种状态:
1、Running—运行态:
当任务处于实际运行时被称之为运行态,即 CPU 的使用权被这个任务占用。
2、Ready—就绪态
处于就绪态的任务是指那些能够运行(没有被阻塞和挂起),但是当前没有运行的任务,因为同优先级或更高优先级的任务正在运行。
3、Blocked—阻塞态
由于等待信号量,消息队列,事件标志组等而处于的状态被称之为阻塞态,另外任务调用延迟函数也会处于阻塞态。
4、Suspended—挂起态
类似阻塞态,通过调用函数 vTaskSuspend()对指定任务进行挂起,挂起后这个任务将不被执行,只有调用函数 xTaskResume()才可以将这个任务从挂起态恢复。
三、任务状态的切换
四、系统启动
1、vTaskStartScheduler()函数
/*Start scheduler */
osKernelStart();
osStatus osKernelStart (void)l{
vTaskStartScheduler();
return osOK;
}
1.1 作用
启动 FreeRTOS调度
1.2 启动函数介绍
函数原型:void vTaskStartScheduler( void );
函数描述:函数 vTaskStartScheduler 用于启动 FreeRTOS 调度器,即启动 FreeRTOS 的多任务执行。
使用这个函数要注意以下几个问题:
①空闲任务和可选的定时器任务是在调用这个函数后自动创建的。
②正常情况下这个函数是不会返回的。如果有返回,极有可能是用于定时器任务或者空闲任务的内存空间不足造成创建失败,此时需要加大FreeRTOS可管理的内存空间。
2、空闲任务
空闲任务是FreeRTOS系统中没有其它任务运行时自动进入的系统任务,不需要用户创建,启用调度器时,调度器会自动创建空闲任务。
2.1 空闲任务的作用
① 执行删除任务时,系统并不会立即释放任务的内存空间,只会将任务添加到结束列表中,真正的系统资源回收工作在空闲任务中完成。
②可以在空闲任务中实现低功耗功能。
五、任务创建
5.1 创建任务的方式
① 静态创建任务,xTaskCreateStatic(),需要自行定义任务栈空间与任务控制块,一般不采用,不作介绍。
②动态创建任务,xTaskCreate(),系统动态分配任务栈空间与任务控制块,应用采用此方式。
5.2 动态创建任务函数详解
函数原型:
函数描述:函数 xTaskCreate 用于实现 FreeRTOS 操作系统的任务创建,并且还可以自定义任务栈的大小。
①第 一 个参数填创建任务的函数名
②第 二 个参数是任务名,这个参数主要用于调试的时候方便看是哪个任务
③第 三 个参数是任务栈大小,单位 word,也就是 4 字节
④ 第 四 个参数是创建的任务函数的形参
⑤第 五 个参数是定义任务优先级
⑥第 六 个参数是任务句柄,用于区分不同的任务
5.3 使用举例
STM32cubeMX创建方法:
方法详解:
① 首先利用宏定义定义任务参数结构体变量
#name:将name转化为字符串,等效于"name"
os_thread_def__name:将name以字符串方式拼接,等效于"os_thread_def_name"
① 调用动态创建函数创建任务,形参为上面的结构体地址
六、任务删除
6.1 删除任务函数详解
函数原型:void vTaskDelete( TaskHandle_t xTask ); /* 任务句柄 */
函数描述:函数 vTaskDelete 用于实现 FreeRTOS 操作系统的任务删除。
①第 1 个参数填要删除任务的句柄
使用这个函数要注意以下问题:
① 使用此函数需要在 FreeRTOSConfig.h 配置文件中配置如下宏定义为 1#define INCLUDE_vTaskDelete 1
② 如果用往此函数里面填的任务 ID 是 NULL,即数值 0 的话,那么删除的就是当前正在执行的任务,此任务被删除后,FreeRTOS 会切换到任务就绪列表里面下一个要执行的最高优先级任务。
③ 在 FreeRTOS 中,创建任务所需的内存需要在空闲任务中释放,如果用户在 FreeRTOS 中调用了这个函数的话,一定要让空闲任务有执行的机会,否则这块内存是无法释放的。另外,创建的这个任务在使用中申请了动态内存,这个内存不会因为此任务被删除而删除,这一点要注意,一定要在删除前将此内存释放。
6.2 使用举例
七、任务挂起
7.1 挂起任务函数详解
函数原型:void vTaskSuspend( TaskHandle_t xTaskToSuspend); /* 任务句柄 */
函数描述:函数 vTaskSuspend 用于实现 FreeRTOS 操作系统的任务挂起。
第 1 个参数填要挂起任务的句柄
使用这个函数要注意以下问题:
① 使用此函数需要在 FreeRTOSConfig.h 配置文件中配置如下宏定义为 1#define INCLUDE_vTaskSuspend 1
② 如果用往此函数里面填的任务 ID 是 NULL,即数值 0 的话,那么挂起的就是当前正在执行的任务,此任务被挂起后,FreeRTOS 会切换到任务就绪列表里面下一个要执行的高优先级任务。
③ 多次调用此函数的话,只需调用一次 vTaskResume 即可将任务从挂起态恢复。
7.2 使用举例
八、任务恢复(普通方式与中断方式)
8.1 普通方式恢复任务函数详解
函数原型:void vTaskResume( TaskHandle_t xTaskToResume); /* 任务句柄 */
函数描述:函数 vTaskResume 用于实现 FreeRTOS 操作系统的任务挂起
①第 1 个参数填要恢复任务的句柄
使用这个函数要注意以下问题:
① 使用此函数需要在 FreeRTOSConfig.h 配置文件中配置如下宏定义为 1#define INCLUDE_vTaskSuspend 1
② 多次调用函数 vTaskSuspend 的话,只需调用一次 vTaskResume 即可将任务从挂起态恢复。
③ 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的xTaskResumeFromISR(),以后缀 FromISR 结尾。
使用举例:
8.2 中断方式恢复任务函数详解
函数原型:BaseType_t vTaskResumeFromISR( TaskHandle_t xTaskToResume); /* 任务句柄 */
函数描述:函数 vTaskResumeFromISR 用于实现 FreeRTOS 操作系统的任务恢复。
①第 1 个参数填要恢复任务的句柄
使用这个函数要注意以下问题:
① 使用此函数需要在 FreeRTOSConfig.h 配置文件中配置如下宏定义为 1#define INCLUDE_xResumeFromISR 1
② 多次调用函数 vTaskSuspend 的话,只需调用一次 vTaskResumeFromISR 即可将任务从挂起态恢复。
③ 如果用户打算采用这个函数实现中断与任务的同步,要注意一种情况,如果此函数的调用优先于函数vTaskSuspend 被调用,那么此次同步会丢失,这种情况下建议使用信号量来实现同步。
④此函数是用于中断服务程序中调用的,故不可以在任务中使用此函数,任务中使用的是 vTaskResume。
使用举例:
九、任务管理编程
9. 1 cube配置
修改FreeRTOS_Debug的配置
修改内存分配方式为动态分配
9.2 手动创建任务
在freertos.c中进行定义
①定义句柄
/* USER CODE BEGIN Variables */
extern volatile uint32_t CPU_RunTime;
osThreadId KEY1Handle;
/* USER CODE END Variables */
②定义入口函数
/* USER CODE BEGIN FunctionPrototypes */
void KEY1_Task(void const * argument);
/* USER CODE END FunctionPrototypes */
③创建任务
/* USER CODE BEGIN RTOS_THREADS */
osThreadDef(KEY1, KEY1_Task, osPriorityIdle, 0, 128);
KEY1Handle = osThreadCreate(osThread(KEY1), NULL);
/* USER CODE END RTOS_THREADS */
④入口函数实现
/* USER CODE BEGIN Application */
void KEY1_Task(void const * argument)
{
/* USER CODE BEGIN KEY1_Task */
/* Infinite loop */
for(;;)
{
osDelay(1);
}
/* USER CODE END KEY1_Task */
}
/* USER CODE END Application */
9.3 手动删除、挂起任务
void KEY0_Task(void const * argument)
{
/* USER CODE BEGIN KEY0_Task */
/* Infinite loop */
for(;;)
{
int KeyState=0;
//按键检测
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==GPIO_PIN_RESET)
{
KeyState=1;
while(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==GPIO_PIN_RESET)
{
osDelay(20);//必须用阻塞延时
}
}
if(KeyState)
{
vTaskSuspend(LED1Handle);//挂起LED1_Task
HAL_UART_Transmit(&huart2,(uint8_t*)"KEY0 depress... \r\n", 20, HAL_MAX_DELAY);
if(LED0Handle != NULL)
{
vTaskDelete(LED0Handle);
LED0Handle=NULL;
HAL_UART_Transmit(&huart2,(uint8_t*)"delete LED0_Task... \r\n", 20, HAL_MAX_DELAY);
}
else
{
HAL_UART_Transmit(&huart2,(uint8_t*)"None LED0_Task... \r\n", 20, HAL_MAX_DELAY);
}
}
osDelay(1);
}
/* USER CODE END KEY0_Task */
}
9.4 手动创建、恢复任务
void KEY1_Task(void const * argument)
{
/* USER CODE BEGIN KEY1_Task */
/* Infinite loop */
for(;;)
{
int KeyState=0;
//按键检测
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)==GPIO_PIN_RESET)
{
KeyState=1;
while(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)==GPIO_PIN_RESET)
{
osDelay(20);//必须用阻塞延时
}
}
if(KeyState)
{
vTaskResume(LED1Handle);//恢复LED1_Task
HAL_UART_Transmit(&huart2,(uint8_t*)"KEY1 depress... \r\n", 20, HAL_MAX_DELAY);
if(LED0Handle == NULL)
{
osThreadDef(LED0, LED0_Task, osPriorityNormal, 0, 128);
LED0Handle = osThreadCreate(osThread(LED0), NULL);
if(LED0Handle!=NULL)
HAL_UART_Transmit(&huart2,(uint8_t*)"LED0_Task create success... \r\n", 20, HAL_MAX_DELAY);
}
else
{
HAL_UART_Transmit(&huart2,(uint8_t*)"exist LED0_Task ... \r\n", 20, HAL_MAX_DELAY);
}
}
osDelay(1);
}
/* USER CODE END KEY1_Task */
}