一,任务的基本概念
FreeRTOS是一个支持多任务的操作系统,多个任务可以共享一个优先级,当任务configUSE_TIME_SLICING 为 1,则可以使用时间调度的方式共享处理器。
简而言之,freertos任务就是一系列任务的集合。
二,任务调度器
在任何时刻都只可以运行一个任务,这个运行什么任务是由调度器来决定的,宏观看上去好像是一起在执行一堆任务,但实际上确实任务交替执行,当任务进行切入和切出的时候,通过TCB来保存当前的执行环境,当任务再次执行的时候,就可以在原来停止的地方继续执行。
freertos提供的任务调度器是抢占式调度,即优先级越高的任务优先执行且可以抢占低优先级的任务,0为空闲任务
当使能下面的语句,一般限定优先级最大为32。
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 /* 1: 使用硬件计算下一个要运行的任务, 0: 使用软件算法计算下一个要运行的任务, 默认: 0 */
当一个系统中存在多个任务的时候,下一个就绪的任务的选择也是一个重要的问题,freertos提供了两种寻找最高优先级任务的方法
方法一:在就绪链表中进行依次查询,因为在创建任务的时候,以及将优先级从高到低进行了排序,然后找到对应任务的TCB。
方法二:计算前导零指令CLZ,直接计算出最高优先级的任务【stm32可以使用】
三,任务状态迁移
上一篇文章已经讲解了freertos任务的四种状态,但每种任务之间的切换也需要好好理解
1.创建任务---->就绪态:当任务创建成功后,则会进入就绪态,表明任务已经准备就绪,等待任务调度器的调度。
2.就绪态---->运行态:当任务切换的时候,就绪列表中的最高优先级的任务就会被执行,从而进入运行态。
3.运行态---->就绪态:当有更高级的任务创建或者从回复过来,会发生任务调度,最高优先级的任务会进入运行态,原本运行的任务变为就绪态【被高优先级的任务给抢占了】。
4.运行态---->阻塞态:正在运行的任务发生阻塞,则会从就绪列表中删除,任务变为阻塞态,然后任务切换到最高优先级的任务执行。
5.阻塞态---->就绪态:当阻塞的任务被恢复后,被恢复的任务加入就绪队列,从阻塞态变为就绪态,当被恢复的任务优先级高于当前执行的任务,则会发生任务切换,从就绪态转为运行态。
6.变为挂起状态:通过vTaskSuspend()API函数将任何状态的任务挂起,被挂起的任务得不到cpu的使用权,也不会参与调度,除非从挂起态解除。
7.解除挂起状态:调用vTaskResume(),如果被恢复的任务优先级高于当前执行的任务,则会发生任务切换,从就绪态转为运行态。
四,常用任务函数讲解
1.任务挂起函数 vTaskSuSpend()
挂起的任务无法得到CPU的使用权,无论当前的任务处于什么优先级,任务都通过vTaskSuSpend()函数被挂起,除非从挂起态解除,否则无法参与调度。
2.vTaskSuspendAll()
这个函数是将所有的任务都挂起,相当于是挂起了任务调度器,调度器被挂起,中断还是会使能,如果中断需要进行上下文的切换,任务就会被挂起,当任务调度器恢复后,才会切换执行任务。
3.任务恢复函数 vTaskResume()
挂起的任务想要解除挂起态,就需要这个函数,任务恢复就是让挂起的任务重新进入就绪状态,恢复的任务保持之前挂起前的状态,然后继续运行,在就绪队列中,优先级最高的任务则会先执行,任务也需要进行上下文切换【因为优先级不同,所以原本就绪队列中的最高优先级被这个解除挂起状态的更高优先级任务给抢占了,所以CPU会将之前的内容保存起来,去加载新的任务的全部信息】
4.xTaskResumeFromISR()
用于删除一个任务。当一个任务删除另外一个任务时,形参为要删除任 务创建时返回的任务句柄,如果是删除自身, 则形参为 NULL。 要想使用该函数必须在 FreeRTOSConfig.h 中把 INCLUDE_vTaskDelete 定义为 1,删除的任务将从所有就绪,阻塞, 挂起和事件列表中删除。
6.任务相对延时函数 vTaskDelete()
#define INCLUDE_vTaskDelay 1 /* 任务延时 */
7.绝对延时函数 vTaskDelayUntil()
这个函数可以让每个任务执行固定的时间且不受外部影响,任务从开始到下一次开始的时间是绝对不变的。
代码如下:
/**
****************************************************************************************************
* @file freertos.c
* @author 正点原子团队(ALIENTEK)
* @version V1.4
* @date 2022-01-04
* @brief FreeRTOS 任务挂起与恢复实验
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 精英F103开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
****************************************************************************************************
*/
#include "freertos_demo.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 */
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /* 任务函数 */
/* TASK3 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK3_PRIO 4 /* 任务优先级 */
#define TASK3_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task3Task_Handler; /* 任务句柄 */
void task3(void *pvParameters); /* 任务函数 */
/******************************************************************************************************/
/* LCD刷屏时使用的颜色 */
uint16_t lcd_discolor[11] = {WHITE, BLACK, BLUE, RED,
MAGENTA, GREEN, CYAN, YELLOW,
BROWN, BRRED, GRAY};
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
lcd_show_string(10, 10, 220, 32, 32, "STM32", RED);
lcd_show_string(10, 47, 220, 24, 24, "Task Susp & Resum", RED);
lcd_show_string(10, 76, 220, 16, 16, "ATOM@ALIENTEK", RED);
lcd_draw_rectangle(5, 110, 115, 314, BLACK);
lcd_draw_rectangle(125, 110, 234, 314, BLACK);
lcd_draw_line(5, 130, 115, 130, BLACK);
lcd_draw_line(125, 130, 234, 130, BLACK);
lcd_show_string(15, 111, 110, 16, 16, "Task1: 000", BLUE);
lcd_show_string(135, 111, 110, 16, 16, "Task2: 000", BLUE);
xTaskCreate((TaskFunction_t )start_task, /* 任务函数 */
(const char* )"start_task", /* 任务名称 */
(uint16_t )START_STK_SIZE, /* 任务堆栈大小 */
(void* )NULL, /* 传入给任务函数的参数 */
(UBaseType_t )START_TASK_PRIO, /* 任务优先级 */
(TaskHandle_t* )&StartTask_Handler); /* 任务句柄 */
vTaskStartScheduler();
}
/**
* @brief start_task
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建任务1 */
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
/* 创建任务2 */
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
/* 创建任务3 */
xTaskCreate((TaskFunction_t )task3,
(const char* )"task3",
(uint16_t )TASK3_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK3_PRIO,
(TaskHandle_t* )&Task3Task_Handler);
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/**
* @brief task1
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task1(void *pvParameters)
{
uint32_t task1_num = 0;
printf("进入任务1\r\n");
while (1)
{
lcd_fill(6, 131, 114, 313, lcd_discolor[++task1_num % 11]);
lcd_show_xnum(71, 111, task1_num, 3, 16, 0x80, BLUE);
printf("lalala\r\n");
vTaskDelay(500);
}
}
/**
* @brief task2
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task2(void *pvParameters)
{
uint32_t task2_num = 0;
printf("进入任务2\r\n");
while (1)
{
lcd_fill(126, 131, 233, 313, lcd_discolor[11 - (++task2_num % 11)]);
lcd_show_xnum(191, 111, task2_num, 3, 16, 0x80, BLUE);
printf("nenene\r\n");
vTaskDelay(500);
}
}
/**
* @brief task3
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task3(void *pvParameters)
{
uint8_t key = 0;
while (1)
{
key = key_scan(0);
switch (key)
{
case KEY0_PRES: /* 挂起任务1 */
{
vTaskSuspend(Task2Task_Handler);
printf("任务2挂起\r\n");
break;
}
case WKUP_PRES: /* 恢复任务1 */
{
vTaskResume(Task2Task_Handler);
printf("任务2恢复\r\n");
break;
}
default:
{
break;
}
}
vTaskDelay(10);
}
}