目录
前言
声明:学习笔记是看正点原子视频教学+个人理解总结的,仅供学习交流!!
FreeRTOS 的使用过程可以简单的总结成以下几点:
1)系统初始化:如时钟、中断控制器、外设等等。
2)创建任务(本章重点):在 FreeRTOS 中,任务是最小的调度单位,使用 FreeRTOS 时需要先创建一个或多个任务,为任务分配堆栈空间和优先级。
3)启动任务调度器:任务创建后,需要调用 vTaskStartScheduler() 函数启动 FreeRTOS 的任务调度器,这样任务才能根据优先级或时间片进行调度切换。
4)多任务调度:创建有多个任务,又开启了任务调度器,FreeRTOS 任务调度器就会根据优先级和调度策略进行任务切换,以实现多任务并发执行。当一个任务被抢占时,调度器会保存当前任务的上下文(即保存当前任务的执行数据,保证下次切换回来后能继续当前的进度),并恢复下一个任务的上下文。
动态创建任务
创建函数 | BaseType_t xTaskCreate ( const configSTACK_DEPTH_TYPE usStackDepth, /* 任务堆栈大小,注意字为单位 */ void * const pvParameters, /* 传递给任务函数的参数 */ UBaseType_t uxPriority, /* 任务优先级,范围:0 ~ configMAX_PRIORITIES - 1 */ TaskHandle_t * const pxCreatedTask /* 任务句柄,就是任务的任务控制块 */ ) |
返回值 | 任务创建成功,返回 pdPASS 任务创建失败,返回 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY |
任务控制块的结构体成员介绍:
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;
任务栈栈顶,在任务切换时的任务上下文保存、任务恢复息息相关。
注意:每个任务都有属于自己的任务控制块,类似身份证。
1、动态创建任务函数的内部具体实现做了什么?
申请堆栈内存和任务控制块内存
↓
对 TCB 结构体成员进行赋值
↓
添加新任务到就绪列表中
2、动态创建任务的使用流程
1)将宏 configSUPPORT_DYNAMIC_ALLOCATION 配置为 1(表示允许动态申请内存。宏在 FreeRTOSConfig.h 文件内)
2)先再函数外定义动态创建任务函数的参数,然后再调用动态创建任务函数
3)编写任务函数 (是动态创建任务函数的第一个参数指向的函数)
此函数创建的任务会立刻进入就绪态,之后由任务调度器调度运行。
3、程序示例
需实现内容如下:
创建四个任务:start_task、task1、task2、task3。
start_task 任务负责用来创建另外的三个任务 task1~task3,
task1 实现 LED0 每 500ms 闪烁一次,
task2 实现 LED1 每 500ms 闪烁一次,
task3 实现判断按键KEY0是否按下,按下则删掉task1
1)main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/SDRAM/sdram.h"
#include "./MALLOC/malloc.h"
#include "freertos_demo.h"
int main(void)
{
/************************裸机外设初始化*************************************/
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(360, 25, 2, 8); /* 设置时钟,180Mhz */
delay_init(180); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */
sdram_init(); /* SRAM初始化 */
lcd_init(); /* 初始化LCD */
my_mem_init(SRAMIN); /* 初始化内部内存池 */
my_mem_init(SRAMEX); /* 初始化外部内存池 */
my_mem_init(SRAMCCM); /* 初始化CCM内存池 */
/************************FreeRTOS的使用***********************************/
freertos_demo();
}
2)freertos_demo.h
#ifndef __FREERTOS_DEMO_H
#define __FREERTOS_DEMO_H
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "FreeRTOS.h"
#include "task.h"
void freertos_demo(void);
#endif
3)freertos_demo.c
#include "freertos_demo.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 );
/* FreeRTOS例程的入口函数,这个只是让main.c文件的代码看起来更简洁 */
void freertos_demo(void)
{
/* 动态创建任务函数:创建 start_task 任务 */
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(); /* 进入临界区:会关闭所有中断,防止任务调度,保证下面程序完整执行 */
/* 动态创建任务函数:创建 task1 任务 */
xTaskCreate((TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
/* 动态创建任务函数:创建 task2 任务 */
xTaskCreate((TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handler );
/* 动态创建任务函数:创建 task3 任务 */
xTaskCreate((TaskFunction_t ) task3,
(char * ) "task3",
(configSTACK_DEPTH_TYPE ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(TaskHandle_t * ) &task3_handler );
vTaskDelete(NULL); /* 删除当前任务,即 start_task */
taskEXIT_CRITICAL(); /* 退出临界区:恢复中断,恢复正常的任务调度功能 */
}
/* task1,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
while(1)
{
printf("task1正在运行!!!\r\n");
LED0_TOGGLE();
vTaskDelay(500);
}
}
/* task2,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
while(1)
{
printf("task2正在运行!!!\r\n");
LED1_TOGGLE();
vTaskDelay(500);
}
}
/* task3,判断按键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);
}
}
静态创建任务
创建函数 | 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 /* 任务控制块指针,由用户分配 */ ); |
返回值 | 任务创建成功,返回 任务句柄 任务创建失败(用户没有给静态任务分配内存),返回 NULL |
1、静态创建任务函数的内部具体做了什么?
对 TCB 结构体成员进行赋值
↓
添加新任务到就绪列表中
2、静态创建任务的使用流程
1)将宏 configSUPPORT_STATIC_ALLOCATION 配置为 1。
2)调用如下函数:
vApplicationGetIdleTaskMemory( ):给空闲任务的分配一个堆栈内存,并创建相关的 TCB。
vApplicationGetTimerTaskMemory( ):给定时器任务的分配一个堆栈内存,并创建相关的 TCB。
静态创建需要用户自己分配堆栈内存给任务,而动态是由系统自己分配。
3)先在函数外定义静态创建任务函数的参数,然后再调用静态创建任务函数。
4)编写任务函数 (是静态创建任务函数的第一个参数指向的函数)
此函数创建的任务会立刻进入就绪态,之后由任务调度器调度运行。
3、程序示例
需实现内容如下:
创建四个任务:start_task、task1、task2、task3。
start_task 任务负责用来创建另外的三个任务 task1~task3,
task1 实现 LED0 每 500ms 闪烁一次,
task2 实现 LED1 每 500ms 闪烁一次,
task3 实现判断按键KEY0是否按下,按下则删掉task1
1)main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/SDRAM/sdram.h"
#include "./MALLOC/malloc.h"
#include "freertos_demo.h"
int main(void)
{
/************************裸机外设初始化*************************************/
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(360, 25, 2, 8); /* 设置时钟,180Mhz */
delay_init(180); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */
sdram_init(); /* SRAM初始化 */
lcd_init(); /* 初始化LCD */
my_mem_init(SRAMIN); /* 初始化内部内存池 */
my_mem_init(SRAMEX); /* 初始化外部内存池 */
my_mem_init(SRAMCCM); /* 初始化CCM内存池 */
/************************FreeRTOS的使用***********************************/
freertos_demo();
}
2)freertos_demo.h
#ifndef __FREERTOS_DEMO_H
#define __FREERTOS_DEMO_H
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "FreeRTOS.h"
#include "task.h"
void freertos_demo(void);
#endif
3)freertos_demo.c
#include "freertos_demo.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;
}
/* FreeRTOS例程入口函数,这个只是让代码看起来更简洁 */
void freertos_demo(void)
{
/* 静态创建任务函数:创建 start_task 任务 */
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(); /* 开启任务调度器:开启后就会根据任务的优先级进行调度 */
}
/* start_task:实现创建任务 task1~task3 */
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区:会关闭所有中断,防止任务调度,保证下面程序完整执行 */
/* 静态创建任务函数:创建 task1 任务 */
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 任务 */
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 任务 */
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); /* 删除任务 start_task */
taskEXIT_CRITICAL(); /* 退出临界区:恢复中断,恢复正常的任务调度功能 */
}
/* task1:实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
while(1)
{
printf("task1正在运行!!!\r\n");
LED0_TOGGLE();
vTaskDelay(500);
}
}
/* task2:实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
while(1)
{
printf("task2正在运行!!!\r\n");
LED1_TOGGLE();
vTaskDelay(500);
}
}
/* task3:判断按键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);
}
}
任务删除函数
函数 | void vTaskDelete (TaskHandle_t xTaskToDelete) |
形参 | xTaskToDelete:待删除任务的任务句柄。如果为 NULL,则删除当前正在运行的任务自身,即调用该函数的任务 |
功能 | 用于删除已被创建的任务。被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除。 |
空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存, 则需要由用户在任务被删除前提前释放,否则将导致内存泄露 。
1、任务删除函数的内部具体做了什么?
获取所要删除任务的控制块:通过传入的任务句柄,判断所需要删除哪个任务,NULL 代表删除任务自身。
↓
将被删除的任务从所在列表中移除:将该任务从所在的列表中移除,包括就绪列表、阻塞列表、挂起列表、事件列表等
↓
判断需要删除的任务:如果是删除任务自身,需先添加到等待删除列表,空闲任务执行内存释放。如果是删除其他任务,释放内存,任务数量减一。
↓
更新下一个任务的阻塞时间:更新下一个任务的阻塞超时时间,防止被删除的任务就是下一个阻塞超时的任务
2、删除任务的流程
1)使用删除任务函数之前,需要将宏 INCLUDE_vTaskDelete 配置为 1
2)函数的参数写入要删除任务的任务句柄 handle,如果写入 NULL 代表删除调用者本身