Ai-M61-32S AP 配网学习 之 FreeRTOS任务

[复制链接]
查看1413 | 回复6 | 2023-11-30 17:16:17 | 显示全部楼层 |阅读模式

本帖最后由 WT_0213 于 2023-11-30 17:16 编辑

FreeRTOS任务控制API函数主要实现任务延时、任务挂起、解除任务挂起、任务优先级获取和设置等功能。

1.任务创建

1.1函数描述
BaseType_t xTaskCreate(
    TaskFunction_t pvTaskCode,
    const char * const pcName,
    unsigned short usStackDepth,
    void *pvParameters,
    UBaseType_t uxPriority,
    TaskHandle_t * pvCreatedTask
);

创建新的任务并加入任务就绪列表。

如果使用FreeRTOS-MPU(在官方下载包中,为Cortex-M3内核写了两个移植方案,一个是普通的FreeRTOS移植层,还有一个是FreeRTOS-MPU移植层。后者包含完整的内存保护),那么推荐使用函数xTaskCreateRestricted()来代替xTaskCreate()。在使用FreeRTOS-MPU的情况下,使用xTaskCreate()函数可以创建运行在特权模式或用户模式(见下面对函数参数uxPriority的描述)的任务。当运行在特权模式下,任务可以访问整个内存映射;当处于用户模式下,任务仅能访问自己的堆栈。无论在何种模式下,MPU都不会自动捕获堆栈溢出,因此标准的FreeRTOS堆栈溢出检测机制仍然会被用到。xTaskCreateRestricted()函数具有更大的灵活性。

1.2参数描述

pvTaskCode:指针,指向任务函数的入口。任务永远不会返回(位于死循环内)。该参数类型TaskFunction_t定义在文件projdefs.h中,定义为:typedefvoid (TaskFunction_t)( void )。 pcName:任务描述。主要用于调试。字符串的最大长度由宏configMAX_TASK_NAME_LEN指定,该宏位于FreeRTOSConfig.h文件中。 usStackDepth:指定任务堆栈大小,能够支持的堆栈变量数量,而不是字节数。比如,在16位宽度的堆栈下,usStackDepth定义为100,则实际使用200字节堆栈存储空间。堆栈的宽度乘以深度必须不超过size_t类型所能表示的最大值。比如,size_t为16位,则可以表示的最大值是65535。 pvParameters:指针,当任务创建时,作为一个参数传递给任务。 uxPriority:任务的优先级。具有MPU支持的系统,可以通过置位优先级参数的portPRIVILEGE_BIT位,随意的在特权(系统)模式下创建任务。比如,创建一个优先级为2的特权任务,参数uxPriority可以设置为( 2 | portPRIVILEGE_BIT )。 pvCreatedTask:用于回传一个句柄(ID),创建任务后可以使用这个句柄引用任务。

1.3返回值

如果任务成功创建并加入就绪列表函数返回pdPASS,否则函数返回错误码。

1.4用法举例
 /* 创建任务. */
void vTaskCode( void * pvParameters )
{
    for( ;; )
    {
       /* 任务代码放在这里 */
    }
}

/* 创建任务函数 */
void vOtherFunction( void )
{
    static unsigned char ucParameterToPass;
    xTaskHandle xHandle;

     /* 创建任务,存储句柄。注:传递的参数ucParameterToPass必须和任务具有相同的生存周期,
        因此这里定义为静态变量。如果它只是一个自动变量,可能不会有太长的生存周期,因为
                中断和高优先级任务可能会用到它。 */
     xTaskCreate( vTaskCode, "NAME", STACK_SIZE,&ucParameterToPass, tskIDLE_PRIORITY, &xHandle );

     /* 使用句柄删除任务. */
    if( xHandle !=NULL )
    {
        vTaskDelete( xHandle );
    }
}

2.任务删除

2.1 任务描述

voidvTaskDelete( TaskHandle_t xTask );

从RTOS内核管理器中删除一个任务。任务删除后将会从就绪、阻塞、暂停和事件列表中移除。在文件FreeRTOSConfig.h中,必须定义宏INCLUDE_vTaskDelete 为1,本函数才有效。

注:被删除的任务,其在任务创建时由内核分配的存储空间,会由空闲任务释放。如果有应用程序调用xTaskDelete(),必须保证空闲任务获取一定的微控制器处理时间。任务代码自己分配的内存是不会自动释放的,因此删除任务前,应该将这些内存释放。

2.2参数描述

xTask:被删除任务的句柄。为NULL表示删除当前任务。

3.相对延时

3.1函数描述

void vTaskDelay( portTickTypexTicksToDelay )

调用vTaskDelay()函数后,任务会进入阻塞状态,持续时间由vTaskDelay()函数的参数xTicksToDelay指定,单位是系统节拍时钟周期。常量portTICK_RATE_MS 用来辅助计算真实时间,此值是系统节拍时钟中断的周期,单位是毫秒。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskDelay 必须设置成1,此函数才能有效。

vTaskDelay()指定的延时时间是从调用vTaskDelay()后开始计算的相对时间。比如vTaskDelay(100),那么从调用vTaskDelay()后,任务进入阻塞状态,经过100个系统时钟节拍周期,任务解除阻塞。因此,vTaskDelay()并不适用与周期性执行任务的场合。此外,其它任务和中断活动,会影响到vTaskDelay()的调用(比如调用前高优先级任务抢占了当前任务),因此会影响任务下一次执行的时间。API函数vTaskDelayUntil()可用于固定频率的延时,它用来延时一个绝对时间。

3.2参数描述

xTicksToDelay:延时时间总数,单位是系统时钟节拍周期。

3.3用法举例
voidvTaskFunction( void * pvParameters )
 {
     /* 阻塞500ms. */
     constportTickType xDelay = 500 / portTICK_RATE_MS;

     for( ;; )
     {
         /* 每隔500ms触发一次LED, 触发后进入阻塞状态 */
         vToggleLED();
         vTaskDelay( xDelay );
     }
}

4.绝对延时

4.1函数描述

void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_txTimeIncrement );

任务延时一个指定的时间。周期性任务可以使用此函数,以确保一个恒定的频率执行。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskDelayUntil 必须设置成1,此函数才有效。

这个函数不同于vTaskDelay()函数的一个重要之处在于:vTaskDelay()指定的延时时间是从调用vTaskDelay()之后(执行完该函数)开始算起的,但是vTaskDelayUntil()指定的延时时间是一个绝对时间。

调用vTaskDelay()函数后,任务会进入阻塞状态,持续时间由vTaskDelay()函数的参数指定,单位是系统节拍时钟周期。因此vTaskDelay()并不适用于周期性执行任务的场合。因为调用vTaskDelay()到任务解除阻塞的时间不总是固定的并且该任务下一次调用vTaskDelay()函数的时间也不总是固定的(两次执行同一任务的时间间隔本身就不固定,中断或高优先级任务抢占也可能会改变每一次执行时间)。

vTaskDelay()指定一个从调用vTaskDelay()函数后开始计时,到任务解除阻塞为止的相对时间,而vTaskDelayUntil()指定一个绝对时间,每当时间到达,则解除任务阻塞。

应当指出的是,如果指定的唤醒时间已经达到,vTaskDelayUntil()立刻返回(不会有阻塞)。因此,使用vTaskDelayUntil()周期性执行的任务,无论任何原因(比如,任务临时进入挂起状态)停止了周期性执行,使得任务少运行了一个或多个执行周期,那么需要重新计算所需要的唤醒时间。这可以通过传递给函数的指针参数pxPreviousWake指向的值与当前系统时钟计数值比较来检测,在大多数情况下,这并不是必须的。

常量portTICK_RATE_MS 用来辅助计算真实时间,此值是系统节拍时钟中断的周期,单位是毫秒。

当调用vTaskSuspendAll()函数挂起RTOS调度器时,不可以使用此函数。

4.2参数描述

pxPreviousWakeTime:指针,指向一个变量,该变量保存任务最后一次解除阻塞的时间。第一次使用前,该变量必须初始化为当前时间。之后这个变量会在vTaskDelayUntil()函数内自动更新。 xTimeIncrement:周期循环时间。当时间等于(*pxPreviousWakeTime + xTimeIncrement)时,任务解除阻塞。如果不改变参数xTimeIncrement的值,调用该函数的任务会按照固定频率执行。

4.3用法举例
//每10次系统节拍执行一次
 void vTaskFunction( void * pvParameters )
 {
     static portTickType xLastWakeTime;
     const portTickType xFrequency = 10;

     // 使用当前时间初始化变量xLastWakeTime
     xLastWakeTime = xTaskGetTickCount();

     for( ;; )
     {
         //等待下一个周期
         vTaskDelayUntil( &xLastWakeTime,xFrequency );

         // 需要周期性执行代码放在这里
     }
 }

5.获取任务优先级

5.1函数描述

UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask );

获取指定任务的优先级。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskPriorityGet必须设置成1,此函数才有效。

5.2参数描述

xTask:任务句柄。NULL表示获取当前任务的优先级。

5.3返回值

返回指定任务的优先级。

5.4用法举例
voidvAFunction( void )
 {
     xTaskHandlexHandle;
     // 创建任务,保存任务句柄
     xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
     // ...
     // 使用句柄获取创建的任务的优先级
     if( uxTaskPriorityGet( xHandle ) !=tskIDLE_PRIORITY )
     {
         // 任务可以改变自己的优先级
     }
     // ...
     // 当前任务优先级比创建的任务优先级高?
     if( uxTaskPriorityGet( xHandle ) <uxTaskPriorityGet( NULL ) )
     {
         // 当前优先级较高
     }
 }

6.设置任务优先级

6.1函数描述

void vTaskPrioritySet( TaskHandle_txTask, UBaseType_tuxNewPriority );

设置指定任务的优先级。如果设置的优先级高于当前运行的任务,在函数返回前会进行一次上下文切换。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskPrioritySet 必须设置成1,此函数才有效。

6.2参数描述

xTask:要设置优先级任务的句柄,为NULL表示设置当前运行的任务。

uxNewPriority:要设置的新优先级。

6.3用法举例
voidvAFunction( void )
 {
     xTaskHandlexHandle;
     // 创建任务,保存任务句柄。
     xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
     // ...
     // 使用句柄来提高创建任务的优先级
     vTaskPrioritySet( xHandle,tskIDLE_PRIORITY + 1 );
     // ...
     // 使用NULL参数来提高当前任务的优先级,设置成和创建的任务相同。
     vTaskPrioritySet( NULL, tskIDLE_PRIORITY +1 );
 }

7.任务挂起

7.1函数描述

void vTaskSuspend( TaskHandle_txTaskToSuspend );

挂起指定任务。被挂起的任务绝不会得到处理器时间,不管该任务具有什么优先级。

调用vTaskSuspend函数是不会累计的:即使多次调用vTaskSuspend ()函数将一个任务挂起,也只需调用一次vTaskResume ()函数就能使挂起的任务解除挂起状态。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskSuspend必须设置成1,此函数才有效。

7.2参数描述

xTaskToSuspend:要挂起的任务句柄。为NULL表示挂起当前任务。

7.3用法举例
voidvAFunction( void )
 {
     xTaskHandlexHandle;
     // 创建任务,保存任务句柄.
     xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
     // ...
     // 使用句柄挂起创建的任务.
     vTaskSuspend( xHandle );
     // ...
     // 任务不再运行,除非其它任务调用了vTaskResume(xHandle )
     //...
     // 挂起本任务.
     vTaskSuspend( NULL );
     // 除非另一个任务使用handle调用了vTaskResume,否则永远不会执行到这里
 }

8.恢复挂起的任务

8.1函数描述

void vTaskResume( TaskHandle_txTaskToResume );

恢复挂起的任务。

通过调用一次或多次vTaskSuspend()挂起的任务,可以调用一次vTaskResume ()函数来再次恢复运行。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskSuspend必须置1,此函数才有效。

8.2参数描述

xTaskToResume:要恢复运行的任务句柄。

8.3用法举例
voidvAFunction( void )
 {
         xTaskHandle xHandle;
     // 创建任务,保存任务句柄
     xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
     // ...
     // 使用句柄挂起创建的任务
     vTaskSuspend( xHandle );
     // ...
     //任务不再运行,除非其它任务调用了vTaskResume(xHandle )  
          //...
     // 恢复挂起的任务.
     vTaskResume( xHandle );
     // 任务再一次得到处理器时间
     // 任务优先级与之前相同
 }

9.恢复挂起的任务(在中断服务函数中使用)

9.1函数描述

BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume );

用于恢复一个挂起的任务,用在ISR中。

通过调用一次或多次vTaskSuspend()函数而挂起的任务,只需调用一次xTaskResumeFromISR()函数即可恢复运行。

xTaskResumeFromISR()不可用于任务和中断间的同步,如果中断恰巧在任务被挂起之前到达,这就会导致一次中断丢失(任务还没有挂起,调用xTaskResumeFromISR()函数是没有意义的,只能等下一次中断)。这种情况下,可以使用信号量作为同步机制。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 必须设置成1,此函数才有效。

9.2参数描述

xTaskToResume:要恢复运行的任务句柄。

9.3返回值

如果恢复任务后需要上下文切换返回pdTRUE,否则返回pdFALSE。由ISR确定是否需要上下文切换。

9.4用法举例
xTaskHandlexHandle;               //注意这是一个全局变量

 void vAFunction( void )
 {
     // 创建任务并保存任务句柄
     xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );

     // ... 剩余代码.
 }

 void vTaskCode( void *pvParameters )
 {
     for( ;; )
     {
         // ... 在这里执行一些其它功能

         // 挂起自己
         vTaskSuspend( NULL );

         //直到ISR恢复它之前,任务会一直挂起
     }
 }

 void vAnExampleISR( void )
 {
     portBASE_TYPExYieldRequired;

     // 恢复被挂起的任务
     xYieldRequired = xTaskResumeFromISR(xHandle );

     if( xYieldRequired == pdTRUE )
     {
         // 我们应该进行一次上下文切换
         // 注:  如何做取决于你具体使用,可查看说明文档和例程
         portYIELD_FROM_ISR();
     }
 }

版权声明:本文为CSDN博主「研究是为了理解」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:http://t.csdnimg.cn/pYeNL

回复

使用道具 举报

bzhou830 | 2023-12-1 08:42:48 | 显示全部楼层
选择去发光,而不是被照亮
回复

使用道具 举报

san | 2023-12-1 23:27:09 | 显示全部楼层
学习
回复

使用道具 举报

爱玩开发板的湖 | 2023-12-7 13:40:03 | 显示全部楼层
不错的贴子,学习了
回复 支持 反对

使用道具 举报

1084504793 | 2023-12-7 16:49:59 | 显示全部楼层
学习了
回复

使用道具 举报

lazy | 2023-12-9 12:58:59 | 显示全部楼层
学习了
回复

使用道具 举报

心云 | 2023-12-10 11:01:54 | 显示全部楼层
学习
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则