FreeRTOS学习笔记—任务创建和删除

简介: 本文学习了如何创建和删除任务。最后,分析解决了遇到的问题。


🎀 文章作者:二土电子
🐸 期待大家一起学习交流!


一、任务创建和删除API函数

FreeRTOS 的任务创建和删除相关的API函数有下面几个

  • xTaskCreate()
    使用动态的方法创建一个任务
  • xTaskCreateStatic()
    使用静态的方法创建一个任务
  • xTaskCreateRestricted()
    创建一个使用 MPU 进行限制的任务,相关内存使用动态内存分配
  • vTaskDelete()
    删除一个任务

MPU意思是Memory Protect Unit,即为存储保护单元,它是位于存储器内部的一个可编程的区域,定义了存储器的属性和存储器的访问权限。这里就不再对MPU做深入了解了。

1.1 xTaskCreate()函数

由于该函数是动态方法创建任务,因此它的任务内存需要使用“pvPortMalloc”去申请。所以在使用该函数创建任务时,对应的“configSUPPORT_DYNAMIC_ALLOCATION”宏定义要打开。

#define configSUPPORT_DYNAMIC_ALLOCATION        1                       //支持动态内存申请

下面来看一下xTaskCreate()函数的各个参数

BaseType_t xTaskCreate(    TaskFunction_t pxTaskCode,
                            const char * const pcName,
                            const uint16_t usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask )
  • pxTaskCode
    任务函数
  • pcName
    任务名字。任务名字不能超过“configMAX_TASK_NAME_LEN”
  • usStackDepth
    任务堆栈大小。需要注意的是,实际的任务堆栈大小是该值 * 4字节,在上一篇已经介绍过了。其中空闲任
    务的任务堆栈大小为“configMINIMAL_STACK_SIZE”
  • pvParameters
    传递给任务函数的参数
  • uxPriotiry
    任务优先级。范围是0~ configMAX_PRIORITIES-1
  • pxCreatedTask
    任务句柄。任务创建成功以后会返回此任务的任务句柄,这个句柄其实就是任务的任务堆栈。此参数就用来保存这个任务句柄。其他 API 函数可能会使用到这个句柄。任务句柄通俗来讲,就是任务的标识,用来区分是哪个任务。

该函数有两种返回值

  • pdPASS
    任务创建成功
  • errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY
    导致任务创建失败(通常可能是任务堆内存不足)

    1.2 xTaskCreateStatic()函数

    该函数是使用静态方法创建任务,需要将“configSUPPORT_STATIC_ALLOCATION”宏定义打开。该函数有以下参数
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 )
  • pxTaskCode
    任务函数
  • pcName
    任务名字。任务名字不能超过“configMAX_TASK_NAME_LEN”
  • usStackDepth
    任务堆栈大小。,由于是静态方法创建任务,所以任务堆栈由用户给出,一般是个数组,该参数值就是这个数组的大小
  • pvParameters
    传递给任务的参数
  • uxPriotiry
    任务优先级。范围是0~ configMAX_PRIORITIES-1
  • puxStackBuffer
    任务堆栈。一般为数组。数组类型要为 StackType_t 类型
  • pxTaskBuffer
    任务控制块

该函数的返回值如下

  • NULL
    任务创建失败(puxStackBuffer 或 pxTaskBuffer 为 NULL 的时候会导致这个错误的发生)
  • 其他值
    任务创建成功,返回任务的任务句柄

    1.3 vTaskDelete()函数

    使用任务删除函数时,需要开启“INCLUDE_vTaskDelete”宏定义。

删除一个用函数 xTaskCreate()或者 xTaskCreateStatic0创建的任务,被删除了的任务不再存在,也就是说再也不会进入运行态。任务被删除以后就不能再使用此任务的句柄。

如果此任务是使用动态方法创建的,也就是使用函数 xTaskCreate()创建的,那么在此任务被删除以后此任务之前申请的堆栈和控制块内存会在空闲任务中被释放掉,因此当调用函数 vTaskDelete()删除任务以后,必须给空闲任务一定的运行时间。

只有那些由内核分配给任务的内存才会在任务被删除以后自动的释放掉,用户分配给任务的内存需要用户自行释放掉,比如某个任务中用户调用函数 pvPotMalloc()分配了 500 字节的存,那么在此任务被删除以后用户也必须调用函数 vPortFree()将这 500 字节的内存释放掉,否则会导致内存泄露。

该函数的输入参数如下,无返回值

  • xTaskToDelete
    要删除的任务的任务句柄。如果在某函数中需要调用该函数删除自身,那么该参数写NULL即可。

二、任务创建和删除(动态方法)

下面根据正点原子提供的一个实验项目,学习一下上面任务创建与删除函数的使用。

2.1 任务要求

创建三个任务,分别是start_task,task1_task,task2_task,这三个任务的功能如下

  • start_task
    用来创建其他两个任务
  • task1_task
    此任务运行5次以后,调用任务删除函数vTaskDelete()删除task2_task。该任务也会控制LED0闪烁
  • task2_task
    该任务控制LED1的闪烁

    2.2 程序设计

    裸机状态下的主程序如下
int main(void)
{
         
    Med_Mcu_Iint();   // 系统初始化

    while (1)
    {
   
        // 具体处理
    }
}

使用操作系统时,首先要创建任务

void xxx_task (void *pxCreatedTask);   // 任务函数

int main (void)
{
   
    Med_Mcu_Iint();   // 系统初始化

    //创建任务
    xTaskCreate((TaskFunction_t )pxTaskCode,            //任务函数
                (const char*    )pcName,                //任务名称
                (uint16_t       )usStackDepth,          //任务堆栈大小
                (void*          )pvParameters,          //传递给任务函数的参数
                (UBaseType_t    )uxPriority,            //任务优先级
                (TaskHandle_t*  )&pxCreatedTask);       //任务句柄 
}

void xxx_task (void *pxCreatedTask)
{
   
    while1{
   
    }
}

有了上面的框架,只需要给参数赋值即可。对于任务堆栈,任务优先级等参数,可以用宏定义的方式写。需要注意的是,优先级0不能使用。因为空闲任务需要使用优先级0。在开启任务调度器时,会自动创建一个空闲任务,该任务的任务优先级为优先级0。最高优先级也不可以使用。因为使用软件定时器时,系统需要有一个定时器任务来维护这个软件定时器。这个定时器任务的任务优先级是最高优先级。

2.2.1 创建开始任务

//任务优先级
#define START_TASK_PRIO        1
//任务堆栈大小    
#define START_STK_SIZE         120  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task (void *pxCreatedTask);

//创建任务
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);   //任务句柄
void start_task (void *pxCreatedTask)
{
   
    //创建任务1
    xTaskCreate((TaskFunction_t )taks1_task,            //任务函数
                (const char*    )"taks1_task",          //任务名称
                (uint16_t       )TASK1_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )TASK1_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&TASK1Task_Handler);   //任务句柄

        //创建任务2
    xTaskCreate((TaskFunction_t )taks2_task,            //任务函数
                (const char*    )"taks2_task",          //任务名称
                (uint16_t       )TASK2_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )TASK2_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&TASK2Task_Handler);   //任务句柄 

        // 开始任务只需要执行一次,执行完成后删除即可
        vTaskDelete(StartTask_Handler); //删除开始任务
}

2.2.2 创建任务1和任务2

关于任务1和任务2的一些宏定义,这里就不再详细列出来了,下面是任务1和任务2的函数

void taks1_task (void *pxCreatedTask)
{
   
    u8 task1Cunt = 0;   // 任务1运行次数计数变量

    while (1)
    {
   
        task1Cunt = task1Cunt + 1;   // task1运行次数加1

        Med_Led_StateReverse(LED0);   // LED0状态取反
        vTaskDelay(500);   // 延时500ms

        // 运行5次后
        if (task1Cunt >= 5 && TASK2Task_Handler != NULL)
        {
   
            // 删除任务2
            vTaskDelete(TASK2Task_Handler);
            TASK2Task_Handler = NULL;
        }
    }
}
void taks2_task (void *pxCreatedTask)
{
   
    while (1)
    {
   
        Med_Led_StateCtrl(LED1,LED_ON);   // 点亮LED1
        vTaskDelay(200);   // 延时200ms

        Med_Led_StateCtrl(LED1,LED_OFF);   // 熄灭LED1
        vTaskDelay(800);   // 延时800ms
    }
}

需要注意的是,在删除任务时,需要判断一下要删除任务的任务句柄是否为无效(NULL)。

2.2.3 开启任务调度器

vTaskStartScheduler();          //开启任务调度

三、总结

在其他任务中删除另一个任务,可能会有很多后遗症,所以删除时最好是任务子自删除。

3.1 删除任务后任务句柄不是NULL

最初删除任务2的程序如下

// 运行5次后
if (task1Cunt >= 5)
{
   
    // 删除任务2
    vTaskDelete(TASK2Task_Handler);
}

发现在运行5次任务1之后,程序就卡住了。打断点发现,程序卡在了删除任务2那里。一直在删除任务2。在删除任务2之前加入判断,判断任务2的任务句柄是否为NULL。

// 运行5次后
if (task1Cunt >= 5 && TASK2Task_Handler != NULL)
{
   
    // 删除任务2
    vTaskDelete(TASK2Task_Handler);
}

发现程序依旧会卡在删除任务2这里。用Debug查看删除任务2后任务2的任务句柄的值。发现在删除任务2之后,任务2的任务句柄依旧是有值的。冲浪后发现,大家也遇到了这种情况。同时,也学到了一种简单粗暴的处理方法。在删除任务后,将被删除任务的任务句柄修改为NULL。

// 运行5次后
if (task1Cunt >= 5 && TASK2Task_Handler != NULL)
{
   
    // 删除任务2
    vTaskDelete(TASK2Task_Handler);
    TASK2Task_Handler = NULL;
}

修改完成后,程序运行正常。

相关文章
|
存储 算法 安全
【Freertos基础入门】队列(queue)的使用
【Freertos基础入门】队列(queue)的使用
978 0
|
传感器 调度 开发者
【Freertos基础入门】freertos任务的优先级
【Freertos基础入门】freertos任务的优先级
1555 0
|
编译器 调度
FreeRTOS任务的创建(动态方法和静态方法)
FreeRTOS任务的创建(动态方法和静态方法)
2114 0
WK
|
人工智能 算法 C语言
为什么C语言不是人工智能领域的首选编程语言?
尽管多种编程语言在人工智能领域均有应用,Python却因其独特优势成为了首选。Python的简洁语法提高了开发效率与易用性;其庞大的生态系统,尤其是丰富的AI库和框架如TensorFlow、PyTorch等,简化了算法实现过程;Python适用于快速原型设计与实验,加速项目迭代;良好的跨平台兼容性减少了配置负担;庞大的社区支持与丰富的文档资源便于学习和技术交流。尽管如此,C语言在性能等方面仍具优势,在特定场景下不可或缺。
WK
526 60
|
调度 开发者
【Freertos基础入门】2个Freertos的Delay函数
【Freertos基础入门】2个Freertos的Delay函数
1532 1
9-1| cp: 无法获取"884/*" 的文件状态(stat): 没有那个文件或目录 这是什么意思
9-1| cp: 无法获取"884/*" 的文件状态(stat): 没有那个文件或目录 这是什么意思
WK
|
机器学习/深度学习 算法 大数据
鱼群算法
鱼群算法(FSA)是一种基于仿生学的群智能算法,模拟鱼群在水中集群、觅食和逃避捕食的行为,寻找问题空间中的全局最优解。该算法由李晓磊等人于2002年提出,通过初始化鱼群、评估适应度、更新行为和终止条件等步骤进行迭代优化。其优点包括实现简单、全局搜索能力强和自适应性好,但收敛速度较慢且易陷入局部最优。FSA已广泛应用于函数优化、路径规划、图像分割等领域,并有望通过改进性能、结合其他算法及拓展应用领域等方式进一步提升其应用价值。
WK
289 0
|
存储 安全 算法
从0入门FreeRTOS之第二节FreeRTOS的体系结构
FreeRTOS的体系结构设计精巧且高效,专为嵌入式系统量身打造。理解FreeRTOS的体系结构对开发高效、稳定的嵌入式应用至关重要。下面,我们详细介绍FreeRTOS的核心组件、内核机制、中断管理和内存管理等内容。
524 0
|
Swift iOS开发 C++
设置Swift开发环境
设置Swift开发环境
235 1