| 上一篇 | 下一篇 |
|---|---|
| 字和字节 |
目 录
任务创建和删除 相关的 API 函数
任务堆栈大小是以
字(word)为单位
此类 API 函数主要有三个:
| API 函数 | 描述 |
|---|---|
xTaskCreate() | 动态方式创建任务 |
xTaskCreateStatic() | 静态方式创建任务 |
vTaskDelete() | 删除任务 |
-
动态方式创建任务:
任务的任务控制块(TCB)以及任务栈空间所需的内存,均由 FreeRTOS 自动从 FreeRTOS 管理的堆中分配。
这个自动分配不是说连内存大小都是自动设置的,内存大小仍然要手动输入,只是说这个分配的时机和方式是动态的。
可以理解为 任务栈的地址是由 FreeRTOS 自动分配的,而栈的 大小是由开发者通过
uxStackDepth参数手动指定的。 -
静态方式创建任务:
任务的任务控制块(TCB)以及任务栈空间所需的内存,需用户分配提供,在编译时就分配好,通常通过全局数组或静态变量实现。
1)动态创建任务函数,常用(⭐️)
函数 xTaskCreate() 位于 tasks.c 文件中,其输入参数有 6 个,函数具体定义如下(不同版本的代码不太一样),含注释版:
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) // 判断是否支持动态内存分配
...
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 任务函数
const char * const pcName, // 任务名称
const configSTACK_DEPTH_TYPE uxStackDepth, // 任务栈大小
void * const pvParameters, // 传递给任务的参数
UBaseType_t uxPriority, // 任务优先级
TaskHandle_t * const pxCreatedTask ) // 输出参数:任务句柄
{
TCB_t * pxNewTCB; // 任务控制块指针
BaseType_t xReturn; // 返回值,用于返回任务创建的结果
traceENTER_xTaskCreate(pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask);
// 调试追踪:记录进入 xTaskCreate() 函数时的相关信息
pxNewTCB = prvCreateTask(pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask);
// 调用 prvCreateTask() 创建任务,返回任务控制块指针 pxNewTCB
if( pxNewTCB != NULL ) // 如果任务控制块创建成功
{
#if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) )
{
/* 如果系统支持多核且启用了任务核心亲和性设置
在此设置任务的亲和性,指定任务在哪个核心上运行 */
pxNewTCB->uxCoreAffinityMask = configTASK_DEFAULT_CORE_AFFINITY;
}
#endif
prvAddNewTaskToReadyList( pxNewTCB ); // 将新任务添加到就绪任务列表,等待调度
xReturn = pdPASS; // 设置返回值为 pdPASS,表示任务创建成功
}
else // 如果任务控制块创建失败
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; // 设置返回值为内存分配失败错误码
}
traceRETURN_xTaskCreate( xReturn ); // 调试追踪:记录函数返回时的相关信息
return xReturn; // 返回任务创建结果
}
...
#endif
其中主要的工作都是用子函数 prvCreateTask() 实现的,其函数源码为:
static TCB_t * prvCreateTask( TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE uxStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
{
TCB_t * pxNewTCB; // 任务控制块(TCB)指针,用于存储新创建任务的控制信息
/* 如果栈是从低地址到高地址生长,首先分配栈,然后分配 TCB。
如果栈是从高地址到低地址生长,先分配 TCB,再分配栈空间。 */
#if ( portSTACK_GROWTH > 0 ) // 判断栈是否从低地址生长(portSTACK_GROWTH > 0 时,栈从低地址生长)
{
/* 分配 TCB 内存空间 */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL ) // 如果 TCB 内存分配成功
{
/* 初始化 TCB 内存为 0 */
( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
/* 分配栈空间:分配的栈大小为 uxStackDepth 个 StackType_t 类型的空间 */
pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );
if( pxNewTCB->pxStack == NULL ) // 如果栈空间分配失败
{
/* 如果栈内存分配失败,释放已经分配的 TCB 内存 */
vPortFree( pxNewTCB );
pxNewTCB = NULL; // 设置 TCB 为 NULL,表示创建失败
}
}
}
#else /* portSTACK_GROWTH < 0 */
{
StackType_t * pxStack;
/* 如果栈是从高地址生长,首先分配栈空间 */
pxStack = pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );
if( pxStack != NULL ) // 如果栈内存分配成功
{
/* 分配 TCB 内存空间 */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL ) // 如果 TCB 内存分配成功
{
/* 初始化 TCB 内存为 0 */
( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
/* 将栈空间地址保存在 TCB 中 */
pxNewTCB->pxStack = pxStack;
}
else // 如果 TCB 内存分配失败
{
/* 释放之前分配的栈空间 */
vPortFreeStack( pxStack );
}
}
else // 如果栈内存分配失败
{
pxNewTCB = NULL; // 设置 TCB 为 NULL,表示任务创建失败
}
}
#endif /* portSTACK_GROWTH */
if( pxNewTCB != NULL ) // 如果 TCB 和栈都分配成功
{
#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
/* 如果支持静态和动态任务分配,标记任务为动态分配 */
pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */
/* 初始化任务:设置任务的函数、名称、优先级、参数等 */
prvInitialiseNewTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
}
/* 返回新创建的 TCB。如果任务创建失败,返回 NULL */
return pxNewTCB;
}
想要完全搞懂函数内部的逻辑,还是非常难的,可以先学会使用,再深挖内部细节。
① 输入参数介绍
TaskFunction_t pxTaskCode, /* 指向任务函数的指针 */
const char * const pcName, /* 任务名字,最大长度configMAX_TASK_NAME_LEN, 默认16 */
const configSTACK_DEPTH_TYPE uxStackDepth, /* 任务堆栈大小,注意字(word)为单位 */
void * const pvParameters, /* 传递给任务函数的参数, 用不到, 一般为NULL */
UBaseType_t uxPriority, /* 任务优先级,范围:0 ~ configMAX_PRIORITIES-1 */
TaskHandle_t * const pxCreatedTask /* 任务句柄,指向任务的任务控制块 */
② 返回值
| 返回值 | 描述 |
|---|---|
pdPASS | 任务创建成功 |
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY | 任务创建失败 |
其实就是 1 和 -1 ,只不过是经过重定义的宏罢了。
pdPASS这个宏的命名以 “pd” 开头,是 FreeRTOS 中一种命名约定的一部分,用于表示 Portable (跨平台) Data Types(可移植数据类型)。
③ 调用步骤与函数体内容解析
调用步骤(此函数创建的任务会立刻进入就绪态,由任务调度器调度运行):
- 将宏 configSUPPORT_DYNAMIC_ALLOCATION 配置为 1 ;
- 定义函数入口参数;
- 编写任务函数。
函数体内部实现过程:
- 申请 堆栈内存 和 任务控制块内存;
- TCB 结构体成员赋值;
- 添加新任务到就绪列表中。
2)静态创建任务函数
静态创建任务要比动态创建复杂的多…而且实际应用中用的少,但是也要掌握
函数 xTaskCreateStatic() 位于 tasks.c 文件中,其输入参数有 7 个,函数具体定义如下:
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
...
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE uxStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer,
StaticTask_t * const pxTaskBuffer )
{
TaskHandle_t xReturn = NULL;
TCB_t * pxNewTCB;
traceENTER_xTaskCreateStatic( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority,
puxStackBuffer, pxTaskBuffer );
pxNewTCB = prvCreateStaticTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority,
puxStackBuffer, pxTaskBuffer, &xReturn );
if( pxNewTCB != NULL )
{
#if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) )
{
/* Set the task's affinity before scheduling it. */
pxNewTCB->uxCoreAffinityMask = configTASK_DEFAULT_CORE_AFFINITY;
}
#endif
prvAddNewTaskToReadyList( pxNewTCB );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
traceRETURN_xTaskCreateStatic( xReturn );
return xReturn;
}
...
#endif
① 输入参数介绍
TaskFunction_t pxTaskCode, /* 指向任务函数的指针 */
const char * const pcName, /* 任务函数名 */
const uint32_t ulStackDepth, /* 任务堆栈大小注意字(word)为单位 */
void * const pvParameters, /* 传递的任务函数参数,用不到, 一般为NULL*/
UBaseType_t uxPriority, /* 任务优先级 */
StackType_t * const puxStackBuffer, /* 任务堆栈,一般为数组,由用户分配 */
StaticTask_t * const pxTaskBuffer /* 任务控制块指针,由用户分配 */
② 返回值
| 返回值 | 描述 |
|---|---|
| NULL | 用户没有提供相应的内存,任务创建失败 |
| 有效的任务句柄 | 任务创建成功 |
③ 调用步骤与函数体内容解析
调用步骤(此函数创建的任务会立刻进入就绪态,由任务调度器调度运行):
-
需将宏
configSUPPORT_STATIC_ALLOCATION配置为1 -
定义 “空闲任务&软件定时器任务” 的任务堆栈及 TCB
后面会解释为什么要有这一步以及第三步。
-
实现两个接口函数
- (必要)
vApplicationGetIdleTaskMemory()空闲任务内存赋值(传入第 2 步定义的空闲任务结构体) - (可选)
vApplicationGetTimerTaskMemory()软件定时器内存赋值(传入第 2 步定义的定时器任务结构体)
- (必要)
-
定义函数入口参数
-
编写任务函数
函数体内部实现过程:
- TCB 结构体成员赋值
- 添加新任务到就绪列表中
3)任务删除函数(⭐️)
void vTaskDelete(TaskHandle_t xTaskToDelete)
由于函数体太复杂,这里就不贴出了,输入参数如下,没有返回值:
| 输入参数 | 描述 |
|---|---|
xTaskToDelete | 待删除任务的任务句柄(指针,指向TCB) |
【删除的结果】:
-
用于删除已被创建的任务,被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除。
-
被删除的任务,其状态会保留最后一刻的状态。
-
被删除的任务的任务句柄并不会自动变成
NULL,任务句柄本身不会被直接修改或重置,因此我们需要手动将任务句柄设置为NULL,以便标识该任务已经被删除。
【注意】:
- 当传入的参数为
NULL,则代表删除任务自身(当前正在运行的任务),立即执行,但内存 不会 立即被释放(任务自杀)。 - 在任务 1 的任务函数体中调用此函数,传入任务 2 的任务句柄,就可以删除任务 2 ,也是立即执行,内存 会 立即被释放。
- FreeRTOS中,在任务自杀情况下,空闲任务会自动负责释放自杀任务被系统分配的内存(动态创建的任务),但是由用户在任务删除前申请的内存(静态创建的任务), 则需要由用户在任务被删除前提前释放,否则将导致内存泄露。
① 调用与函数体内容解析
调用:
-
使用删除任务函数,需将宏
INCLUDE_vTaskDelete配置为1; -
入口参数输入需要删除的任务句柄(
NULL代表删除本身)。
函数体内部实现过程:
- 获取所要删除任务的控制块
- 通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身。
- 将被删除任务,移除所在列表
- 将该任务在所在列表中移除,包括:就绪、阻塞、挂起、事件等列表。
- 判断所需要删除的任务
- 删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行。
- 删除其他任务,释放内存,任务数量就减少了。
- 更新下个任务的阻塞时间
- 更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务 。
4)实验示例
示例代码你可以直接写在 main.c 文件中,也可以新建一个 freertos_demo.c 文件,专门用来存放。
4.1)动态创建任务+任务删除
① 实验目的: 学会 xTaskCreate() 和 vTaskDelete() 的使用。
② 实验设计: 将设计四个任务:start_task、task1、task2、task3。
四个任务的功能如下:
- start_task:用来创建其他的三个任务(执行完之后就要删除,避免重复创建任务);
- task1:实现 LED0 每 500ms 闪烁一次;
- task2:实现 LED1 灭 200ms ,再亮 800 ms,循环;
- task3:判断按键 KEY0 是否按下,按下则删掉 task1 (注意观察是不是立即删除的)。
【注意】这里就不要使用自己定义的延时函数了,要用自带的延时函数,自己的延时函数就是在那里死等得了。
③ main.c 代码:
需要先将 FreeRTOSConfig.h 文件中的相关宏置 1 :
#define configSUPPORT_DYNAMIC_ALLOCATION 1 /* 1: 支持动态申请内存, 默认: 1 */
main.c 代码:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "FreeRTOS.h"
#include "task.h"
// -----------------------------------------------------------------------------------
#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 128 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数
#define LED0_TASK_PRIO 2 //任务优先级
#define LED0_STK_SIZE 50 //任务堆栈大小
TaskHandle_t LED0Task_Handler; //任务句柄
void led0_task(void *p_arg); //任务函数
#define LED1_TASK_PRIO 3 //任务优先级
#define LED1_STK_SIZE 50 //任务堆栈大小
TaskHandle_t LED1Task_Handler; //任务句柄
void led1_task(void *p_arg); //任务函数
#define KEY_TASK_PRIO 4 //任务优先级
#define KEY_STK_SIZE 50 //任务堆栈大小
TaskHandle_t KEYTask_Handler; //任务句柄
void KEY_task(void *p_arg); //任务函数
// -----------------------------------------------------------------------------------
int main(void)
{
HAL_Init();
delay_init(72); //延时函数初始化
usart_init(115200); //初始化串口
LED_Init(); //初始化LED
KEY_Init(); //按键初始化
//创建开始任务
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)
{
//创建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);
//创建KEY任务
xTaskCreate((TaskFunction_t )KEY_task,
(const char* )"key_task",
(uint16_t )KEY_STK_SIZE,
(void* )NULL,
(UBaseType_t )KEY_TASK_PRIO,
(TaskHandle_t* )&KEYTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
}
//LED0任务函数, LED0每隔500ms翻转一次
void led0_task(void *pvParameters)
{
while(1)
{
printf("task1\r\n");
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
vTaskDelay(500);
}
}
//LED1任务函数, LED1以不同时间亮灭
void led1_task(void *pvParameters)
{
while(1)
{
printf("task2\r\n");
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET);
vTaskDelay(200);
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET);
vTaskDelay(800);
}
}
//KEY任务函数, 判断按键 KEY0 是否按下,按下则删掉 task1
void KEY_task(void *pvParameters)
{
while(1)
{
printf("task3\r\n");
if(KEY_Scan(0)==1)
{
if (LED0Task_Handler != NULL)
{
vTaskDelete(LED0Task_Handler);
LED0Task_Handler = NULL;
}
}
vTaskDelay(10); // 之前没加这行代码, 导致task1和task2就一开始运行了一次, 然后就一直运行task3, LED后面一直是灭的
}
}
④ 实验结果:
动态任务创建实验结果
⑤ 执行流程:
-
start_task 创建 task1 后,task1 打断 start_task 并立即执行,此时 start_task 一直是就绪态;
-
直到 task1 延时进入阻塞(延时时间长),start_task 转为运行态继续创建 task2 ;
-
start_task 创建 task2 后,task2 打断 start_task 并立即执行,此时 start_task 又处于就绪态;
-
直到 task2 延时进入阻塞(延时时间长),此时 task1 仍阻塞,所以 start_task 转为运行态继续创建 task3 ;
-
start_task 创建 task3 后,task3 打断 start_task 并立即执行,此时 start_task 又处于就绪态;
-
直到 task3 延时进入阻塞,此时 task1 和 task2 仍处于阻塞状态, start_task 又处于就绪态;
-
start_task 执行任务自杀,结束了自己的使命,然后退出临界区;
-
下面就是三个任务根据抢占式调度机制执行了。
相信大家看完执行流程也就能知道:为什么先运行的是任务优先级最低的 task1 了。
⑥ 为什么按键再次按下会卡住:
有些同学按键再次按下的时候,好像卡住了,那是因为重复删除任务会触发内存管理报错,替换如下代码就没事了:
if(KEY_Scan(0)==1)
{
if (LED0Task_Handler != NULL)
{
vTaskDelete(LED0Task_Handler);
LED0Task_Handler = NULL;
}
}
⑦ 执行流程的简化:
如果想要等三个任务全部创建好之后,再执行任务调度,那么就在 start_task 的任务函数体的头和尾加上临界区的相关代码:
//开始任务任务函数
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);
//创建KEY任务
xTaskCreate((TaskFunction_t )KEY_task,
(const char* )"key_task",
(uint16_t )KEY_STK_SIZE,
(void* )NULL,
(UBaseType_t )KEY_TASK_PRIO,
(TaskHandle_t* )&KEYTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区 ----
}
这样的话,那就是等三个任务创建好之后,start_task 自杀,然后退出临界区,三个任务执行抢占式调度,task3 先运行。
4.2)静态创建任务+任务删除
① 实验目的: 学会 xTaskCreateStatic() 和 vTaskDelete() 的使用。
② 实验设计: 将设计四个任务:start_task、task1、task2、task3 。
四个任务的功能如下:
- start_task:用来创建其他的三个任务(执行完之后就要删除,避免重复创建任务);
- task1:实现 LED0 每 500ms 闪烁一次;
- task2:实现 LED1 灭 200ms ,再亮 800 ms,循环;
- task3:判断按键 KEY0 是否按下,按下则删掉 task1 (注意观察是不是立即删除的)。
【注意】这里就不要使用自己定义的延时函数了,要用自带的延时函数,自己的延时函数就是在那里死等得了。
③ 相关代码:
1、需要先将 FreeRTOSConfig.h 文件中的相关宏置 1 :
#define configSUPPORT_STATIC_ALLOCATION 0 /* 1: 支持静态申请内存, 默认: 0 */
2、编写空闲任务内存赋值函数 vApplicationGetIdleTaskMemory() ,这个函数先照着写,后面会详细解释用法:
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE]; // 空闲任务内存分配
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize)
{
* ppxIdleTaskTCBBuffer = &idle_task_tcb;
* ppxIdleTaskStackBuffer = idle_task_stack;
* pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; // configMINIMAL_STACK_SIZE 被系统定义了
}
有些同学可能会遇到这个错误:
..\User\main.c(36): error: #147-D: declaration is incompatible with "void vApplicationGetIdleTaskMemory(StaticTask_t **, StackType_t **, uint16_t *)" (declared at line 2001 of "..\Middlewares\FreeRTOS\include\task.h")
这个错误的意思是:由于版本差异,你定义的 vApplicationGetIdleTaskMemory() 的参数类型与 FreeRTOS 官方声明不一致(尤其是最后一个 uint32_t* 应为 uint16_t*)。
3、编写软件定时器任务内存赋值函数 vApplicationGetTimerTaskMemory() ,这个函数先照着写,后面会详细解释用法:
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
void vApplicationGetTimerTaskMemory(StaticTask_t ** ppxTimerTaskTCBBuffer,
StackType_t ** ppxTimerTaskStackBuffer,
uint32_t * pulTimerTaskStackSize)
{
* ppxTimerTaskTCBBuffer = &timer_task_tcb;
* ppxTimerTaskStackBuffer = timer_task_stack;
* pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
4、main.c 代码:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "FreeRTOS.h"
#include "task.h"
// -----------------------------------------------------------------------------------
#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 128 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
StackType_t start_task_stack[START_STK_SIZE]; // 任务堆栈
StaticTask_t start_task_tcb; //任务控制块
void start_task(void *pvParameters); //任务函数
#define LED0_TASK_PRIO 2 //任务优先级
#define LED0_STK_SIZE 50 //任务堆栈大小
TaskHandle_t LED0Task_Handler; //任务句柄
StackType_t LED0_task_stack[LED0_STK_SIZE]; // 任务堆栈
StaticTask_t led0_task_tcb; //任务控制块
void led0_task(void *p_arg); //任务函数
#define LED1_TASK_PRIO 3 //任务优先级
#define LED1_STK_SIZE 50 //任务堆栈大小
TaskHandle_t LED1Task_Handler; //任务句柄
StackType_t LED1_task_stack[LED1_STK_SIZE]; // 任务堆栈
StaticTask_t led1_task_tcb; //任务控制块
void led1_task(void *p_arg); //任务函数
#define KEY_TASK_PRIO 4 //任务优先级
#define KEY_STK_SIZE 50 //任务堆栈大小
TaskHandle_t KEYTask_Handler; //任务句柄
StackType_t KEY_task_stack[KEY_STK_SIZE]; // 任务堆栈
StaticTask_t key_task_tcb; //任务控制块
void KEY_task(void *p_arg); //任务函数
// -----------------------------------------------------------------------------------
// 定义静态方式下, 空闲任务内存分配函数
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE]; // 空闲任务内存分配
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint16_t * pulIdleTaskStackSize)
{
* ppxIdleTaskTCBBuffer = &idle_task_tcb;
* ppxIdleTaskStackBuffer = idle_task_stack;
* pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; // 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,
uint16_t * pulTimerTaskStackSize)
{
* ppxTimerTaskTCBBuffer = &timer_task_tcb;
* ppxTimerTaskStackBuffer = timer_task_stack;
* pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
// -----------------------------------------------------------------------------------
int main(void)
{
HAL_Init();
delay_init(72); //延时函数初始化
usart_init(115200); //初始化串口
LED_Init(); //初始化LED
KEY_Init(); //按键初始化
//创建开始任务
StartTask_Handler = xTaskCreateStatic((TaskFunction_t ) start_task,
(const char * ) "start_task",
(configSTACK_DEPTH_TYPE) START_STK_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(); //进入临界区
//创建LED0任务
LED0Task_Handler = xTaskCreateStatic((TaskFunction_t ) led0_task,
(const char * ) "led0_task",
(configSTACK_DEPTH_TYPE) LED0_STK_SIZE,
(void * ) NULL,
(UBaseType_t ) LED0_TASK_PRIO,
(StackType_t * ) LED0_task_stack,
(StaticTask_t * ) &led0_task_tcb );
//创建LED1任务
LED1Task_Handler = xTaskCreateStatic((TaskFunction_t ) led1_task,
(const char * ) "led1_task",
(configSTACK_DEPTH_TYPE) LED1_STK_SIZE,
(void * ) NULL,
(UBaseType_t ) LED1_TASK_PRIO,
(StackType_t * ) LED1_task_stack,
(StaticTask_t * ) &led1_task_tcb );
//创建KEY任务
KEYTask_Handler = xTaskCreateStatic((TaskFunction_t ) KEY_task,
(const char * ) "key_task",
(configSTACK_DEPTH_TYPE) KEY_STK_SIZE,
(void * ) NULL,
(UBaseType_t ) KEY_TASK_PRIO,
(StackType_t * ) KEY_task_stack,
(StaticTask_t * ) &key_task_tcb );
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED0任务函数, LED0每隔500ms翻转一次
void led0_task(void *pvParameters)
{
while(1)
{
printf("task1\r\n");
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
vTaskDelay(500);
}
}
//LED1任务函数, LED1以不同时间亮灭
void led1_task(void *pvParameters)
{
while(1)
{
printf("task2\r\n");
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET);
vTaskDelay(200);
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET);
vTaskDelay(800);
}
}
//KEY任务函数, 判断按键 KEY0 是否按下,按下则删掉 task1
void KEY_task(void *pvParameters)
{
while(1)
{
printf("task3\r\n");
if(KEY_Scan(0)==1)
{
if (LED0Task_Handler != NULL)
{
vTaskDelete(LED0Task_Handler);
LED0Task_Handler = NULL;
}
}
vTaskDelay(10); // 之前没加这行代码, 导致task1和task2就一开始运行了一次, 然后就一直运行task3, LED后面一直是灭的
}
}
效果和动态的一样。

被折叠的 条评论
为什么被折叠?



