• FreeRTOS任务管理


    使用FreeRTOS时,我们可以在application中创建多个任务(task),有些文档把任务也称为线程(thread)。

    一、任务的创建与删除

    1、什么是任务

            在FreeRTOS中,任务就是一个函数,原型如下:

    void ATaskFunction(void *pvParameters);

            要注意的是:

            这个函数不能返回;

            同一个函数,可以用来创建多个任务,换句话说,多个任务可以运行同一个函数;

            函数内部,尽量使用局部变量;

            每个任务都有自己的栈;

            每个任务运行这个函数时,任务A的局部变量放在任务A的栈里,任务B的局部变量放在任务B的栈里,不同任务的局部变量,有自己的副本。

            函数使用全局变量、静态变量的话;只有一个副本:多个任务使用的是同一个副本;要防止冲突。

    下面是一个示例:

    1. void ATaskFunction( void *pvParameters )
    2. {
    3. /* 对于不同的任务,局部变量放在任务的栈里,有各自的副本 */
    4. int32_t lVariableExample = 0;
    5. /* 任务函数通常实现为一个无限循环 */
    6. for( ;; )
    7. {
    8. /* 任务的代码 */
    9. }
    10. /* 如果程序从循环中退出,一定要使用vTaskDelete删除自己
    11. * NULL表示删除的是自己
    12. */
    13. vTaskDelete( NULL );
    14. /* 程序不会执行到这里, 如果执行到这里就出错了 */

    2、任务的创建

    创建任务时可以使用2个函数:动态分配内存、静态分配内存。

    使用动态分配内存的函数如下:

    1. BaseType_t xTaskCreate(
    2. TaskFunction_t pxTaskCode, // 函数指针, 任务函数
    3. const char * const pcName, // 任务的名字
    4. const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位为word,10表示40字节
    5. void * const pvParameters, // 调用任务函数时传入的参数
    6. UBaseType_t uxPriority, // 优先级
    7. TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用它来操作这个任务

    参数说明:

    使用静态分配内存的函数如下:

    1. TaskHandle_t xTaskCreateStatic (
    2. TaskFunction_t pxTaskCode, // 函数指针, 任务函数
    3. const char * const pcName, // 任务的名字
    4. const uint32_t ulStackDepth, // 栈大小,单位为word,10表示40字节
    5. void * const pvParameters, // 调用任务函数时传入的参数
    6. UBaseType_t uxPriority, // 优先级
    7. StackType_t * const puxStackBuffer, // 静态分配的栈,就是一个buffer
    8. StaticTask_t * const pxTaskBuffer // 静态分配的任务结构体的指针, 用它来操作这个任务
    9. );

    相比于使用动态分配内存创建任务的函数,最后2个参数不一样:

    3、任务的删除

    void vTaskDelete( TaskHandle_t xTaskToDelete );

    4、任务的状态

    以前我们很简单地把任务的状态分为 2 中:运行(Runing)、非运行(Not Running)。
    对于非运行的状态,还可以继续细分,比如前面的FreeRTOS_04_task_priority中:
    ⚫ Task3 执行 vTaskDelay 后:处于非运行状态,要过 3 秒种才能再次运行
    ⚫ Task3 运行期间, Task1、 Task2 也处于非运行状态,但是它们随时可以运行
    ⚫ 这两种"非运行"状态就不一样,可以细分为:
            阻塞状态(Blocked)
            暂停状态(Suspended)
            就绪状态(Ready)

    暂停状态(Suspended)
    void vTaskSuspend( TaskHandle_t xTaskToSuspend );
    参数 xTaskToSuspend 表示要暂停的任务,如果为 NULL,表示暂停自己。
    要退出暂停状态,只能由别人来操作:
    ⚫ 别的任务调用: vTaskResume
    ⚫ 中断程序调用: xTaskResumeFromISR

    5、空闲任务

    一个任务不能执行结束,否则会进入prvTaskExitError函数,在此关闭所有的中断,所有的任务将都不能执行。一个任务想要结束,只能是自杀或者他杀(vTaskDelete)。

    delay函数建议修改为vTaskDelay()函数,该函数不进行任务调度。

            空闲任务可以释放被删除的任务的内存。 一个良好的程序,它的任务都是事件驱动的:平时大部分时间处于阻塞状态。有可能我们自己创建的所有任务都无法执行,但是调度 器 必 须 能 找 到 一 个 可 以 运 行 的 任 务 : 所 以 , 我 们 要 提 供 空 闲 任 务 。 在 使 vTaskStartScheduler()函数来创建、启动调度器时,这个函数内部会创建空闲任务:

    ⚫ 空闲任务优先级为 0:它不能阻碍用户任务运行
    ⚫ 空闲任务要么处于就绪态,要么处于运行态,永远不会阻塞

    我们可以添加一个空闲任务的钩子函数(Idle Task Hook Functions),空闲任务的循
    环每执行一次,就会调用一次钩子函数。钩子函数的作用有这些:

    ⚫ 执行一些低优先级的、后台的、需要连续执行的函数
    ⚫ 测量系统的空闲时间:空闲任务能被执行就意味着所有的高优先级任务都停
    止了,所以测量空闲任务占据的时间,就可以算出处理器占用率。
    ⚫ 让系统进入省电模式:空闲任务能被执行就意味着没有重要的事情要做,当
    然可以进入省电模式了。
    ⚫ 空闲任务的钩子函数的限制:
    ⚫ 不能导致空闲任务进入阻塞状态、暂停状态
    ⚫ 如果你会使用 vTaskDelete()来删除任务,那么钩子函数要非常高效地执
    行。如果空闲任务移植卡在钩子函数里的话,它就无法释放内存。

    在 FreeRTOS\Source\tasks.c 中,可以看到如下代码,所以前提就是:
    ⚫ 把这个宏定义为 1: configUSE_IDLE_HOOK
    ⚫ 实现 vApplicationIdleHook 函数

    6、Delay函数

    ⚫ vTaskDelay:至少等待指定个数的 Tick Interrupt 才能变为就绪状态
    ⚫ vTaskDelayUntil:等待到指定的绝对时刻,才能变为就绪态。
     

    1. void vTaskDelay( const TickType_t xTicksToDelay ); /* xTicksToDelay: 等待多少给Tick */
    2. /* pxPreviousWakeTime: 上一次被唤醒的时间
    3. * xTimeIncrement: 要阻塞到(pxPreviousWakeTime + xTimeIncrement)
    4. * 单位都是Tick Count
    5. */
    6. BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
    7. const TickType_t xTimeIncrement );

  • 相关阅读:
    Kafka监控工具,LinkedIn详解
    从零开始使用git
    DS18B20详解
    反转字符串中的元音字母
    无线接入回传一体化关键技术及标准化进展
    子查询+UNION+LIMIT
    编码与测试
    厂家解读新标准GB21148-2020《足部防护 安全鞋》的变化有哪些
    长安链GO语言智能合约编写与编译
    spark知识点总结(1)
  • 原文地址:https://blog.csdn.net/a1233219/article/details/139577899