FreeRTOS实用指南 之 创建任务

前言

本文主要介绍FreeRTOS创建任务函数 xTaskCreate() 的函数原型及如何使用 xTaskCreate() 创建任务。


目录

前言

一、xTaskCreate()  API 函数

二、xTaskCreate() 参数和返回值

2.1 pvTaskCode

2.2 pcName

2.3 usStackDepth

2.4 pvParameters

2.5 uxPriority

2.6 pvCreatedTask

2.7 返回值

三、xTaskCreate() 使用例程

参考资料


一、xTaskCreate()  API 函数

这是我们接触到的第一个FreeRTOS函数,可能也是所有API函数中最复杂的一个,但必须了解并掌握这个最基本的组件,才能打开学习FreeRTOS的大门。

下面是 xTaskCreate() 函数原型,通过注释就能大概了解 xTaskCreate() 各个参数的意义,本文到此结束,建议详细阅读注释,对xTaskCreate() 函数有个大体印象后,再阅读后面的内容。

代码1.1 xTaskCreate() 原型

/**
 * task. h
 *
 * 创建新任务并将其添加到准备运行的任务列表中。
 *
 * 在FreeRTOS内部,任务使用两块内存,第一个块用于保存任务的数据结构, 第二个块作为任务的堆栈。
 * 如果任务是使用xTaskCreate()创建的,那么这两个内存块将在xTaskCreate()函数中自动进行动态分配。
 * (参见 https://www.FreeRTOS.org/a00111.html)
 * 如果任务是使用xTaskCreateStatic()创建的,则应用程序必须提供任务所需的内存。
 * 
 * 不使用任何动态内存的版本,请参见xTaskCreateStatic()。
 *
 * xTaskCreate()只能用于创建对整个微控制器内存映射无限制访问的任务。
 * 包含MPU的系统可以使用xTaskCreateRestricted()创建受MPU约束的任务。
 *
 * @param pvTaskCode 指向任务函数的指针。注意,任务函数不能返回(即死循环)。
 *
 * @param pcName 任务函数的描述性名称。主要是为了方便调试。configMAX_TASK_NAME_LEN定义最大长度-默认值为16。
 *
 * @param usStackDepth 任务栈的大小,是字数而不是字节数。例如,16位的栈宽,深度定义为100,则将分配200字节的栈空间。
 *
 * @param pvParameters 指向任务函数参数的指针。
 *
 * @param uxPriority 设置任务运行的优先级。包含MPU的系统可以选择通过设置priority参数的portPRIVILEGE_BIT位,
 * 以特权(系统)模式创建任务。例如,要创建优先级为2的特权任务,uxPriority参数应设置为( 2 | portPRIVILEGE_BIT )。
 *
 * @param pvCreatedTask 传出创建成功的任务的句柄。
 *
 * @return 如果任务已成功创建并添加到就绪列表则返回pdPASS,否则返回projdefs.h文件中定义的错误代码。
 */
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
    BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                            const char * const pcName, /* 仅能使用字符或字符串 */
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask );
#endif

 

二、xTaskCreate() 参数和返回值

2.1 pvTaskCode

pvTaskCode是指向具体任务函数的函数指针,其类型如下。

代码2.1 pvTaskCode类型

typedef void (* TaskFunction_t)( void * );

由此可以看出FreeRTOS任务函数的形式:

  • 返回类型为void
  • 形参类型为void *

需要进一步指出的是:

  • 任务函数的函数名是任意的
  • 任务函数的形参名必须是pvParameters
  • 任务函数不允许以任何方式返回,即在函数中不能存在returm,也不能执行到函数末尾
  • 允许在任务函数中创建其他任务
  • 每个任务函数都有属于自己的栈空间(其大小由usStackDepth参数指定)

下面的例程给出了一个典型的任务函数。同一个任务函数可以被多次创建,它们都有各自的栈空间。栈空间变量不会相互影响,但用static修饰的变量,将被各任务所共享。

小知识:for(;;) 和 while(1)相比,在有些编译器下会省略进入判断条件的步骤,因而更加高效一点。

代码2.2 任务函数的例子

/* 任务函数例程 */
void TaskFunction( void *pvParameters )
{
    /* 栈空间变量,如果多次实现该函数,则每个任务都各自有自己的value */
    int value = 0;
    /* 静态变量,如果多次实现该函数,则各任务将共享parameter */
    static int parameter = 0;

    /* 实际功能通常都实现在一个死循环中 */
    for( ;; )
    {
        /* 实际功能代码 */
        if (value == 0)
        {
            value++;
            parameter++;
            printf("TaskFunction value=%d, parameter=%d.\r\n", value, parameter);
        }
    }

    /* 安全起见,如果程序由于异常执行到这,则删除当前任务 */
    vTaskDelete( NULL );
}

2.2 pcName

pcName并不会被FreeRTOS使用,它单纯的用于辅助调试的描述任务函数的字符串。

可以通过修改 configMAX_TASK_NAME_LEN 的值来定义pcName的最大长度,其默认定义长度为16,其定义如下:

代码2.3 configMAX_TASK_NAME_LEN 定义

#define configMAX_TASK_NAME_LEN   ( 16 )

需要注意的是,这个长度已经包含了结束符'\0'。如果传入的pcName的实际长度超过了configMAX_TASK_NAME_LEN 的大小,字符串将会被自动截断。

2.3 usStackDepth

创建任务时,FreeRTOS内核会为每个任务分配固定的栈空间,usStackDepth的值就是用来告诉内核,要为这个任务分配多大的栈空间。usStackDepth指的是栈空间的字数(word),而不是字节数(byte),因此,栈大小=字宽*栈深度,在使用时,栈大小不能超过 configSTACK_DEPTH_TYPE 定义类型变量的最大值(因为会溢出)。

没有什么方法可以直接计算出一个合适的栈大小。一般情况下,都会根据经验,先简单的设置一个值,然后利用FreeRTOS提供的方法来验证分配的栈大小是否满足要求,并根据结果来调整栈空间的大小,具体的方法将在栈侦测部分进行详细说明。

2.4 pvParameters

pvParameters的值即在创建任务时传给任务函数的入参,因为是一个void*类型,故可以传入任意类型的数据。如果不需要传入参数,可以将pvParameters置为NULL。

2.5 uxPriority

uxPriority用来指定任务初始的执行优先级,优先级的取值范围为[0, configMAX_PRIORITIES - 1],其值越小,任务的执行优先级就越低。在设计上,FreeRTOS的优先级并没有规定上限,configMAX_PRIORITIES 的值由用户设置,但最好使用实际需要的最小数值。当uxPriority大于configMAX_PRIORITIES - 1时,FreeRTOS会自动将其限制为configMAX_PRIORITIES - 1。

FreeRTOS允许创建相同优先级的任务,同时也允许在程序执行过程中修改任务的优先级,具体的详情将在任务优先级部分进行说明。

代码2.4 uxPriority类型

#define portBASE_TYPE    int
typedef unsigned portBASE_TYPE    UBaseType_t;

2.6 pvCreatedTask

pvCreatedTask用于传出任务的句柄。这个句柄可以用来操作已经创建的任务,如改变任务优先级、删除任务等。如果不需要任务句柄,可以将pvCreatedTask置为NULL。

代码2.5 pvCreatedTask类型

/**
 * task. h
 *
 * 引用任务的类型。例如,调用xTaskCreate通过参数指针返回一个TaskHandle_t变量,然后可以将该变量用作vTaskDelete删除任务的参数
 */
struct tskTaskControlBlock;    /* 旧的命名用于兼容内核调试器 */
typedef struct tskTaskControlBlock * TaskHandle_t;

2.7 返回值

如果任务创建成功,则返回pdPASS;如果任务创建失败,则返回相应的错误码。大部分创建失败的原因,都是因为FreeRTOS无法为任务分配足够的空间导致的,在实际程序中,应该判断该返回值,失败时记录错误码,便于查找问题原因。

代码2.6 返回值类型

typedef long BaseType_t;

 

三、xTaskCreate() 使用例程

这个例程演示了创建任务的必要步骤以及各个参数如何使用,读者也可以自行测试。

代码3.1 创建任务例程

#define TASK_STACK_SIZE 100
#define TASK_PRIORITY 1

/* 任务函数例程 */
void TaskFunction( void *pvParameters )
{
    /* 栈空间变量,如果多次实现该函数,则每个任务都各自有自己的value */
    int value = 0;
    /* 静态变量,如果多次实现该函数,则各任务将共享parameter */
    static int parameter = 0;

    /* 实际功能通常都实现在一个死循环中 */
    for( ;; )
    {
        /* 实际功能代码 */
        if (value == 0)
        {
            value++;
            parameter++;
            printf("TaskFunction value=%d, parameter=%d.\r\n", value, parameter);
        }
    }

    /* 安全起见,如果程序由于异常执行到这,则删除当前任务 */
    vTaskDelete( NULL );
}

/* 创建两个相同优先级的任务 */
void main( void )
{
    BaseType_t result = xTaskCreate( TaskFunction, "Task1", TASK_STACK_SIZE, NULL, TASK_PRIORITY, NULL );
    if (result != pdPASS)
    {
        printf("ERROR: task1 create fail!, return %d\r\n", result);
    }

    result = xTaskCreate( TaskFunction, "Task2", TASK_STACK_SIZE, NULL, TASK_PRIORITY, NULL );
    if (result != pdPASS)
    {
        printf("ERROR: task2 create fail!, return %d\r\n", result);
    }

    return;
}

 


参考资料

  • 《FreeRTOS实时内核使用指南》
  • FreeRTOSv202012.00版源码

 

  • 10
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值