FreeRTOS之任务
• 空闲任务(Idle Task)是FreeRTOS调度器启动时自动创建的后台任务,具有以下特性:1.固定为0优先级(时刻都可以抢占),仅在所有其他任务阻塞或挂起时运行。2.必要性:确保系统始终有任务可运行,防止处理器空转。3.资源占用:默认堆栈大小为(通常较小,如128字)。5.4.2 空闲任务的核心功能•自删除任务的内存释放:当任务调用 vTaskDelete(NULL) 删除自身时,其任务控
1.任务的基本特性
1.1 任务的定义与结构
1.2 任务类型
2.任务状态与转换
FreeRTOS 的任务包含五种状态,需要通过调度器动态切换:
补充:
3. 任务调度机制
3.1 调度概述
3.2 抢占式调度的详解
3.2.1 如图
3.2.2 运行条件:
• 依次创建三个任务:task1、task2、task3,优先级分别为1、2、3。
3.2.3 运行过程如下:
3.3 时间片轮转详解
• 任务运行固定时间片(由 configTICK_RATE_HZ 定义,如1ms的时间片)后,会自动切换至同优先级的下一个任务。
3.3.1 如图

3.3.2 运行条件
• 依次创建三个任务:task1、task2、task3,优先级均为1。
3.3.3. 运行过程
4. 任务创建函数
• 任务创建函数主要有两种:动态创建与静态创建。 主要有以下的区别:
4.1 动态创建任务(自动分配):xTaskCreat。
• 函数原型:
BaseType_t xTaskCreate(
TaskFunction_t pvTaskCode, // 任务函数指针(函数原型:void task(void *pvParams))
const char *const pcName, // 任务名称(调试用,长度≤configMAX_TASK_NAME_LEN)
configSTACK_DEPTH_TYPE usStackDepth, // 堆栈深度(单位:字,如1024字=4KB for ARM)
void *const pvParameters, // 任务参数指针(传递给任务函数)
UBaseType_t uxPriority, // 优先级(0最低,configMAX_PRIORITIES-1最高)
TaskHandle_t *const pxCreatedTask // 任务句柄(用于后续操作如删除、挂起)
);
//此函数创建的任务会立刻进入就绪态,由任务调度器调度运行
• 返回值:
• pdPASS :任务创建成功。
• errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY :堆内存不足,创建失败。
• 注意: 需要将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1。
4.2 动态创建任务函数的内部实现
• 申请堆栈内存和任务控制块(TCB)内存。
• 给TCB结构体成员赋值。
• 添加新任务到就绪列表。
• 如图,任务控制块(TCB)的部分成员
typedef struct tskTaskControlBlock
{
volatile StackType_t * pxTopOfStack; /* 任务栈栈顶,必须为 TCB 的第一个成员 */
ListItem_t xStateListItem; /* 任务状态列表项 */
ListItem_t xEventListItem; /* 任务事件列表项 */
UBaseType_t uxPriority; /* 任务优先级,数值越大,优先级越大 */
StackType_t * pxStack; /* 任务栈起始地址 */
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务名字 */
//…省略很多条件编译的成员
} tskTCB;
//任务栈栈顶指针,在任务切换时的任务上下文保存、任务恢复息息相关。每个任务都有属于自己的任务控制块,类似身份证。
4.3 静态(内存分配)创建任务:xTaskCreateStatic。
4.3.1 函数原型
TaskHandle_t xTaskCreateStatic(
TaskFunction_t pvTaskCode,
const char *const pcName,
uint32_t ulStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
StackType_t *const puxStackBuffer, // 用户预分配的堆栈内存
StaticTask_t *const pxTaskBuffer // 用户预分配的TCB内存
);
4.3.2 返回值
4.3.3 使用流程
1. 配置宏与预分配内存
• 在 FreeRTOSConfig.h 中启用静态内存分配:
#define configSUPPORT_STATIC_ALLOCATION 1 // 启用静态内存分配
• 预分配 TCB 和堆栈内存:
//空闲任务配置
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,
configSTACK_DEPTH_TYPE * pulIdleTaskStackSize )
{
* ppxIdleTaskTCBBuffer = &idle_task_tcb;
* ppxIdleTaskStackBuffer = idle_task_stack;
* pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
//软件定时器内存分配
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
StackType_t ** ppxTimerTaskStackBuffer,
configSTACK_DEPTH_TYPE * pulTimerTaskStackSize)
{
* ppxTimerTaskTCBBuffer = &timer_task_tcb;
* ppxTimerTaskStackBuffer = timer_task_stack;
* pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
3. 调用xTaskCreateStatic创建任务
TaskHandle_t xStaticTaskHandle = xTaskCreateStatic(
vTaskDemo, // 任务函数指针
"StaticTask", // 任务名称
1024, // 堆栈深度
NULL, // 任务参数
3, // 优先级
xTaskStack, // 预分配的堆栈地址
&xTaskTCB // 预分配的 TCB 地址
);
//返回值检查:若 xStaticTaskHandle != NULL 表示成功。
4.3.4 静态创建内部实现
1. TCB结构体成员赋值。
2. 添加新任务到就绪列表。
5. 任务删除
5.1 任务删除函数
void vTaskDelete(TaskHandle_t xTaskToDelete);
//参数:xTaskToDelete :任务句柄。若为 NULL ,删除当前任务自身(当前正在运行的任务)。
//前提:使用删除任务函数,需将宏 INCLUDE_vTaskDelete 配置为 1
//描述:
//该函数用于删除已被创建的任务,被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表。
//需要注意的是,空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存,则需要由用户在任务被删除前提前释放,否则将导致内存泄露
5.2 行为
5.3 内部实现过程
5.4 什么是空闲任务
5.4.1 空闲任务的概述
• 空闲任务(Idle Task)是 FreeRTOS 调度器启动时自动创建的后台任务,具有以下特性:
5.4.2 空闲任务的核心功能
6. 任务的挂起与恢复
6.1 函数介绍
6.1.1 vTaskSuspend() - 任务挂起函数
void vTaskSuspend() - 任务挂起函数
功能:将指定任务置为挂起态,该任务将不再参与调度,直到被显式恢复。
参数xTaskToSuspend :目标任务的句柄(传 NULL 表示挂起自身)。
源码行为:
从就绪/阻塞列表中移除任务,并插入挂起列表 xSuspendedTaskList 。
若挂起自身,立即触发上下文切换 portYIELD_WITHIN_API() 。
配置依赖:需在FreeRTOSConfig.h 中启用宏 INCLUDE_vTaskSuspend=1 。
6.1.2 vTaskResume() - 任务恢复函数
void vTaskResume() - 任务恢复函数
功能:将挂起的任务恢复到就绪态。
参数:xTaskToResume :目标任务的句柄。
限制:仅能恢复通过 vTaskSuspend() 挂起的任务,不支持恢复因事件阻塞的任务。
无返回值:直接修改任务状态至就绪列表。
6.1.3 xTaskResumeFromISR() - 中断中任务恢复函数
xTaskResumeFromISR() - 中断中任务恢复函数
功能:在中断服务程序(ISR)中恢复挂起的任务。
参数:xTaskToResume:目标任务的句柄。
返回值:
pdTRUE :恢复的任务优先级≥当前任务,需调用 portYIELD_FROM_ISR() 触发上下文切换。
pdFALSE :恢复的任务优先级较低,无需立即切换。
配置依赖:需启用宏 INCLUDE_xTaskResumeFromISR=1 。
注意:
NVIC中断优先级分组需设置为分组4;
中断服务程序中要调用freeRTOS的API函数则中断优先级不能高于FreeRTOS所管理的最高优先级(只能在这个范围内5-15)
6.2 注意
6.2.1 非嵌套性
• 无论调用多少次 vTaskSuspend() 挂起任务,只需调用一次 vTaskResume() 即可恢复。
6.2.2 中断安全性
7. 实验
7.1 实验一
1. 动态创建三个任务:task1、task2、task3,三个任务作用如下:
task1:以1000ms频率闪烁LED1;
task2:以500ms频率闪烁LED2;
task3:检测按键
2. task2创建后即自我删除;
3. 检测到按键1按下,则删除task1任务。
7.1.1 代码
#include "FreeRTOS.h"
#include "task.h"
#include "led.h"
#include "freertos_test.h"
#include "key.h"
TaskHandle_t task1_handle; /* 任务句柄 */
TaskHandle_t task2_handle; /* 任务句柄 */
TaskHandle_t task3_handle; /* 任务句柄 */
void task1(void *pvParameters)
{
printf("task1创建成功,准备运行...\r\n");
while(1)
{
led1_on();
vTaskDelay(1000);
led1_off();
vTaskDelay(1000);
}
}
void task2(void *pvParameters)
{
printf("task2创建成功,准备自我删除...\r\n");
vTaskDelete(NULL);
while(1)
{
led2_on();
vTaskDelay(500);
led2_off();
vTaskDelay(500);
}
}
void task3(void *pvParameters)
{
printf("task3创建成功,检测按键...\r\n");
uint8_t key_num = 0;
while(1)
{
key_num = key_scan();
if(key_num == 1){
if(task1_handle != NULL){
printf("task1被删除\r\n");
vTaskDelete(task1_handle);//led的亮灭取决于最后的状态。
task1_handle = NULL;
}
}
vTaskDelay(10);
}
}
void freertos_test(void)
{
xTaskCreate(task1, /* 任务函数 */
"task1", /* 任务名称 */
128, /* 任务堆栈大小 */
NULL, /* 传入给任务函数的参数 */
1, /* 任务优先级 */
&task1_handle); /* 任务句柄 */
xTaskCreate(task2, /* 任务函数 */
"task2", /* 任务名称 */
128, /* 任务堆栈大小 */
NULL, /* 传入给任务函数的参数 */
1, /* 任务优先级 */
&task2_handle); /* 任务句柄 */
xTaskCreate(task3, /* 任务函数 */
"task3", /* 任务名称 */
128, /* 任务堆栈大小 */
NULL, /* 传入给任务函数的参数 */
1, /* 任务优先级 */
&task3_handle); /* 任务句柄 */
}
7.2 实验二
7.2.1 使用静态任务创建方法完成小实验1
#include "FreeRTOS.h"
#include "task.h"
#include "led.h"
#include "freertos_test.h"
#include "key.h"
//定义静态创建任务函数要预分配的TCB和堆栈空间
TaskHandle_t task1_handle; /* 任务句柄 */
StackType_t task1_Stack[configMINIMAL_STACK_SIZE];
StaticTask_t task1_TCB;
TaskHandle_t task2_handle; /* 任务句柄 */
StackType_t task2_Stack[configMINIMAL_STACK_SIZE];
StaticTask_t task2_TCB;
TaskHandle_t task3_handle; /* 任务句柄 */
StackType_t task3_Stack[configMINIMAL_STACK_SIZE];
StaticTask_t task3_TCB;
//定义空闲任务需要的空间
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
//实验空闲任务
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
configSTACK_DEPTH_TYPE * puxIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &idle_task_tcb;
*ppxIdleTaskStackBuffer = idle_task_stack;
*puxIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
//定义空闲任务需要的空间
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
//实现定时器任务
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
StackType_t ** ppxTimerTaskStackBuffer,
configSTACK_DEPTH_TYPE * puxTimerTaskStackSize )
{
* ppxTimerTaskTCBBuffer = &timer_task_tcb;
* ppxTimerTaskStackBuffer = timer_task_stack;
* puxTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
void task1(void *pvParameters)
{
printf("task1创建成功,准备运行...\r\n");
while(1)
{
led1_on();
vTaskDelay(1000);
led1_off();
vTaskDelay(1000);
}
}
void task2(void *pvParameters)
{
printf("task2创建成功,准备自我删除...\r\n");
vTaskDelete(NULL);
while(1)
{
led2_on();
vTaskDelay(500);
led2_off();
vTaskDelay(500);
}
}
void task3(void *pvParameters)
{
printf("task3创建成功,检测按键...\r\n");
uint8_t key_num = 0;
while(1)
{
key_num = key_scan();
if(key_num == 1){
if(task1_handle != NULL){
printf("task1在用静态创建任务的函数中被删除\r\n");
vTaskDelete(task1_handle);//led的亮灭取决于最后的状态。
task1_handle = NULL;
}
}
vTaskDelay(10);
}
}
void freertos_test(void)
{
printf("使用静态创建任务的方法\r\n");
task1_handle = xTaskCreateStatic(task1, /* 任务函数 */
"task1", /* 任务名称 */
128, /* 任务堆栈大小 */
NULL, /* 传入给任务函数的参数 */
1, /* 任务优先级 */
task1_Stack, /* 堆栈空间 */
&task1_TCB); /* TCB */
task2_handle = xTaskCreateStatic(task2, /* 任务函数 */
"task2", /* 任务名称 */
128, /* 任务堆栈大小 */
NULL, /* 传入给任务函数的参数 */
1, /* 任务优先级 */
task2_Stack, /* 堆栈空间 */
&task2_TCB); /* TCB */
task3_handle = xTaskCreateStatic(task3, /* 任务函数 */
"task3", /* 任务名称 */
128, /* 任务堆栈大小 */
NULL, /* 传入给任务函数的参数 */
1, /* 任务优先级 */
task3_Stack, /* 堆栈空间 */
&task3_TCB); /* TCB */
}
7.3 实验三
7.3.1 描述
7.3.2 代码
#include "FreeRTOS.h"
#include "task.h"
#include "led.h"
#include "freertos_test.h"
#include "key.h"
TaskHandle_t task1_handle; /* 任务句柄 */
TaskHandle_t task2_handle; /* 任务句柄 */
TaskHandle_t task3_handle; /* 任务句柄 */
void task1(void *pvParameters)
{
printf("task1创建成功,准备运行...\r\n");
while(1)
{
led1_on();
vTaskDelay(1000);
led1_off();
vTaskDelay(1000);
}
}
void task2(void *pvParameters)
{
printf("task2创建成功,准备自我删除...\r\n");
vTaskDelete(NULL);
while(1)
{
led2_on();
vTaskDelay(500);
led2_off();
vTaskDelay(500);
}
}
void task3(void *pvParameters)
{
printf("task3创建成功,检测按键...\r\n");
uint8_t key_num = 0;
while(1)
{
key_num = key_scan();
if(key_num == 1){
printf("按下了按键1,此时任务一被挂起\r\n");
vTaskSuspend(task1_handle);
}else if(key_num == 2){
printf("按下了按键2,此时任务一被恢复\r\n");
vTaskResume(task1_handle);
}
vTaskDelay(10);
}
}
void freertos_test(void)
{
xTaskCreate(task1, /* 任务函数 */
"task1", /* 任务名称 */
128, /* 任务堆栈大小 */
NULL, /* 传入给任务函数的参数 */
1, /* 任务优先级 */
&task1_handle); /* 任务句柄 */
xTaskCreate(task2, /* 任务函数 */
"task2", /* 任务名称 */
128, /* 任务堆栈大小 */
NULL, /* 传入给任务函数的参数 */
1, /* 任务优先级 */
&task2_handle); /* 任务句柄 */
xTaskCreate(task3, /* 任务函数 */
"task3", /* 任务名称 */
128, /* 任务堆栈大小 */
NULL, /* 传入给任务函数的参数 */
1, /* 任务优先级 */
&task3_handle); /* 任务句柄 */
}
7.4 实验四
7.4.1 描述
7.4.2 代码
#include "key.h"
#include "delay.h"
#include "FreeRTOS.h"
#include "task.h"
extern TaskHandle_t task1_handle;
//初始化GPIO
void key_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_0; // 两个KEY对应的引脚
gpio_initstruct.Mode = GPIO_MODE_INPUT; // 输入
gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
gpio_initstruct.Pin = GPIO_PIN_1;
gpio_initstruct.Mode = GPIO_MODE_IT_FALLING;
gpio_initstruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
HAL_NVIC_SetPriority(EXTI1_IRQn, 6, 0); // 设置EXTI0中断线的优先级,并且中断优先级不能高于FreeRTOS管理的范围 5-15
HAL_NVIC_EnableIRQ(EXTI1_IRQn); // 使能中断
}
//按键扫描函数
uint8_t key_scan(void)
{
//检测按键是否按下
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
{
//消抖
delay_ms(10);
//再次判断按键是否按下
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
{
//如果确实是按下的状态,等待按键松开
while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
//返回按键值
return 1;
}
}
// //检测按键是否按下
// if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
// {
// //消抖
// delay_ms(10);
// //再次判断按键是否按下
// if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
// {
// //如果确实是按下的状态,等待按键松开
// while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);
// //返回按键值
// return 2;
// }
// }
//返回默认值
return 0;
}
void EXTI1_IRQHandler(){
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
if(GPIO_Pin == GPIO_PIN_1){
BaseType_t ret = xTaskResumeFromISR(task1_handle);
if(ret == pdTRUE){
portYIELD_FROM_ISR(pdTRUE);
}
}
/*delay_ms(20);//说白了现在是在数数,不是真正的"延时了"
BaseType_t ret;
if (GPIO_Pin == GPIO_PIN_1)
{
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){
printf("在中断中恢复task1\r\n");
ret = xTaskResumeFromISR(task1_handle);
if(ret == pdTRUE){
portYIELD_FROM_ISR(ret);
}
}
}*/
}
#include "FreeRTOS.h"
#include "task.h"
#include "led.h"
#include "freertos_test.h"
#include "key.h"
TaskHandle_t task1_handle; /* 任务句柄 */
TaskHandle_t task2_handle; /* 任务句柄 */
TaskHandle_t task3_handle; /* 任务句柄 */
void task1(void *pvParameters)
{
printf("task1创建成功,准备运行...\r\n");
while(1)
{
led1_on();
vTaskDelay(1000);
led1_off();
vTaskDelay(1000);
}
}
void task2(void *pvParameters)
{
printf("task2创建成功,准备自我删除...\r\n");
vTaskDelete(NULL);
while(1)
{
led2_on();
vTaskDelay(500);
led2_off();
vTaskDelay(500);
}
}
void task3(void *pvParameters)
{
printf("task3创建成功,检测按键...\r\n");
uint8_t key_num = 0;
while(1)
{
key_num = key_scan();
if(key_num == 1){
printf("按下了按键1,此时任务一被挂起\r\n");
vTaskSuspend(task1_handle);
}
vTaskDelay(10);
}
}
void freertos_test(void)
{
xTaskCreate(task1, /* 任务函数 */
"task1", /* 任务名称 */
128, /* 任务堆栈大小 */
NULL, /* 传入给任务函数的参数 */
1, /* 任务优先级 */
&task1_handle); /* 任务句柄 */
xTaskCreate(task2, /* 任务函数 */
"task2", /* 任务名称 */
128, /* 任务堆栈大小 */
NULL, /* 传入给任务函数的参数 */
1, /* 任务优先级 */
&task2_handle); /* 任务句柄 */
xTaskCreate(task3, /* 任务函数 */
"task3", /* 任务名称 */
128, /* 任务堆栈大小 */
NULL, /* 传入给任务函数的参数 */
1, /* 任务优先级 */
&task3_handle); /* 任务句柄 */
}
更多推荐




所有评论(0)