您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   模型库  
会员   
   
人工智能、机器学习 TensorFlow
6月30日-7月1日 直播
基于 UML 和EA进行分析设计
7月30-31日 北京+线上
图数据库与知识图谱
8月21日-22日 北京+线上
   
 
 订阅
FreeRTOS任务之使用篇

 
 
  13  次浏览      1 次
 2025-7-1
 
编辑推荐:
本文主要介绍了 FreeRTOS任务之使用篇相关内容。 希望对您的学习有所帮助。
本文来自于CSDN,由火龙果软件Linda编辑、推荐。

1.区分

在FreeRTOS中,**任务(task)和线程(thread)的概念是相似的,但它们之间有一些微小的区别。**一般情况下将任务理解成线程也是可以的

1.1 相似之处:

任务和线程的功能:无论是任务还是线程,它们都是操作系统调度执行的基本单位。它们都代表了一个独立的执行流,执行一定的代码,拥有自己的执行上下文(例如程序计数器、堆栈、寄存器等)。

多任务/多线程的目的:任务和线程的主要目的是实现并发执行,使得多个代码块或功能模块能够“并行”处理,提高系统的响应性和效率。

上下文切换:任务和线程都需要支持上下文切换,也就是说操作系统需要在不同的任务或线程之间进行切换,保存当前任务的状态并加载另一个任务或线程的状态。

1.2 区别

FreeRTOS中的任务(Task)

在FreeRTOS中,任务是FreeRTOS调度的基本单位。FreeRTOS中的任务通常与线程在其他操作系统中的概念相似,但由于FreeRTOS是一个实时操作系统(RTOS),它的任务管理与一般操作系统中的线程管理有一些不同之处。

任务调度:FreeRTOS中的任务由FreeRTOS内核调度,而不像一般操作系统中的线程那样由操作系统调度器调度。

任务创建:FreeRTOS通过 xTaskCreate() 来创建任务,每个任务都有自己的栈,执行的是一个具体的任务函数。任务的调度基于优先级,可以是抢占式的,也可以是协作式的。

优先级:FreeRTOS任务具有优先级,任务可以设置不同的优先级。高优先级的任务会抢占低优先级的任务。

资源占用:FreeRTOS任务通常占用较少的资源,相比线程,它的实现和切换开销较小,因此适合在资源受限的嵌入式系统中使用。

传统操作系统中的线程(Thread):

在线程的传统操作系统(如Windows、Linux)中,线程是一个操作系统级别的概念。线程与进程相比,进程是独立的资源分配单位,而线程是在进程内执行的最小单位。

线程调度:传统操作系统的线程由操作系统的内核调度,线程可以有自己的内存空间(例如,堆栈和局部变量),并且与其他线程共享父进程的资源。

线程的资源和开销:线程通常占用较多的资源,因为它们在操作系统的调度中有更多的管理和控制,例如多核处理器的使用、内存分配等。线程切换的开销通常比FreeRTOS任务要大,因为操作系统需要管理更多的系统资源。

线程的多核支持:传统的操作系统支持在多核系统中调度线程,它们可能会在不同的CPU核心上并行执行。

**FreeRTOS中的任务(Task)**可以理解为一种特殊的线程,优化了资源管理并更适合嵌入式和实时操作系统。任务的创建和调度相对轻量级,任务优先级对调度有重要影响,且任务间的切换开销较小。

**传统操作系统中的线程(Thread)**通常是更重的概念,包含更多的资源管理和调度机制,适用于多核操作系统和大规模并发应用。

尽管它们在许多方面有相似性,任务和线程的区别主要在于它们的实现细节、资源占用、调度机制等,FreeRTOS中的任务更倾向于嵌入式和实时性要求高的场景,而线程则更常见于桌面操作系统和大型应用程序中。

2.任务的创建和删除

2.1 任务创建

每个任务的创建都需要分配一个栈的空间的给它,并且每个任务的栈空间是独一无二的,不能重复。freeRtos中有两种方式创建任务:

动态创建,指任务的栈的空间由系统随机分配

静态创建,指任务的栈的空间可以由自己指定,比如创建一个全局静态数组作为栈分配给该任务,只不过数组的容量需要和指定的栈的深度对应

2.1.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            // 任务句柄
);

xTaskCreate 函数的作用是创建一个任务,任务的定义包括任务代码、任务名称、栈大小、传递给任务的参数、优先级和任务句柄。这个函数是 FreeRTOS 创建和管理任务的核心接口。任务的参数根据任务的需求和系统的资源来配置,正确设置这些参数非常重要,以确保任务的正确运行和内存的合理利用。参数介绍如下:

pxTaskCode (任务函数指针)

TaskFunction_t(通常定义为 void (*)(void *pvParameters))。

这是一个指向任务函数的指针,任务函数是执行任务的主体代码。你需要为每个任务创建一个函数,这个函数会一直运行,除非任务被删除或挂起。

任务函数通常是一个无限循环,在函数中可以执行你需要完成的任务。如果任务有结束时需要做的事情,任务函数必须调用 vTaskDelete(NULL) 来删除自己。

void myTask(void *pvParameters) {
    while(1) {
        // 执行任务代码
    }
}

pcName (任务名称)

const char * const,是任务的名称(字符串类型)。

这是任务的名字,它对调试非常有用。FreeRTOS 内部不会使用这个名字,只是为了开发人员调试和识别任务。

名字的长度由 configMAX_TASK_NAME_LEN 决定,通常是在 FreeRTOSConfig.h 中配置的。如果没有特别要求,通常保持默认值即可。

xTaskCreate(myTask, "MyTask", 128, NULL, 1, NULL);

在这个例子中,任务的名字是 "MyTask"。

usStackDepth (栈深度)

configSTACK_DEPTH_TYPE,通常是 uint16_t 类型。

为每个任务分配的栈大小。任务栈用于存储任务的局部变量、函数调用等。这里的 usStackDepth 表示栈的深度(单位是字)。例如,如果传入 100,表示栈大小为 100 字(通常是 400 字节,如果 word 是 4 字节。) ---- 查看了该函数内部的调用,发现貌似栈的基本单位是uint_32,也就是word默认的就是4字节:

pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );其中StackType_t:typedef portSTACK_TYPE StackType_t;,而portSTACK_TYPE :#define portSTACK_TYPE uint32_t,为4字节

注意:栈的大小要根据任务的需求来设置,太小可能导致栈溢出,太大则浪费内存。栈的大小有时需要通过经验或分析工具来确定。FreeRTOS 本身没有提供非常精确的栈计算方法,但可以通过调试工具分析栈的使用情况。

xTaskCreate(myTask, "MyTask", 128, NULL, 1, NULL);  // 128 字节的栈

pvParameters (任务参数)

void *(指向任何类型的指针)。

这个参数会被传递给任务的任务函数 pxTaskCode。如果任务不需要任何参数,可以传递 NULL。它使得任务可以接收传递给它的额外数据(如结构体或指针等)。

void myTask(void *pvParameters) {
    // 任务执行时,可以通过 pvParameters 获取到传递的参数
}

xTaskCreate(myTask, "MyTask", 128, (void *)parameter, 1, NULL);

 

在这个例子中,pvParameters 是一个传递给任务的指针,可以让任务访问外部传递的数据。

uxPriority (任务优先级)

UBaseType_t,通常是 uint32_t 或 unsigned int 类型。

定义任务的优先级。FreeRTOS 中的任务是基于优先级调度的,优先级值越小,优先级越低。有效优先级范围是 0 到 configMAX_PRIORITIES - 1,其中 configMAX_PRIORITIES 是在 FreeRTOSConfig.h 中定义的最大优先级数。

xTaskCreate(myTask, "MyTask", 128, NULL, 2, NULL);  // 任务优先级为 2

如果传入的优先级超过了 configMAX_PRIORITIES - 1,FreeRTOS 会自动将其调整为 configMAX_PRIORITIES - 1。

pxCreatedTask (任务句柄)

TaskHandle_t *,这是一个指向任务句柄的指针。 在FreeRTOS中,每个任务在创建时都会分配一个任务句柄。这个句柄是一个指向任务控制块(TCB)的指针。

如果想在任务创建后保存任务的句柄(用于之后操作任务,比如修改任务优先级、删除任务等),可以传入这个参数。任务句柄是一个标识符,实现在任务间进行交互或控制任务。

如果不需要这个句柄,可以将其设为 NULL,这样就不保存任务句柄。

TaskHandle_t xTaskHandle;
xTaskCreate(myTask, "MyTask", 128, NULL, 1, &xTaskHandle);  // 保存任务句柄

之后,可以使用 xTaskHandle 来控制任务,例如改变任务优先级:

vTaskPrioritySet(xTaskHandle, 2);  // 修改任务优先级

 

2.1.2 静态创建

TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
                                   const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                                   const uint32_t ulStackDepth,
                                   void * const pvParameters,
                                   UBaseType_t uxPriority,
                                   StackType_t * const puxStackBuffer,
                                   StaticTask_t * const pxTaskBuffer )

 

参6的StackType_t * const puxStackBuffer,可以指定一个静态数组传入作为所创建任务的栈,需要注意数组类型实际上是uint_32

要想使用该文件还要去配置FreeRtos中的文件:

实现函数

2.1.3 最后一个参数

xTaskCreate`的最后一个参数:`TaskHandle_t * const pxCreatedTask
xTaskCreateStatic`的最后一个参数:` StaticTask_t * const pxTaskBuffer

其实这两个参数最后都是指向一个TCB_t结构体:

typedef tskTCB TCB_t;

// 其中tskTCB定义如下:
typedef struct tskTaskControlBlock       /* The old naming convention is used
to prevent breaking kernel aware debuggers. */
{ volatile StackType_t * pxTopOfStack; /*< Points to the location of the
last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */
#if ( portUSING_MPU_WRAPPERS == 1 ) xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part
of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
#endif ListItem_t xStateListItem; /*< The list that the state list
item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
ListItem_t xEventListItem; /*< Used to reference a task from an event list. */ UBaseType_t uxPriority; /*< The priority of the task. 0 is the lowest priority. */ StackType_t * pxStack; /*< Points to the start of the stack. */ char pcTaskName[ configMAX_TASK_NAME_LEN ]; /*< Descriptive name given to
the task when created. Facilitates debugging only. */
/*lint !e971
Unqualified char types are allowed for strings and single characters only. */
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) StackType_t * pxEndOfStack; /*< Points to the highest valid address for the stack. */ #endif #if ( portCRITICAL_NESTING_IN_TCB == 1 ) UBaseType_t uxCriticalNesting; /*< Holds the critical section nesting depth
for ports that do not maintain their own count in the port layer. */
#endif #if ( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxTCBNumber; /*< Stores a number that increments each time a TCB is
created. It allows debuggers to determine when a task has been deleted and then recreated. */
UBaseType_t uxTaskNumber; /*< Stores a number specifically for use by third party trace code. */ #endif #if ( configUSE_MUTEXES == 1 ) UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */ UBaseType_t uxMutexesHeld; #endif #if ( configUSE_APPLICATION_TASK_TAG == 1 ) TaskHookFunction_t pxTaskTag; #endif #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; #endif #if ( configGENERATE_RUN_TIME_STATS == 1 ) uint32_t ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */ #endif #if ( configUSE_NEWLIB_REENTRANT == 1 ) /* Allocate a Newlib reent structure that is specific to this task. * Note Newlib support has been included by popular demand, but is not * used by the FreeRTOS maintainers themselves. FreeRTOS is not * responsible for resulting newlib operation. User must be familiar with * newlib and must provide system-wide implementations of the necessary * stubs. Be warned that (at the time of writing) the current newlib design * implements a system-wide malloc() that must be provided with locks. * * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html * for additional information. */ struct _reent xNewLib_reent; #endif #if ( configUSE_TASK_NOTIFICATIONS == 1 ) volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; #endif /* See the comments in FreeRTOS.h with the definition of * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */ #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro
has been consolidated for readability reasons. */
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE
if the task is a statically allocated to ensure no attempt is made to free the memory. */
#endif #if ( INCLUDE_xTaskAbortDelay == 1 ) uint8_t ucDelayAborted; #endif #if ( configUSE_POSIX_ERRNO == 1 ) int iTaskErrno; #endif } tskTCB;

 

freeRtos给出的解释:Task control block. A task control block (TCB) is allocated for each task, and stores task
state information, including a pointer to the task’s
context(the task’s run time environment, including
register values)

任务控制块。为每个任务分配一个任务控制块(TCB),它存储任务的状态信息,包括指向任务上下文的指针(任务的运行时环境,包括寄存器值)。

具体的是如何指向该结构体,感兴趣的可以去看看函数的内部实现

2.1.4 示例程序

#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>

// 定义任务栈大小
#define TASK_STACK_SIZE 128

// 定义任务优先级
#define TASK_PRIORITY 1

// 定义任务栈和任务控制块 (用于静态任务创建)
static StackType_t xTaskStack[TASK_STACK_SIZE];
static StaticTask_t xTaskBuffer;

// 动态任务函数
void vTaskDynamic(void *pvParameters) {
    while(1) {
        printf("This is the dynamic task!\n");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

// 静态任务函数
void vTaskStatic(void *pvParameters) {
    while(1) {
        printf("This is the static task!\n");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

int main(void) {
    // 创建动态任务 (使用xTaskCreate)
    xTaskCreate(vTaskDynamic, "DynamicTask", TASK_STACK_SIZE, NULL, TASK_PRIORITY, NULL);

    // 创建静态任务 (使用xTaskCreateStatic)
    xTaskCreateStatic(
        vTaskStatic,                  // 任务函数
        "StaticTask",                 // 任务名称
        TASK_STACK_SIZE,              // 栈大小
        NULL,                         // 任务参数
        TASK_PRIORITY,                // 优先级
        xTaskStack,                   // 静态栈内存
        &xTaskBuffer                  // 静态任务控制块
    );

    // 启动调度器
    vTaskStartScheduler();

    while(1); // 程序不应该到达这里
    return 0;
}

下面是各自的示例程序,静态和动态

freertos_example_createtaskstatic.zip

freertos_example_createtask.zip

以上都是基于同个优先级的情况下的,同优先级的任务表面上是并行,实际上却是交叉运行的

2.2 任务删除

void vTaskDelete( TaskHandle_t xTaskToDelete );

//xTaskToDelete:要删除的任务的句柄。如果传入 NULL,则删除当前任务。
//删除指定的任务。如果传入的是任务句柄,它将删除对应的任务。任务删除后,任务的内存会被释放,包括栈空间和任务控制块(TCB)。
//如果传入的是 NULL,则会删除当前正在执行的任务,也就是说,当前任务会删除自己。

2.2.1 三种删除

#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>

// 任务句柄
TaskHandle_t xTaskHandle1 = NULL;
TaskHandle_t xTaskHandle2 = NULL;

// 任务函数1
void vTaskFunction1(void *pvParameters) {
    while(1) {
        printf("Task 1 is running.\n");

        // 任务1自杀:删除自己
        if (some_condition) {
            printf("Task 1 is deleting itself.\n");
            vTaskDelete(NULL);  // 自杀,删除自己
        }

        vTaskDelay(pdMS_TO_TICKS(1000));  // 延时1秒
    }
}

// 任务函数2
void vTaskFunction2(void *pvParameters) {
    while(1) {
        printf("Task 2 is running.\n");

        // 被杀:由其他任务删除
        if (some_condition) {
            printf("Task 2 is being deleted by Task 1.\n");
            vTaskDelete(xTaskHandle2);  // 删除任务2
        }

        vTaskDelay(pdMS_TO_TICKS(1000));  // 延时1秒
    }
}

// 任务函数3(杀人)
void vTaskFunction3(void *pvParameters) {
    while(1) {
        printf("Task 3 is running.\n");

        // 杀人:删除任务1
        if (some_condition) {
            printf("Task 3 is deleting Task 1.\n");
            vTaskDelete(xTaskHandle1);  // 删除任务1
        }

        vTaskDelay(pdMS_TO_TICKS(1000));  // 延时1秒
    }
}

int main(void) {
    // 创建任务1
    xTaskCreate(vTaskFunction1, "Task1", 128, NULL, 1, &xTaskHandle1);
    
    // 创建任务2
    xTaskCreate(vTaskFunction2, "Task2", 128, NULL, 1, &xTaskHandle2);
    
    // 创建任务3
    xTaskCreate(vTaskFunction3, "Task3", 128, NULL, 1, NULL);

    // 启动调度器
    vTaskStartScheduler();

    while(1);  // 不应该到达这里
    return 0;
}

自杀(**vTaskDelete(NULL)**):任务1在满足某个条件时调用 vTaskDelete(NULL),删除自己。这个任务会从调度中移除,并且其栈和控制块被释放。

被杀(**vTaskDelete(xTaskHandle2)**):任务2可以被任务1删除。在任务1中,调用 vTaskDelete(xTaskHandle2) 删除任务2。这是通过任务句柄完成的,任务2会被从调度中移除。

杀人(**vTaskDelete(xTaskHandle1)**):任务3可以删除任务1。任务3通过调用 vTaskDelete(xTaskHandle1) 删除任务1。这是通过任务句柄完成的,任务1会被从调度中移除。

以上内容都是基于创建任务的时候,任务的优先级都是相同的情况 — 参数:**uxPriority**

2.2.2 涉及优先级

FreeRTOS在xTaskCreate中分配TCB和栈,但并不一定就是在vTaskDelete中释放TCB和栈。可以尝试去不断轮流调用xTaskCreate和vTaskDelete,最终的结果是内存被消耗殆尽(自杀的情况下)

调用 vTaskDelete() 删除任务时,FreeRTOS 不会立即释放任务的 TCB 和栈内存。这是因为:

任务可能正在执行代码(例如处于临界区、持有资源),直接释放内存可能导致不可预测的行为(如内存访问冲突)。

FreeRTOS 的 vTaskDelete 会将待释放的任务的 TCB 和栈内存标记为“待回收”,并插入到一个待删除任务列表中。

实际的释放操作由 Idle 任务(空闲任务)完成。Idle 任务在系统空闲时(即没有其他任务运行时)会检查这个列表,并安全地释放内存。

Idle 任务的优先级是 0(最低优先级)。如果其他任务始终处于就绪状态(例如高优先级任务通过 vTaskDelay() 主动释放 CPU),Idle 任务可能无法及时运行。需要设计任务调度策略,确保 Idle 任务有机会执行。例如,在任务中适当使用 vTaskDelay() 或阻塞式 API。

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;
}

 

结果是可以看到先打印:Task1 is running,然后打印:Task2 is running and about to delete itself,后面依次轮流打印

main函数中创建任务1,优先级为1。任务1运行时,它创建任务2,任务2的优先级是2。任务2的优先级最高,它马上执行。

任务2打印一句话后,就删除了自己。(可以尝试去将任务2中的代码加一个循环打印,就可以发现任务1的代码接下来没机会执行到)

任务2被删除后,任务1的优先级最高,轮到任务1继续运行,它调用 vTaskDelay() 进入Block状 态

任务1 Block期间,轮到Idle任务执行:它释放任务2的内存(TCB、栈)时间到后,任务1变为最高优先级的任务继续执行。 如此循环。

如果将任务1中的vTaskDelay注释到,起初的程序现象没啥问题,但是后面就会出现:Create Task2 Failed,这就是前面提到的内存被消耗殆尽,任务1一直在执行导致最低优先级的Idle任务没执行到

3.任务优先级

这一部分上面《任务的删除》部分已经讲的差不多了,高优先级的任务先运行,这里做补充:

FreeRTOS的调度器可以使用2种方法来快速找出优先级最高的、可以运行的任务。使用不同的方法时,configMAX_PRIORITIES 的取值有所不同。

(1) 通用方法(C 语言实现)

配置:configUSE_PORT_OPTIMISED_TASK_SELECTION = 0 或未定义。

兼容所有架构,代码可移植。

对 configMAX_PRIORITIES 无限制(但越大越消耗内存和时间)。

通过遍历优先级位图(uxTopReadyPriority)找到最高优先级任务。

适用于优先级数量较多(>32)或硬件不支持优化指令的架构。

(2) 架构优化方法(汇编加速)

配置:configUSE_PORT_OPTIMISED_TASK_SELECTION = 1。

依赖硬件指令(如 ARM 的 CLZ 指令)快速定位最高优先级位。

限制:configMAX_PRIORITIES ≤ 32。

查找效率高,时间复杂度为 O(1)。

适用于优先级数量 ≤32 且硬件支持优化指令(如 Cortex-M 系列)。

猜测的内部大概实现如下:

1. 就绪任务列表(Ready List)

按优先级分组:每个优先级对应一个链表,任务根据优先级插入对应链表。

优先级位图(uxTopReadyPriority):

一个 32 位变量,每一位表示对应优先级是否有就绪任务。

例如,优先级 5 的任务就绪时,第 5 位被置 1。

2. 查找最高优先级任务的流程

从最高优先级向最低优先级遍历 uxTopReadyPriority。

找到第一个非零位,确定最高优先级。

从该优先级的链表中取出第一个任务执行。

// 伪代码示例
for (priority = configMAX_PRIORITIES - 1; priority >= 0; priority--) {
    if (uxTopReadyPriority & (1UL << priority)) {
        return priority;
    }
}

任务的优先级这部分涉及到调度算法,调度行为规则如下

抢占式调度:

高优先级任务就绪时,立即抢占低优先级任务(也就是低优先级任务要把CPU让给已经就绪的高优先级任务,让其去执行)。

通过中断或任务主动释放 CPU(如 vTaskDelay())触发调度。

时间片轮转(同优先级任务):

相同优先级的任务共享 CPU 时间,通过时间片轮流执行(如“喂饭”和“回复信息”交替执行)。

时间片长度由 configTICK_RATE_HZ 定义的系统节拍周期决定。

创建任务后给定的任务优先级,在后续也是可以在任务当中去进行更改的

3.1 实验1

以上的内容都是freertos内部对任务优先级的一些设计内容,接下来猜一下这个代码的结果,能猜出就对任务优先级有大概的了解了。

void vTask1( void *pvParameters )
{
    /* 任务函数的主体一般都是无限循环 */
    for( ;; ){
        /* 打印任务的信息 */
        printf("T1\r\n");
    }
}
void vTask2( void *pvParameters )
{
    /* 任务函数的主体一般都是无限循环 */
    for( ;; ){
        /* 打印任务的信息 */
        printf("T2\r\n");
    }
}
void vTask3( void *pvParameters )
{
    const TickType_t xDelay3000ms = pdMS_TO_TICKS( 3000UL );
    /* 任务函数的主体一般都是无限循环 */
    for( ;; ){
        /* 打印任务的信息 */
        printf("T3\r\n");
        // 如果不休眠的话, 其他任务无法得到执行
        vTaskDelay( xDelay3000ms );
    }
}
int main(void)
{
    prvSetupHardware();
    xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
    xTaskCreate(vTask2, "Task 2", 1000, NULL, 1, NULL);
    xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);
    /* 启动调度器 */
    vTaskStartScheduler();
    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
    return 0;
}

3.2 实验2:修改任务优先级

使用uxTaskPriorityGet来获得任务的优先级:

UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );

参数xTask用来获取指定任务的优先级,若是为NULL则获取当前任务的优先级

使用vTaskPrioritySet 来设置任务的优先级:

void vTaskPrioritySet( TaskHandle_t xTask,
                        UBaseType_t uxNewPriority );

 

使用参数xTask来指定任务,设置为NULL表示设置自己的优先级;参数uxNewPriority表示新的优先级,取值范围是0~(configMAX_PRIORITIES – 1)。

下面是示例代码:

void vTask1( void *pvParameters )
{
    UBaseType_t uxPriority;
    /* Task1,Task2都不会进入阻塞或者暂停状态*/
    /* 根据优先级决定谁能运行*/
    /* 得到Task1自己的优先级 */
    uxPriority = uxTaskPriorityGet( NULL );
    for( ;; )
    {
        printf( "Task 1 is running\r\n" );
        printf("About to raise the Task 2 priority\r\n" );
        /* 提升Task2的优先级高于Task1 */
        /* Task2会即刻执行 */
        vTaskPrioritySet( xTask2Handle, ( uxPriority + 1 ) );
        /* 如果Task1能运行到这里,表示它的优先级比Task2高
        /* 那就表示Task2肯定把自己的优先级降低了*/
    }
}
void vTask2( void *pvParameters )
{
    UBaseType_t uxPriority;
    /* Task1,Task2都不会进入阻塞或者暂停状态*/
    /* 根据优先级决定谁能运行*/
    /* 得到Task2自己的优先级 */
    uxPriority = uxTaskPriorityGet( NULL );
    for( ;; )
    {
        /* 能运行到这里表示Task2的优先级高于Task1 */
        /* Task1提高了Task2的优先级 */
        printf( "Task 2 is running\r\n" );
        printf( "About to lower the Task 2 priority\r\n" );
        /* 降低Task2自己的优先级,让它小于Task1*/
        /* Task1得以运行*/
        vTaskPrioritySet( NULL, ( uxPriority - 2 ) );
    }
}

int main( void )
{
    prvSetupHardware();
    /* Task1的优先级更高, Task1先执行 */
    xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL );
    xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, &xTask2Handle );
    /* 启动调度器 */
    vTaskStartScheduler();
    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
    return 0;
}

其中提到的阻塞或者暂停状态,后续任务的状态中会将讲。

   
13 次浏览       1
 
相关文章

CMM之后对CMMI的思考
对软件研发项目管理的深入探讨
软件过程改进
软件过程改进的实现
 
相关文档

软件过程改进框架
软件过程改进的CMM-TSP-PSP模型
过程塑造(小型软件团队过程改进)
软件过程改进:经验和教训
 
相关课程

以"我"为中心的过程改进(iProcess )
iProcess过程改进实践
CMMI体系与实践
基于CMMI标准的软件质量保证

最新活动计划
人工智能.机器学习TensorFlow 6-30[直播]
基于 UML 和EA进行分析设计 7-30[北京]
软件架构设计方法、案例与实践 7-24[北京]
用户体验、易用性测试与评估 7-25[西安]
图数据库与知识图谱 8-23[北京]
需求分析师能力培养 8-28[北京]
 
 
最新文章
iPerson的过程观:要 过程 or 结果
基于模型的需求管理方法与工具
敏捷产品管理之 Story
敏捷开发需求管理(产品backlog)
Kanban看板管理实践精要
最新课程
基于iProcess的敏捷过程
软件开发过程中的项目管理
持续集成与敏捷开发
敏捷过程实践
敏捷测试-简单而可行
更多...   
成功案例
英特尔 SCRUM-敏捷开发实战
某著名汽车 敏捷开发过程与管理实践
北京 敏捷开发过程与项目管理
东方证券 基于看板的敏捷方法实践
亚信 工作量估算
更多...