基础介绍
为什么要使用RTOS
个人理解:==因为复杂任务,需要实时调整任务==
Rtos特点
==系统架构==
==特点==:
- 实时性
- 实现功能划分为多个任务
- 任务调度
- 高优先级任务抢占低优先级任务
- 每个任务都有自己的栈空间------保存当前被迫打断的任务
使用理由
基础知识学习
三种任务调度方式
- 抢占式调度
主要是针对优先级不同的任务,每个任务都有一个优先级,优先级高的任务可以抢占
优先级低的任务.
- 时间片调度
主要针对优先级相同的任务,当多个任务的优先级相同时,任务调度器会在每一次系统
时钟节拍到的时候切换任务.
- 协程式调度
当前执行任务将会一直运行,同时高优先级的任务不会抢占低优先级任务
FreeRTOS现在虽然还支持,但是官方已经表示不再更新协程式调度.
任务状态
-
运行态
正在执行的任务,该任务就处于运行态,注意在STM32中,同一时间仅一个任务处于运行态
-
就绪态
如果该任务已经能够被执行,但当前还未被执行,那么该任务处于就绪态
-
阻塞态
如果一个任务因延时或等待外部事件发生,那么这个任务就处于阻塞态
- 挂起态
类似暂停,调用函数 vTaskSuspend() 进入挂起态,需要调用解挂函数vTaskResume()
才可以进入就绪态 .
==四种状态的相互切换==
任务的创建和删除
==3种常见API函数==
xTaskCreate() //动态创建任务,---内容空间由系统分配
xTaskCreateStatic() //静态创建任务--人为分配内容
vTaskDelete() //删除任务
==动态创建函数例子==
BaseTyoe_t xTaskCreate
(
TaskFunction_t pxTaskCode, //指向任务函数的指针
const char*const pcName, //任务名字
const configSTACK_DEPTH_TYPE usStackDepth, //任务堆栈大小,字为单位
void*const pvParameters, //传递给任务函数的参数
UBaseType_t uxPriority, //任务优先级(0-31)(越大越优先)
TaskHandle_t*const pxCreatedTask, //任务句柄,就是任务控制块
)
if (return==pdPASS) println("任务创建成功");
else println("任务创建失败");
编写流程
1. 配置宏congfigSUPPORT_DYNAMIC_ALLOCATION为1
1. 定义函数入口参数
1. 编写任务函数
==静态创建函数==
TaskHandle_t xTaskCreateStatic
(
TaskFunction_t pxTaskCode, //指向任务函数的指针
const char*const pcName, //任务函数名字
const uint32_t ulStackDepth, //任务堆栈大小,字为单位
void *const pvParameters, //传递的任务函数参数
UBaseType_t uxPriority //任务优先级
StackType_t*const puxStackBuffer //任务堆栈,数组,由用户分配
StaticTask_t*const pxTaskBuffer //任务控制指针
)
if(return==NULL) println("用户没有提供相应内容,任务创建失败");
if(return=="其他值") println("该值为任务句柄,任务创建成功")
编写流程
- 配置configSUPPORT_STATIC_ALLOCATION为1
- 定义空闲任务&定时器任务的任务堆栈及TCB
- 实现两个接口函数
- vApplicationGetldleTaskMemory() ----必有
- vApplicationGetTimerTaskMemory()
- 定义函数入口参数
- 编写任务函数
==删除函数==
xTaskToDelete("任务句柄") //如果传入删除为空则删除任务本身
流程
- 配置INCLUDE_vTaskDelete配置为1
- 入口参数输入需要删除的任务句柄
==两者总结==
- 动态创建任务更常见,简单
- 静态任务可以将任务堆栈放在特定内存位置
任务的挂起与回复
==3种常见的API函数==
vTaskSuspend() //挂起任务
vTaskResume() //恢复被挂起任务
xTaskResumeFromISR() //在中断中恢复被挂起任务
==挂起任务==
void vTaskSUspend(TaskHandle_t) //被挂起任务句柄
流程
- 先配置INCLUDE_vTaskSuspend为1
- 传入NULL为挂起任务本身
==恢复任务==
void vTaskResume(TaskHandle_t)
流程
- 配置INCLUDE_vTaskSuspend为1
- 无论任务被挂起多少次,只需要恢复一次,恢复后就会进入就绪态
==从中断中恢复==
BaseType_t xTaskResumeFromISR(TaskHandle_t)
/*
if return paTRUE ----任务恢复后需要进行任务切换
pdFALSE----任务恢复后不需要进行任务切换
*/
流程
- 配置INCLUDE_vTaskSuspend和INCLUDE_xTaskResumeISR为1
- 该函数专门用于中断函数中
- 中断函数的优先级在4–15之间,越小越优先
中断管理(处理紧急事件)
==中断执行机制==
- 中断请求:外设产生中断(GPIO外部中断,定时器中断);
- 响应中断:CPU停止执行当前程序,转而执行中端程序
- 退出中断:执行完毕,返回被打断出的程序,往下继续执行
==FREERTOS中断机制==
-
低于configMAX_SYSCALL_INTERRUPT_PRIORITY优先级里的中断才运行调用FREERTOS的API函数(5-15)
-
建议将所有的优先级位指定为抢占式级位,方便管理
- 调用函数HAL_NVIC_setPriorityGrouping(NVIC_PRIORITYGRPUP_4)
-
中断优先级数值越小越优先,任务优先级数值越大越优先
临界段代码保护
==定义==
:必须完整运行,不能被打断的代码端
==应用场景==
- 外设(严格按照时序初始化的外设:IIC,SPI)
- 系统(系统自身需求)
- 用户(用户需求)
==API函数介绍==
taskENTER_CRITICAL() //任务级进入临界区
taskEXIT_CRITICAL() //任务级退出临界区
taskENTER_CRITICAL_FROM_ISR() //中断级进入临界区
taskEXIT_CRITICAL_FROM_ISR() //中断级退出临界区
==任务级临界区示例==
taskENTER_CRITICAL();
{
;//临界区代码
}
taskEXIT_CRITICAL();
==中断级临界区示例==
unint32_t save_status;
save_status = taskENTER_CRITICAL_FROM_ISR();
{
//临界区代码
}
taskEXIT_CRITICAL_FROM_ISR(save_status);
==特点==
- 成对使用
- 支持嵌套
- 尽量保持耗时短
任务调度器的挂起和恢复
==API函数介绍==
VTaskSuspendALL() //挂起任务调度器
vTaskResumeAll() //恢复任务调度器
/*示例
vTaskSuspendALL();
{
;
}
xTaskResumeALL();
*/
==特点==
- 挂起任务调度器,不关闭中断
- 仅仅是防止了任务之间的资源争夺,中断照样可以响应
- 挂起调度器的方式,适用于临界区与任务与任务之间;既不需要去延时中断,也可以保证临界区的安全
列表和列表项
==简介:==
- 列表是 FreeRTOS 中的一个数据结构,概念上和链表有点类似,列表被用来跟踪 FreeRTOS中的任务
- 列表项就是存放在列表中的项目

- 列表相当于链表,列表项相当于节点,FreeRTOS 中的列表是一个双向环形链表
- 列表的特点:列表项间的地址非连续的,是人为的连接到一起的。列表项的数目是由后期添加的个数决定的,随时可以改变
- 数组的特点:数组成员地址是连续的,数组在最初确定了成员数量后期无法改变
==列表结构简介==
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /* 校验值 */
volatile UBaseType_t uxNumberOfItems; /* 列表中的列表项数量 */
ListItem_t * configLIST_VOLATILE pxIndex /* 用于遍历列表项的指针 */
MiniListItem_t xListEnd /* 末尾列表项 */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /* 校验值 */
} List_t;
==描述==
- 在该结构体中, 包含了两个宏,这两个宏是确定的已知常量, FreeRTOS通过检查这两个常量的值, 来判断列表的数据在程序运行过程中,是否遭到破坏 ,该功能一般用于调试, 默认是不开启的
- 成员uxNumberOfItems,用于记录列表中列表项的个数(不包含 xListEnd)
- 成员 pxIndex 用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项
- 成员变量 xListEnd 是一个迷你列表项,排在最末尾

==列表项==
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /* 用于检测列表项的数据完整性 */
configLIST_VOLATILE TickType_t xItemValue /* 列表项的值 */
struct xLIST_ITEM * configLIST_VOLATILE pxNext /* 下一个列表项 */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious /* 上一个列表项 */
void * pvOwner /* 列表项的拥有者 */
struct xLIST * configLIST_VOLATILE pxContainer; /* 列表项所在列表 */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /* 用于检测列表项的数据完整性 */
};
typedef struct xLIST_ITEM ListItem_t;
==特点==
- 成员变量 xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序
- 成员变量 pxNext 和 pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列表项
- 成员变量 pxOwner 用于指向包含列表项的对象(通常是任务控制块)
- 成员变量 pxContainer 用于指向列表项所在列表。

==迷你列表项==
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /* 用于检测数据完整性 */
configLIST_VOLATILE TickType_t xItemValue; /* 列表项的值 */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /* 上一个列表项 */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /* 下一个列表项 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
==特点==
- 迷你列表项也是列表项,但迷你列表项仅用于标记列表的末尾和挂载其他插入列表中的列表项
- 成员变量 xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序
- 成员变量 pxNext 和 pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列表项
- 迷你列表项只用于标记列表的末尾和挂载其他插入列表中的列表项,因此不需要成员变量 pxOwner 和 pxContainer,以节省内存开销

==列表与列表项的关系==\
==列表相关的API函数==
vListlinitialise() //初始化列表
vListlnitialseitem() //初始化列表项
vListlnserrEnd() //列表末尾插入列表项
vListlnsert() //列表插入列表项
uxListRemove() //列表移除列表项
//详解看资料第七章
具体案例分析
动态创建任务
==实验设计==:
- 设计4个任务,start_task,task1,task2,task3
- start_task–用于创建3个任务
- task1—LED闪烁500ms
- task2—LED2闪烁500ms
- task3—按下按键删除task1
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.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_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1( void * pvParameters );
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
void task2( void * pvParameters );
/* TASK3 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK3_PRIO 4
#define TASK3_STACK_SIZE 128
TaskHandle_t task3_handler;
void task3( void * pvParameters );
/******************************************************************************************************/
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
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(); //必须开启任务调度器,否则无法使用
}
void start_task( void * pvParameters )
{
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); //删除任务本身
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
while(1)
{
printf("task1正在运行!!!\r\n");
LED0_TOGGLE();
vTaskDelay(500);
}
}
/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
while(1)
{
printf("task2正在运行!!!\r\n");
LED1_TOGGLE();
vTaskDelay(500);
}
}
/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
printf("task3正在运行!!!\r\n");
key = key_scan(0);
if(key == KEY0_PRES)
{
if(task1_handler != NULL)
{
printf("删除task1任务\r\n");
vTaskDelete(task1_handler);
task1_handler = NULL;
}
}
vTaskDelay(10);
}
}
静态创建任务
*/
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.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_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
StackType_t start_task_stack[START_TASK_STACK_SIZE];
StaticTask_t start_task_tcb;
void start_task( void * pvParameters );
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
StackType_t task1_stack[TASK1_STACK_SIZE];
StaticTask_t task1_tcb;
void task1( void * pvParameters );
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
StackType_t task2_stack[TASK2_STACK_SIZE];
StaticTask_t task2_tcb;
void task2( void * pvParameters );
/* TASK3 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK3_PRIO 4
#define TASK3_STACK_SIZE 128
TaskHandle_t task3_handler;
StackType_t task3_stack[TASK3_STACK_SIZE];
StaticTask_t task3_tcb;
void task3( void * pvParameters );
/******************************************************************************************************/
/* 空闲任务配置 */
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
/* 软件定时器任务配置 */
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
/* 空闲任务内存分配 */
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
* ppxIdleTaskTCBBuffer = &idle_task_tcb;
* ppxIdleTaskStackBuffer = idle_task_stack;
* pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
/* 软件定时器内存分配 */
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
StackType_t ** ppxTimerTaskStackBuffer,
uint32_t * pulTimerTaskStackSize )
{
* ppxTimerTaskTCBBuffer = &timer_task_tcb;
* ppxTimerTaskStackBuffer = timer_task_stack;
* pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
start_task_handler = xTaskCreateStatic( (TaskFunction_t ) start_task,
(char * ) "start_task",
(uint32_t ) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(StackType_t * ) start_task_stack,
(StaticTask_t * ) &start_task_tcb );
vTaskStartScheduler();
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
task1_handler = xTaskCreateStatic( (TaskFunction_t ) task1,
(char * ) "task1",
(uint32_t ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(StackType_t * ) task1_stack,
(StaticTask_t * ) &task1_tcb );
task2_handler = xTaskCreateStatic( (TaskFunction_t ) task2,
(char * ) "task2",
(uint32_t ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(StackType_t * ) task2_stack,
(StaticTask_t * ) &task2_tcb );
task3_handler = xTaskCreateStatic( (TaskFunction_t ) task3,
(char * ) "task3",
(uint32_t ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(StackType_t * ) task3_stack,
(StaticTask_t * ) &task3_tcb );
vTaskDelete(start_task_handler);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
while(1)
{
printf("task1正在运行!!!\r\n");
LED0_TOGGLE();
vTaskDelay(500);
}
}
/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
while(1)
{
printf("task2正在运行!!!\r\n");
LED1_TOGGLE();
vTaskDelay(500);
}
}
/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
printf("task3正在运行!!!\r\n");
key = key_scan(0);
if(key == KEY0_PRES)
{
if(task1_handler != NULL)
{
printf("删除task1任务\r\n");
vTaskDelete(task1_handler);
task1_handler = NULL;
}
}
vTaskDelay(10);
}
}
任务挂起和恢复
/*2、实验设计:将设计四个任务:start_task、task1、task2、task3
start_task
task1
task2
四个任务的功能如下:
用来创建其他的三个任务
实现LED0每500ms闪烁一次
实现LED1每500ms闪烁一次
task3
判断按键按下逻辑,KEY0按下,挂起task1,按下KEY1在任务中恢复task1
按下KEY2,在中断中恢复task1(外部中断线实现)*/
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1( void * pvParameters );
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
void task2( void * pvParameters );
/* TASK3 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK3_PRIO 4
#define TASK3_STACK_SIZE 128
TaskHandle_t task3_handler;
void task3( void * pvParameters );
/******************************************************************************************************/
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
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();
}
void start_task( void * pvParameters )
{
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);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
uint32_t task1_num = 0;
while(1)
{
printf("task1_num:%d\r\n",++task1_num);
LED0_TOGGLE();
vTaskDelay(500);
}
}
/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
uint32_t task2_num = 0;
while(1)
{
printf("task2_num:%d\r\n",++task2_num);
LED1_TOGGLE();
vTaskDelay(500);
}
}
/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
key = key_scan(0);
if(key == KEY0_PRES)
{
printf("挂起task1\r\n");
vTaskSuspend(task1_handler);
}else if(key == KEY1_PRES)
{
printf("在任务中恢复task1\r\n");
vTaskResume(task1_handler);
}
vTaskDelay(10);
}
}
评论区