目录
1、初识FreeRTOS
2、FreeRTOS 任务的状态
3、FreeRTOS 的任务
任务的创建和删除
1、xTaskCreate():使用静态的方法创建一个任务。
2、xTaskCreateStatic():使用静态的方法创建一个任务。
4、vTaskDelete():删除一个任务。
任务挂起和恢复
1、vTaskSuspend():挂起一个任务。
2、vTaskResume():恢复一个任务的运行。
3、xTaskResumeFromISR():中断服务函数中恢复一个任务的运行
4、FreeRTOS 任务切换
5、任务优先级
6、多任务的实验
我们看一下 FreeRTOS 的名字,可以分为两部分:Free 和 RTOS, Free 就是免费的、 自由的、不受约束的意思, RTOS 全称是 Real Time Operating System, 中文名就是实时操作系统。可以看出 FreeROTS 就是一个免费的 RTOS 类系统。这里要注意, RTOS 不是指某一个确定的系统, 而是指一类系统。 比如 UCOS, FreeRTOS, RTX, RT-Thread 等这些都是 RTOS 类操作系统。
实时操作系统允许多个任务同时运行, 这个叫做多任务, 实际上, 一个处理器核心在某一时刻只能运行一个任务。 操作系统中任务调度器的责任就是决定在某一时刻究竟运行哪个任务, 任
务调度在各个任务之间的切换非常快!这就给人们造成了同一时刻有多个任务同时运行的错觉。
操作系统的分类方式可以由任务调度器的工作方式决定, 比如有的操作系统给每个任务分
配同样的运行时间,时间到了就轮到下一个任务, Unix 操作系统就是这样的。 RTOS 的任务调
度器被设计为可预测的, 而这正是嵌入式实时操作系统所需要的, 实时环境中要求操作系统必
须对某一个事件做出实时的响应,因此系统任务调度器的行为必须是可预测的。 像 FreeRTOS 这
种传统的 RTOS 类操作系统是由用户给每个任务分配一个任务优先级, 任务调度器就可以根据
此优先级来决定下一刻应该运行哪个任务。
● 运行态
当一个任务正在运行时, 那么就说这个任务处于运行态, 处于运行态的任务就是当前正在
使用处理器的任务。 如果使用的是单核处理器的话那么不管在任何时刻永远都只有一个任务处
于运行态。
● 就绪态
处于就绪态的任务是那些已经准备就绪(这些任务没有被阻塞或者挂起), 可以运行的任务,
但是处于就绪态的任务还没有运行,因为有一个同优先级或者更高优先级的任务正在运行!
● 阻塞态
如果一个任务当前正在等待某个外部事件的话就说它处于阻塞态, 比如说如果某个任务调
用了函数 vTaskDelay()的话就会进入阻塞态, 直到延时周期完成。任务在等待队列、信号量、事
件组、通知或互斥信号量的时候也会进入阻塞态。任务进入阻塞态会有一个超时时间,当超过
这个超时时间任务就会退出阻塞态,即使所等待的事件还没有来临!
● 挂起态
像阻塞态一样,任务进入挂起态以后也不能被调度器调用进入运行态, 但是进入挂起态的
任务没有超时时间。任务进入和退出挂起态通过调用函数 vTaskSuspend()和 xTaskResume()。
FreeRTOS利用这些状态使任务更加灵活的运行,更加节约CPU资源,实时性更强。这些状态都可以由相对应的API函数来控制任务变成哪种状态!!!!!
FreeRTOS 最基本的功能就是任务管理,而任务管理最基本的操作就是创建和删除任务,官方都给我们提供的相对应的API函数来创建和删除任务。
函数 | 描述 |
xTaskCreate() | 使用动态的方法创建一个任务。 |
xTaskCreateStatic() | 使用静态的方法创建一个任务。 |
xTaskCreateRestricted() | 创建一个使用 MPU 进行限制的任务,相关内存使用动态内存分配。 |
vTaskDelete() | 删除一个任务。 |
函数原型
参数:
函数原型
返回值:
3、 xTaskCreateRestricted():创建一个使用 MPU 进行限制的任务,相关内存使用动态内存分配
此函数也是用来创建任务的, 只不过此函数要求所使用的 MCU 有 MPU(内存保护单元),
用此函数创建的任务会受到 MPU 的保护。 其他的功能和函数 xTaxkCreate()一样。
函数原型
返回值
函数原型
参数
有时候我们需要暂停某个任务的运行,过一段时间以后在重新运行。这个时候要是使用任
务删除和重建的方法的话那么任务中变量保存的值肯定丢失了! FreeRTOS 给我们提供了解决
这种问题的方法, 那就是任务挂起和恢复,当某个任务要停止运行一段时间的话就将这个任务
挂起,当要重新运行这个任务的话就恢复这个任务的运行。
函数 | 描述 |
vTaskSuspend() | 挂起一个任务。 |
vTaskResume() | 恢复一个任务的运行。 |
xTaskResumeFromISR() | 中断服务函数中恢复一个任务的运行 |
此函数用于将某个任务设置为挂起态, 进入挂起态的任务永远都不会进入运行态。退出挂
起态的唯一方法就是调用任务恢复函数 vTaskResume()或 xTaskResumeFromISR()。
函数原型
参数
将一个任务从挂起态恢复到就绪态, 只有通过函数 vTaskSuspend()设置为挂起态的任务才
可以使用 vTaskRexume()恢复!
函数原型
参数
此函数是 vTaskResume()的中断版本,用于在中断服务函数中恢复一个任务后
函数原型
参数
RTOS 系统的核心是任务管理,而任务管理的核心是任务切换,任务切换决定了任务的执
行顺序,任务切换效率的高低也决定了一款系统的性能, 尤其是对于实时操作系统。
在使用 FreeRTOS 的过程中我们通常会在一个任务函数中使用延时函数对这个任务延时,
当执行延时函数的时候就会进行任务切换, 并且此任务就会进入阻塞态,直到延时完成,任务
重新进入就绪态。
每 个 任 务 都 可 以 分 配 一 个 从 0~(configMAX_PRIORITIES-1) 的 优 先 级 , 优先级数字越低表示任务的优先级越低, 0 的优先级最低, configMAX_PRIORITIES-1 的优先级最高。空闲任务的优先级最低,为 0。
FreeRTOS 调度器确保处于就绪态或运行态的高优先级的任务获取处理器使用权,换句话说
就是处于就绪态的最高优先级的任务才会运行。 当宏 configUSE_TIME_SLICING 定义为 1 的时
候多个任务可以共用一个优先级,数量不限。默认情况下宏 configUSE_TIME_SLICING 在文件
FreeRTOS.h 中已经定义为 1。此时处于就绪态的优先级相同的任务就会使用时间片轮转调度器
获取运行时间。
#include "sys.h"
#include "usart.h"
#include "gpio.h"
#include "delay.h"
#include "FreeRTOS.h"
#include "task.h"
//LCD刷屏时使用的颜色
//任务优先级
#define START_TASK_PRIO 4
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED0_TASK_PRIO 0
//任务堆栈大小
#define LED0_STK_SIZE 20
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 0
//任务堆栈大小
#define LED1_STK_SIZE 20
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
delay_init(); //延时函数初始化
My_USART_Init(); //初始化串口
MX_GPIO_Init(); //初始化GPIO
//创建开始任务
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(); //开启任务调度
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED0任务
xTaskCreate((TaskFunction_t )led0_task,
(const char* )"led0_task",
(uint16_t )LED0_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED0_TASK_PRIO,
(TaskHandle_t* )&LED0Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED0任务函数
void led0_task(void *pvParameters)
{
while(1)
{
LED0 = !LED0;
vTaskDelay(500);
}
}
/* jwiw */
//LED1任务函数
void led1_task(void *pvParameters)
{
uint16_t flag=0;
while(1)
{
if(flag==0)
{
flag=1;vTaskDelay(200);
}
else flag=0;
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
vTaskDelay(200);
GPIO_SetBits(GPIOE,GPIO_Pin_5);
vTaskDelay(800);
}
}
实验现象:
LED灯800ms亮,200ms灭;LED0以500ms闪烁。