| 本帖最后由 luobeihai 于 2025-1-6 00:05 编辑 
 #申请原创# @21小跑堂
 1. FreeRTOS的任务状态
 
 
 FreeRTOS中的任务状态,可以简单的分为运行态(running)和非运行态(not running)。 
 但是对于非运行态我们还可以继续细分: 
 1.1 阻塞状态阻塞状态(Blocked)暂停(挂起)状态(Suspended)就绪状态(Ready) 
 
 
 在FreeRTOS创建的任务是***都可以执行的,他们一直在不停地执行着自己的任务,而没有去等待其他的任何事件。 
 但是在实际的产品开发中,我们是不会让一个任务一直在运行的,而是使用“事件驱动”的方法让它运行。就是说任务要等待某个事件发生后它才能运行,这个等待某个事件的过程中就是处于阻塞状态,处于阻塞状态的任务是不会消耗CPU资源的。 
 在阻塞状态中,任务可以等待两种类型的事件: 1.2 暂停(挂起)状态
 
 
 FreeRTOS中的任务也可以进入暂停状态,唯一的方法是通过vTaskSuspend函数。函数原型如下: 
 void vTaskSuspend( TaskHandle_t xTaskToSuspend );
 参数xTaskToSuspend表示要暂停的任务,如果为NULL,表示暂停自己。 
 任务进入暂停态后,想要退出暂停状态,那只能由别的任务来操作: 
 其他任务调用vTaskResume中断程序调用xTaskResumeFromISR 
 其他任务或中断调用上面这两个函数,才能使得进入暂停态的任务退出暂停状态。 
 但是在实际开发中,暂停状态很少用。 1.3 就绪状态
 
 
 就绪状态比较简单,就是这个任务既没有阻塞也没有,也没有挂起,那么这个任务就是处于就绪状态的。处于就绪状态的任务是随时都可以被运行的,只是当前还没有运行。 
 一般任务创建后,就是处于就绪状态的。 1.4 状态转换图
 
 
 如下图,完整的展示了一个任务的各种状态转换。 
 
 2. FreeRTOS任务的创建和删除
 
 
 FreeRTOS有两种方式创建任务,动态方法和静态方法,他们的区别就是动态创建任务所使用到的堆栈由系统自动分配,而静态创建任务所使用到的堆栈则要由程序员自己指定了。 2.1 任务创建动态方法
 
 
 FreeRTOS动态方法创建任务的API是xTaskCreate,其函数原型如下: 
 /* 动态方法创建任务的API */
BaseType_t xTaskCreate(        TaskFunction_t pxTaskCode,                                // 任务函数
                        const char * const pcName,                                // 任务名称
                        const uint16_t usStackDepth,                        // 堆栈大小,实际大小为4 * usStackDepth
                        void * const pvParameters,                                // 传递给任务函数的参数
                        UBaseType_t uxPriority,                                        // 任务优先级
                        TaskHandle_t * const pxCreatedTask                 // 任务句柄(可以理解为就是该任务本身)
                      );
 参数介绍   
 xTaskCreate: 该参数就是指向用户任务函数的指针。任务函数也是一个C函数而已,只不过任务函数没有返回值,函数参数是一个void型指针,任务函数里面通常是一个死循环,里面执行着用户想要实现的任务功能。任务函数原型:void task_function( void *pvParameters )pcName: 任务名称。这个参数不会被 FreeRTOS 使用,其只是单纯地用于辅助调试。应用程序可以通过定义常量 config_MAX_TASK_NAME_LEN 来定义任务名的最大长度——包括’\0’结束符。如果传入的字符串长度超过了这个最大值,字符串将会自动被截断。usStackDepth: 我们创建的每个任务,他们都有唯一属于自己的任务堆栈空间。usStackDepth 值用于告诉内核为该任务分配多大的栈空间,单位是字节。实际分配的大小为:4 * usStackDepth 字节。pvParameters: 传递给任务函数的参数。uxPriority: 任务优先级,FreeRTOS中任务优先级的取值范围是: 0 ~ (configMAX_PRIORITIES – 1),取值越大说明优先级越高。 其中,configMAX_PRIORITIES 这个宏是由用户定义的常量,FreeRTOS并没有规定优先级的上限,但是我们在使用的时候,最好定义一个自己实际需要的大小,这样可以避免浪费内存。pxCreatedTask: 任务句柄。这个句柄将在 API 调用中对该创建出来的任务进行引用, 比如改变任务优先级, 或者删除任务。如果应用程序中不会用到这个任务的句柄,则 pxCreatedTask 可以被设为 NULL。 
 返回值 该API有两个返回值: 2.2 任务创建静态方法
 
 
 静态方法创建任务,需要用户自己指定任务堆栈和任务控制块,静态方法创建任务会比动态方法更复杂一些。静态方法创建任务函数原型如下: 
 /* 静态方法创建任务的API,创建成功函数返回该任务句柄 */
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         // 任务控制块
                              );
 参数介绍(只介绍和动态方法不同的几个参数)  
 返回值   2.3 任务删除
 
 
 当我们觉得某个任务已经不需要时,FreeRTOS提供vTaskDelete()函数来删除任务自身或者其他任务,任务一旦被删除之后,相当于不存在了,后续也不可能进入运行态。 
 如果删除的这个任务,是使用动态方法创建的,那么这个任务删除后,这个任务所用到的堆栈和任务控制块会被空闲任务自动回收。因此有一点很重要,那就是使用 vTaskDelete() 函数的任务千万不能把空闲任务的执行时间饿死,否则空闲任务得不到执行,那么被删除的任务内存也就回收不了了。 
 另外,该任务的内存只有由内核自动分配的,才会由空闲任务自动回收。而那些由用户自己申请的内存,比如pvPortMalloc()申请的内存,在任务删除后,需要用户自己去释放这部分内存,内核是不会自动帮我们回收的。 
 删除任务的函数原型如下: 
 void vTaskDelete( xTaskHandle pxTaskToDelete );
 其中参数pxTaskToDelete为要删除的某个任务句柄,另外任务可以传入NULL来删除自身。 2.4 任务创建和删除示例
 
 
 我们来设计一个例子。 
 首先在main函数里创建优先级为1的任务1,当任务1运行的时候,以优先级2创建任务2。这时因为任务2的优先级最高,所以会马上得到执行。任务2执行时,啥都不做,它只是把自己删除了(vTaskDelete传入NULL即可把自身删除)。当任务2删除自身之后,任务1又成为了最高的优先级任务,任务1继续执行,这时需要调用vTaskDelay()函数来阻塞一小段时间,让空闲任务得以执行,从而回收被删除了的任务2的内存。当任务1退出阻塞态后,再次成为就绪态中具有最高优先级的任务,因此会抢占空闲任务。任务1再次得以运行,如此往复下去。 
 代码清单如下: 
 #include <stdio.h>
#include "bsp_usart.h"
#include "FreeRTOS.h"
#include "task.h"
TaskHandle_t StartTask_Handler;                        //task1任务句柄
/* task2函数 */
void task2(void *pvParameters)
{        
        for ( ; ; )
        {
                printf("task2 is running and about to delete itself\n");
                vTaskDelete( NULL );                // 任务2把自己删除了
        }
}
/* task1函数 */
void task1(void *pvParameters)
{        
        for ( ; ; )
        {
                printf("task1 is running\n");
                xTaskCreate( task2, "task2", 1000, NULL, 2, NULL );                // 创建任务2
                vTaskDelay(1000);                // 延时1s,让空闲任务得以运行,从而回收被删除的任务2的内存
        }
}
int main()
{
        USART_Config();                // 串口初始化
        /* 创建任务1 */
    xTaskCreate((TaskFunction_t )task1,                             //任务函数
                (const char*    )"task1",                           //任务名称
                (uint16_t       )128,                                 //任务堆栈大小
                (void*          )NULL,                              //传递给任务函数的参数
                (UBaseType_t    )1,                                        //任务优先级
                (TaskHandle_t*  )StartTask_Handler);     //任务句柄
                                
    vTaskStartScheduler();                                                   //开启任务调度
}
 代码编译运行后,在串口助手上看到的运行结果如下: 
 
 和我们设计时分析的运行过程时一模一样的。为了更直观的了解这个例子的运行过程,可以看下面的任务运行的流程图: 
 3. FreeRTOS的任务优先级
 
 
 当我们使用xTaskCreate()        API函数创建一个任务的时候,会为任务赋予一个初始的优先级,当然这个优先级可以在调度器启动后,我们可以使用vTaskPrioritySet()        API函数来进行优先级修改的。 
 void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );
 其中xTask参数是传递进某个任务的句柄,NULL则表示修改自己的优先级。uxNewPriority参数表示新设置的优先级,取值范围0~(configMAX_PRIORITIES – 1)。 
 使用uxTaskPriorityGet来获得任务的优先级: 
 UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );
 xTask参数是某个任务句柄,NULL表示获取自己的优先级。返回值就是该任务的优先级。 
 优先级的取值范围是:0~(configMAX_PRIORITIES – 1),数值越大优先级越高。其中configMAX_PRIORITIES这个宏的值可以在FreeRTOSConfig.h 中设定。FreeRTOS虽然没有规定这个宏的最大值,但是实际开发中,我们应该尽量选择一个合适的取值,因为这个值越大,那么内核对内存的开销就越大。 
 FreeRTOS的调度器总是会确保: 
 
 
 
 |