FreeRTOS任务管理

作者 : admin 本文共2846个字,预计阅读时间需要8分钟 发布时间: 2024-06-16 共1人阅读

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

一、任务的创建与删除

1、什么是任务

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

void ATaskFunction(void *pvParameters);

        要注意的是:

        这个函数不能返回;

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

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

        每个任务都有自己的栈;

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

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

下面是一个示例:

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

2、任务的创建

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

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

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

参数说明:

FreeRTOS任务管理插图

FreeRTOS任务管理插图(1)

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

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

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

FreeRTOS任务管理插图(2)

3、任务的删除

void vTaskDelete( TaskHandle_t xTaskToDelete );

FreeRTOS任务管理插图(3)

4、任务的状态

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

FreeRTOS任务管理插图(4)

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

5、空闲任务

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

FreeRTOS任务管理插图(5)

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

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

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

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

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

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

FreeRTOS任务管理插图(6)

6、Delay函数

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

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

本站无任何商业行为
个人在线分享 » FreeRTOS任务管理
E-->