目录
2.2 静态方式创建 xTaskCreateStatic() 函数
前言:
此笔记通过2个创建任务的实例和1个删除任务的实例,来学习编写FreeRTOS的任务管理相关代码知识以及学习FreeRTOS三个相关任务管理的API函数。
所用工具:
1.FreeRTOS源码(官网下载)
2.MDK-Keil软件
3.STM32F1xx/STM32F4xx库
4.STM32F103C8T6
5.串口调试助手
函数包含:
|
FreeRTOS
创建和删除任务相关
API
函数
| 描述 |
|---|---|
|
xTaskCreate()
|
动态方式创建任务
|
|
xTaskCreateStatic()
|
静态方式创建任务
|
|
vTaskDelete()
|
删除任务
|
一、FreeRTOS 创建和删除任务相关 API 函数
1.认识ATaskFunction任务函数
在
FreeRTOS
中,任务就是一个函数,示例如下:
void ATaskFunction( void *pvParameters )
{
/* 对于不同的任务,局部变量放在任务的栈里,有各自的副本 */
int32_t lVariableExample = 0;
/* 任务函数通常实现为一个无限循环,下述函数也就是while(1){}*/
for( ;; )
{
/* 任务的代码 */
}
/* 如果程序从循环中退出,一定要使用vTaskDelete删除自己
* NULL表示删除的是自己
*/
vTaskDelete( NULL );
/* 程序不会执行到这里, 如果执行到这里就出错了 */
}
由此,该函数无返回值,
同一个函数,可以用来创建多个任务(多个任务可以运行同一个函数)
函数内部,尽量使用局部变量;每个任务都有自己的栈,每个任务运行这个函数时,任务A
的局部变量放在任务
A
的栈里、任务
B
的局部变量放在任务
B
的栈里;不同任务的局部变量,有自己的副本,函数使用全局变量、静态变量的话只有一个副本(多个任务使用的是同一个副本要防止冲突)
2.创建任务相关 API 函数的使用
2.1 动态方式创建 xTaskCreate() 函数
此函数用于使用动态的方式创建任务,任务的任务控制块以及任务的栈空间所需的内存,
在
FreeRTOSConfig.h
文件中将宏 configSUPPORT_DYNAMIC_ALLOCATION
配置为
1。
函数原型(包含6个参数)如下所示:
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 函数指针, 任务函数
const char * const pcName, // 任务的名字
const configSTACK_DEPTH_TYPE usStackDepth, //栈大小,单位为word,10表示40字节
void * const pvParameters, // 调用任务函数时传入的参数
UBaseType_t uxPriority, // 优先级
TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用它来操作这个任务
| 参数 | 描述 |
|---|---|
|
pvTaskCode
|
指向任务函数的指针(示例 Task1Function)
|
|
pcName
| 任务名(示例 "Task1") |
|
usStackDepth
|
每个任务都有自己的栈,这里指定栈大小。(示例 100)
单位是
word
,比如传入
100
,表示栈大小为
100 word
,也就是400
字节。
最大值为
uint16_t
的最大值。
确定栈的大小,并不容易,很多时候是估计;精确的办法是看反汇编码。
|
|
pvParameters
|
调用
pvTaskCode
函数指针时用到:
pvTaskCode(pvParameters)(示例 NULL)
|
|
uxPriority
|
优先级范围:
0~(confifigMAX_PRIORITIES – 1) 数值越小优先级越低,
如果传入过大的值,xTaskCreate
会把它调整为
(confifigMAX_PRIORITIES – 1)
|
|
pxCreatedTask
|
用来保存
xTaskCreate
的输出结果:
task handle
。
以后如果想操作这个任务,比如修改它的优先级,就需要这个
handle
。
如果不想使用该
handle
,可以传入
NULL。(示例 &xHandleTask1或者NULL)
|
|
返回值
|
任务创建成功 pdPASS
任务
创建失败 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(内存不足)
|
2.2 静态方式创建 xTaskCreateStatic() 函数
此函数用于使用静态的方式创建任务,事先分配好任务控制块,事先提供栈
需要在
FreeRTOSConfig.h
文件中configSUPPORT_STATIC_ALLOCATION 配置为
1。
函数原型(包含7个参数)如下所示:
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 );//任务控制块指针,内存由用户分配提供
由于当前不作为重点研究对象,具体描述不展开。
3.删除任务相关 API 函数的使用
删除任务时使用的函数如下:
void vTaskDelete( TaskHandle_t xTaskToDelete );
| 参数 | 描述 |
|---|---|
|
pvTaskCode
|
任务句柄,使用
xTaskCreate
创建任务时可以得到一个句柄。
也可传入
NULL
,这表示删除自己。
|
自删: vTaskDelete(NULL)被删: 别的任务执行 vTaskDelete(pvTaskCode) , pvTaskCode 是自己的句柄删其他:执行 vTaskDelete(pvTaskCode) , pvTaskCode 是别的任务的句柄
二、FreeRTOS创建和删除任务实例应用
下述的实例都是根据【FreeRTOS】基于STM32F103x笔记01---初识与移植的基础上编写
当前先忽略优先级的概念后续会补充上。
1.动态和静态创建任务实例
两个动态创建和一个静态创建的任务函数如下:
void Task1Function(void * param)
{
while (1)
{
printf("1");
}
}
void Task2Function(void * param)
{
while (1)
{
printf("2");
}
}
void Task3Function(void * param)
{
while (1)
{
printf("3");
}
}
/*-----------------------------------------------------------*/
StackType_t xTask3Stack[100];
StaticTask_t xTask3TCB;
StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
*ppxIdleTaskStackBuffer = xIdleTaskStack;
*pulIdleTaskStackSize = 100;
}
主函数如下所示:
int main( void )
{
TaskHandle_t xHandleTask1;
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello, world!\r\n");
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
xTaskCreateStatic(Task3Function, "Task3", 100, NULL, 1, xTask3Stack, &xTask3TCB);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
运行结果如下所示:


2.使用任务参数创建任务
任务函数如下所示:共用一个任务函数
void vTaskFunction( void *pvParameters )
{
const char *pcTaskText = pvParameters;
volatile uint32_t ul; /* volatile用来避免被优化掉 */
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务的信息 */
printf(pcTaskText);
/* 延迟一会(比较简单粗暴) */
for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
{
}
}
}
主函数:
static const char *pcTextForTask1 = "T1 run\r\n";
static const char *pcTextForTask2 = "T2 run\r\n";
int main( void )
{
prvSetupHardware();
xTaskCreate(vTaskFunction, "Task 1", 1000, (void *)pcTextForTask1, 1, NULL);
xTaskCreate(vTaskFunction, "Task 2", 1000, (void *)pcTextForTask2, 1, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
不同的任务,pvParameters不一样
同一个函数,可以用来创建多个任务(多个任务可以运行同一个函数)
3.通过vTaskDelete任务的删除
任务1:打印,创建任务2并获取创建的返回值存到ret,继续打印,并进行等待。
任务2:打印,删除自己,会位于任务1等待中运行。
void vTask1( void *pvParameters )
{
const TickType_t xDelay100ms = pdMS_TO_TICKS( 100UL );
BaseType_t ret;
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务的信息 */
printf("Task1 is running\r\n");
ret = xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, &xTask2Handle );
if (ret != pdPASS)
printf("Create Task2 Failed\r\n");
// 如果不休眠的话, Idle任务无法得到执行
// Idel任务会清理任务2使用的内存
// 如果不休眠则Idle任务无法执行, 最后内存耗尽
vTaskDelay( xDelay100ms );
}
}
void vTask2( void *pvParameters )
{
/* 打印任务的信息 */
printf("Task2 is running and about to delete itself\r\n");
// 可以直接传入参数NULL, 这里只是为了演示函数用法
vTaskDelete(xTask2Handle);
}
int main( void )
{
prvSetupHardware();
xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
结果如下:

在任务
1
的函数中,如果不调用
vTaskDelay
,则
Idle
任务用于没有机会执行,它就无法释放创建任务
2
是分配的内存。 而任务1
在不断地创建任务,不断地消耗内存,最终内存耗尽再也无法创建新的任务。
任务 1 的代码中,需要注意的是: xTaskCreate 的返回值。很多手册里说它失败时返回值是 pdFAIL ,这个宏是 0其实失败时返回值是 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ,这个宏是 -1为了避免混淆,我们使用返回值跟 pdPASS 来比较,这个宏是 1。
&spm=1001.2101.3001.5002&articleId=132490729&d=1&t=3&u=446bf0e5e7404d9685e18316c02e609a)
816

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



