FreeRTOS学习笔记 & FreeRTOS任务管理

FreeRTOS是一个实时操作系统,支持多任务。任务是系统资源的竞争单元,有就绪、运行、阻塞和挂起四种状态。任务调度器基于优先级全抢占式,支持时间片轮转。常用API包括任务延迟、优先级设置、挂起和删除等。任务状态查询API可获取任务信息、堆栈使用情况和运行时间等。
摘要由CSDN通过智能技术生成

任务简介

从系统的角度看,任务是竞争系统资源的最小运行单元。FreeRTOS 是一个支持多任务的操作系统。
在 FreeRTOS 中,任务可以使用或等待 CPU、使用内存空间等系统资源,并独立于其它任务运行,任何数量的任务可以共享同一个优先级,如果宏 configUSE_TIME_SLICING 定义为 1,处于就绪态的多个相同优先级任务将会以时间片切换的方式共享处理器。

简而言之:FreeRTOS 的任务可认为是一系列独立任务的集合。每个任务在自己的环境中运行。
在任何时刻,只有一个任务得到运行,FreeRTOS 调度器决定运行哪个任务。调度器会不断的启动、停止每一个任务,宏观看上去所有的任务都在同时在执行。
作为任务,不需要对调度器的活动有所了解,在任务切入切出时保存上下文环境(寄存器值、堆栈内容)是调度器主要的职责。为了实现这点,每个 FreeRTOS 任务都需要有自己的栈空间。
当任务切出时,它的执行环境会被保存在该任务的栈空间中,这样当任务再次运行时,就能从堆栈中正确的恢复上次的运行环境,任务越多,需要的堆栈空间就越大,而一个系统能运行多少个任务,取决于系统的可用的 SRAM。

FreeRTOS 中的任务是抢占式调度机制,高优先级的任务可打断低优先级任务,低优先级任务必须在高优先级任务阻塞或结束后才能得到调度。同时FreeRTOS 也支持时间片轮转调度方式,只不过时间片的调度是不允许抢占任务的 CPU 使用权。
任务通常会运行在一个死循环中,也不会退出,如果一个任务不再需要,可以调用 FreeRTOS 中的任务删除 API 函数接口将其删除

任务调度器简介

FreeRTOS 中提供的任务调度器是基于优先级的全抢占式调度:在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的。系统理论上可以支持无数个优先级(0 ~ N),
优先级数值越小的任务优先级越低,0 为最低优先级,分配给空闲任务使用,一般不建议用户来使用这个优先级。
假如使能了configUSE_PORT_OPTIMISED_TASK_SELECTION 这个宏(在 FreeRTOSConfig.h 文件定义),一般强制限定最大可用优先级数目为 32。在一些资源比较紧张的系统中,可以根据实际情况选择只支持 8 个或 32 个优先级的系统配置。在系统
中,当有比当前任务优先级更高的任务就绪时,当前任务将立刻被换出,高优先级任务抢占处理器运行。

FreeRTOS 内核中采用两种方法寻找最高优先级的任务,
第一种是通用的方法,在就绪链表中查找从高优先级往低查找 uxTopPriority,因为在创建任务的时候已经将优先级进行排序,查找到的第一个 uxTopPriority 就是我们需要的任务,然后通过 uxTopPriority 获取对应的任务控制块。
第二种方法则是特殊方法,利用计算前导零指令 CLZ,直接在 uxTopReadyPriority 这个 32 位的变
量中直接得出 uxTopPriority,这样子就知道哪一个优先级任务能够运行,这种调度算法比普通方法更快捷,但受限于平台(在 STM32 中我们就使用这种方法)。

FreeRTOS 内核中也允许创建相同优先级的任务。相同优先级的任务采用时间片轮转方式进行调度(也就是通常说的分时调度器),时间片轮转调度仅在当前系统中无更高优先级就绪任务存在的情况下才有效。为了保证系统的实时性,系统尽最大可能地保证高优先级的任务得以运行。任务调度的原则是一旦任务状态发生了改变,并且当前运行的任务优先级小于优先级队列组中任务最高优先级时,立刻进行任务切换(除非当前系统处于中断处理程序中或禁止任务切换的状态)。

任务状态简介

FreeRTOS 系统中的每一任务都有多种运行状态。
系统初始化完成后,创建的任务就可以在系统中竞争一定的资源,由内核进行调度。
任务状态通常分为以下四种:
就绪(Ready):该任务在就绪列表中,就绪的任务已经具备执行的能力,只等待调度器进行调度,新创建的任务会初始化为就绪态。

运行(Running):该状态表明任务正在执行,此时它占用处理器,FreeRTOS调度器选择运行的永远是处于最高优先级的就绪态任务,当任务被运行的一刻,它的任务状态就变成了运行态。

阻塞(Blocked):如果任务当前正在等待某个时序或外部中断,我们就说这个任务处于阻塞状态,该任务不在就绪列表中。包含任务被挂起、任务被延时、任务正在等待信号量、读写队列或者等待读写事件等。

挂起态(Suspended):处于挂起态的任务对调度器而言是不可见的,让一个任务进入挂起状态的唯一办法就是调用 vTaskSuspend()函数;
而把一个挂起状态的任务恢复的唯一途径就是调用 vTaskResume() 或 vTaskResumeFromISR()函数,
我们可以这么理解挂起态与阻塞态的区别,当任务有较长的时间不允许运行的时候,我们可以挂起任务,这样子调度器就不会管这个任务的任何信息,直到我们调用恢复任务的 API 函数;而任务处于阻塞态的时候,系统还需要判断阻塞态的任务是否超时,是否可以解除阻塞

常用任务管理 API 函数

vTaskDelay 相对延时

必须将 INCLUDE_vTaskDelay 定义为 1,此函数才可用。
#define INCLUDE_vTaskDelay				           1

void vTaskDelay( const TickType_t xTicksToDelay );

参数:
xTicksToDelay 	调用任务应阻塞的 tick 周期数。

用法示例:

 void vTaskFunction( void * pvParameters )
 {
 /* Block for 500ms. */
 const TickType_t xDelay = 500 / portTICK_PERIOD_MS;

     for( ;; )
     {
         /* Simply toggle the LED every 500ms, blocking between each toggle. */
         vToggleLED();
         vTaskDelay( xDelay );
     }
}

vTaskDelayUntil 绝对延时

INCLUDE_vTaskDelayUntil 必须被定义为 1 才能使用此函数
#define INCLUDE_vTaskDelayUntil 1

void vTaskDelayUntil( TickType_t *pxPreviousWakeTime,
                      const TickType_t xTimeIncrement );

参数:
pxPreviousWakeTime 	指向一个变量的指针,该变量 任务上次被取消阻止。 该变量在第一次使用前 必须用当前时间进行初始化(见下方示例)。 在这之后,该变量 会在 vTaskDelayUntil() 中自动更新。
xTimeIncrement 	周期时间段。 该任务将在 (*pxPreviousWakeTime + xTimeIncrement)时间解除阻塞。 配合相同的 xTimeIncrement 参数值 调用 vTaskDelayUntil 将导致任务 以固定的间隔期执行。

示例用法:
 // Perform an action every 10 ticks.
 void vTaskFunction( void * pvParameters )
 {
 TickType_t xLastWakeTime;
 const TickType_t xFrequency = 10;

     // Initialise the xLastWakeTime variable with the current time.
     xLastWakeTime = xTaskGetTickCount();

     for( ;; )
     {
         // Wait for the next cycle.
         vTaskDelayUntil( &xLastWakeTime, xFrequency );

         // Perform action here.
     }
 }

xTaskAbortDelay 强制任务离开阻塞状态

必须将 INCLUDE_xTaskAbortDelay 定义为 1,此函数才可用
#define INCLUDE_xTaskAbortDelay 1

BaseType_t xTaskAbortDelay( TaskHandle_t xTask );
参数:
xTask  	将被强制退出阻塞状态的任务的句柄 。
要获得任务句柄,请使用 xTaskCreate() 创建任务并使用 pxCreatedTask 参数,或使用返回值创建任务 xTaskCreateStatic() 并存储该值,或在 xTaskGetHandle() 时使用任务的名字。
Returns:
如果 xTask 引用的任务不在“阻塞”状态, 则返回 pdFAIL。 否则返回 pdPASS。

用法示例:

 void vTaskFunction( void * pvParameters )
 {
 /* Block for 500ms. */
 const TickType_t xDelay = 500 / portTICK_PERIOD_MS;

     for( ;; )
     {
         vTaskDelay( xDelay );
     }
}

 void vTaskAbortDelayFunction( void * pvParameters )
 {
     for( ;; )
     {
         xTaskAbortDelay( xTaskGetHandle( "vTaskFunction" ));
     }
}

uxTaskPriorityGet 查询任务的优先级

必须将 INCLUDE_uxTaskPriorityGet 定义为 1,此函数才可用
#define INCLUDE_uxTaskPriorityGet 0

UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask );

参数:
xTask 	待查询的任务句柄。传递 NULL 句柄会导致返回调用任务的优先级。

Returns:	XTask 的优先级。
用法示例:

 void vAFunction( void )
 {
 	TaskHandle_t xHandle;

     // Create a task, storing the handle.
     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );

     // ...

     // Use the handle to obtain the priority of the created task.
     // It was created with tskIDLE_PRIORITY, but may have changed
     // it itself.
     if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY )
     {
         // The task has changed its priority.
     }

     // ...

     // Is our priority higher than the created task?
     if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) )
     {
         // Our priority (obtained using NULL handle) is higher.
     }
 }

vTaskPrioritySet 设置任务优先级

INCLUDE_vTaskPrioritySet 必须定义为 1 才能使用此函数
#define INCLUDE_vTaskPrioritySet  1

void vTaskPrioritySet( TaskHandle_t xTask,
                       UBaseType_t uxNewPriority );
参数:
xTask 	正在设置优先级的任务的句柄。空句柄会设置调用任务的优先级。
uxNewPriority 	将要设置任务的优先级。

用法示例:

 void vAFunction( void )
 {
 TaskHandle_t xHandle;
     // Create a task, storing the handle.
     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
     // ...
     // Use the handle to raise the priority of the created task.
     vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 )
     // ...
     // Use a NULL handle to raise our priority to the same value.
     vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
 }                       

vTaskSuspend 挂起任意任务

必须将 INCLUDE_vTaskSuspend 定义为 1 才能使用此函数
#define  INCLUDE_vTaskSuspend  1

void vTaskSuspend( TaskHandle_t xTaskToSuspend );
参数:
xTaskToSuspend 	被挂起的任务句柄。传递空句柄将导致调用任务被暂停。
用法示例:

 void vAFunction( void )
 {
 TaskHandle_t xHandle;

     // Create a task, storing the handle.
     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );

     // ...

     // Use the handle to suspend the created task.
     vTaskSuspend( xHandle );

     // ...

     // The created task will not run during this period, unless
     // another task calls vTaskResume( xHandle ).

     //...

     // Suspend ourselves.
     vTaskSuspend( NULL );

     // We cannot get here unless another task calls vTaskResume
     // with our handle as the parameter.
 }

vTaskResume 解除任务挂起 (任务中调用)

必须将 INCLUDE_vTaskSuspend 定义为 1 才能使用此函数
#define  INCLUDE_vTaskSuspend 1

void vTaskResume( TaskHandle_t xTaskToResume );

参数:
xTaskToResume 	要恢复的任务句柄。
用法示例:

 void vAFunction( void )
 {
 TaskHandle_t xHandle;

     // Create a task, storing the handle.
     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );

     // ...

     // Use the handle to suspend the created task.
     vTaskSuspend( xHandle );

     // ...

     // The created task will not run during this period, unless
     // another task calls vTaskResume( xHandle ).

     //...

     // Resume the suspended task ourselves.
     vTaskResume( xHandle );

     // The created task will once again get microcontroller processing
     // time in accordance with its priority within the system.
 }

xTaskResumeFromISR 解除任务挂起 (中断中调用)

必须将 include_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 定义为 1 才能使用此函数
#define  INCLUDE_vTaskSuspend 1
#define  INCLUDE_xTaskResumeFromISR 1

BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume );

参数:
xTaskToResume 	要恢复的任务句柄。
Returns:
如果恢复任务导致上下文切换,则返回 pdTRUE,否则返回 pdFALSE。 ISR 使用此信息来确定 ISR 之后是否需要上下文切换。
示例用法:

 TaskHandle_t xHandle;

 void vAFunction( void )
 {
     // Create a task, storing the handle.
     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );

     // ... Rest of code.
 }

 void vTaskCode( void *pvParameters )
 {
     // The task being suspended and resumed.
     for( ;; )
     {
         // ... Perform some function here.

         // The task suspends itself.
         vTaskSuspend( NULL );

         // The task is now suspended, so will not reach here until the ISR resumes it.
     }
 }


 void vAnExampleISR( void )
 {
     BaseType_t xYieldRequired;

     // Resume the suspended task.
     xYieldRequired = xTaskResumeFromISR( xHandle );

     // We should switch context so the ISR returns to a different task.
     // NOTE:  How this is done depends on the port you are using.  Check
     // the documentation and examples for your port.
     portYIELD_FROM_ISR( xYieldRequired );
 }

vTaskDelete 删除任务

INCLUDE_vTaskDelete 必须定义为 1 才能使用此函数
#define  INCLUDE_vTaskDelete 1

void vTaskDelete( TaskHandle_t xTask );

参数:
xTask 	待删除的任务的句柄。传递 NULL 将导致调用任务被删除。
用法示例:

 void vOtherFunction( void )
 {
 TaskHandle_t xHandle = NULL;

     // Create the task, storing the handle.
     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );

     // Use the handle to delete the task.
     if( xHandle != NULL )
     {
         vTaskDelete( xHandle );
     }
 }

常用任务状态查询 API 函数

vTaskGetInfo 获取单个任务信息

vTaskGetInfo() 为单个任务填充 TaskStatus_t 结构体。 TaskStatus_t 结构体包含 任务句柄的成员、任务名称、任务优先级、任务状态、 以及任务消耗的总运行时间等

configUSE_TRACE_FACILITY 必须在 FreeRTOSConfig.h 中定义为 1vTaskGetInfo() 才可用
#define configUSE_TRACE_FACILITY 1

void vTaskGetInfo( TaskHandle_t xTask,
                   TaskStatus_t *pxTaskStatus,
                   BaseType_t xGetFreeStackSpace,
                   eTaskState eState );

参数:
xTask  	正在查询的任务的句柄。 将 xTask 设置为 NULL 将返回有关调用任务的信息。
pxTaskStatus  	由 pxTaskStatus 指向的 TaskStatus_t 结构体将 填充有关 xTask 参数中传递的句柄所引用的任务的信息。
xGetFreeStackSpace  	TaskStatus_t 结构体包含 用于报告被查询任务的堆栈高水位线的成员。 堆栈 高水位线是指堆栈空间历史剩余最小值, 该值越接近于零, 说明任务越接近堆栈溢出。 计算堆栈高水位线需要的时间相对较长,并且可能导致系统 暂时无响应,因此提供了 xGetFreeStackSpace 参数, 以允许跳过高水位线检查。 如果 xGetFreeStackSpace 未设置为 pdFALSE,高水位线值将写入 TaskStatus_t 结构体。
eState  	TaskStatus_t 结构体包含 用于报告被查询任务的状态的成员。 获取任务状态并不像 简单的赋值那么快,因此提供了 eState 参数,以允许 TaskStatus_t 结构体省略状态信息。 要获取状态信息, 请将 eState 设置为 eInvalid,否则 eState 中传递的值将被报告为 TaskStatus_t 结构体中的任务状态。
用法示例:
void vAFunction( void )
{
	TaskHandle_t xHandle;
	TaskStatus_t xTaskDetails;

    /* Obtain the handle of a task from its name. */
    xHandle = xTaskGetHandle( "Task_Name" );

    /* Check the handle is not NULL. */
    configASSERT( xHandle );

    /* Use the handle to obtain further information about the task. */
    vTaskGetInfo( /* The handle of the task being queried. */
                  xHandle,
                  /* The TaskStatus_t structure to complete with information
                  on xTask. */
                  &xTaskDetails,
                  /* Include the stack high water mark value in the
                  TaskStatus_t structure. */
                  pdTRUE,
                  /* Include the task state in the TaskStatus_t structure. */
                  eInvalid );
}

TaskStatus_t 定义
typedef struct xTASK_STATUS
{
   /* 任务句柄. */
   TaskHandle_t xHandle;

   /* 任务名! */
   const signed char *pcTaskName;

   /* 任务唯一编码. */
   UBaseType_t xTaskNumber;

   /* 填充结构体时任务所处的状态. */
   eTaskState eCurrentState;

   /* 任务运行时的优先级. */
   UBaseType_t uxCurrentPriority;

   /* 如果任务是当前优先级,任务将返回的优先级已继承,以避免无界优先级反转时获得互斥锁. 
    Only valid if configUSE_MUTEXES is defined as 1 in
   FreeRTOSConfig.h. */
   UBaseType_t uxBasePriority;

   /* 到目前为止分配给任务的总运行时间,由运行定义
	时间统计时钟。仅当configGENERATE_RUN_TIME_STATS为时有效
	在FreeRTOSConfig.h中定义为1. */
   unsigned long ulRunTimeCounter;

   /* 指向任务堆栈区域的最低地址. */
   StackType_t *pxStackBase;

   /* 为该任务保留的最小堆栈空间
	完成任务的创建。这个值越接近零,任务就越接近
	已经溢出了它的堆栈. */
   configSTACK_DEPTH_TYPE usStackHighWaterMark;
} TaskStatus_t;

xTaskGetCurrentTaskHandle 查询当前正在运行(调用)任务的句柄

INCLUDE_xTaskGetCurrentTaskHandle 必须设置为 1 才能使用此函数。
#define INCLUDE_xTaskGetCurrentTaskHandle 1

TaskHandle_t xTaskGetCurrentTaskHandle( void );

xTaskGetHandle 通过任务名查询任务句柄

将 INCLUDE_xTaskGetHandle 设置为 1xTaskGetHandle() 才可用
#define INCLUDE_xTaskGetHandle 1

TaskHandle_t xTaskGetHandle( const char *pcNameToQuery );

参数:
pCNAMEToQuery  	任务的文本名称 

Returns:
如果可以找到在 pcNameToQuery 中传递名称的任务,则将返回任务句柄,否则返回 NULL

pcTaskGetName 通过任务句柄查询任务名

char * pcTaskGetName( TaskHandle_t xTaskToQuery );

参数:
xTaskToQuery 	查询的任务的句柄。 xTaskToQuery 可以设置为 NULL,以查询调用任务的名称。
Returns:
				指向主题任务名称的字符串指针

xTaskGetIdleTaskHandle 查询空闲任务的句柄

INCLUDE_xTaskGetIdleTaskHandle 必须设置为 1 才能使用此函数。
#define INCLUDE_xTaskGetIdleTaskHandle 1

TaskHandle_t xTaskGetIdleTaskHandle( void );

uxTaskGetStackHighWaterMark 查询任务剩余堆栈空间

INCLUDE_uxTaskGetStackHighWaterMark 定义为 1,uxTaskGetStackHighWaterMark 函数才可用
#define INCLUDE_uxTaskGetStackHighWaterMark 1

UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );

参数:
xTask 	正在查询的任务的句柄。 任务可以通过传递 NULL 作为 xTask 参数来查询自己的高水位标记。
Returns:
返回的值是以字为单位的高水位标记(例如,在 32 位计算机上,返回值为 1 表示有 4 个字节的堆栈未使用)。
如果返回值为零,则任务可能已溢出堆栈。 如果返回值接近于零,则任务已接近堆栈溢出。
示例用法:

    void vTask1( void * pvParameters )
    {
    UBaseType_t uxHighWaterMark;

        /* Inspect our own high water mark on entering the task. */
        uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );

        for( ;; )
        {
            /* Call any function. */
            vTaskDelay( 1000 );

            /* Calling the function will have used some stack space, we would 
            therefore now expect uxTaskGetStackHighWaterMark() to return a 
            value lower than when it was called on entering the task. */
            uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );
        }
    }

eTaskGetState 查询任务状态

INCLUDE_eTaskGetState 设置为 1eTaskGetState() 才可用
#define INCLUDE_eTaskGetState 1

eTaskState eTaskGetState( TaskHandle_t xTask );

下表列出了 eTaskGetState() 为 xTask 参数引用的任务可能存在的每个潜在状态返回的值

返回值
准备就绪	eReady
运行	eRunning (调用任务正在查询自己的优先级)
已阻塞	eBlocked
已挂起	eSuspended
已删除	eDeleted (任务 TCB 正在等待清理)

xTaskGetTickCount 查询Tick计数值(任务中使用)

volatile TickType_t xTaskGetTickCount( void );

Returns:
自 vTaskStartScheduler 被调用起的 tick 数。

xTaskGetTickCountFromISR 查询Tick计数值 (中断中使用)

volatile TickType_t xTaskGetTickCountFromISR( void );

Returns:
自 vTaskStartScheduler 被调用起的 tick 数。

xTaskGetSchedulerState 查询任务调度器状态

将 INCLUDE_xTaskGetSchedulerState 或 configUSE_TIMERS 设置为 1 才能使用此函数
#define INCLUDE_xTaskGetSchedulerState 1

BaseType_t xTaskGetSchedulerState( void );

Returns:
以下常量之一(在 task.h 中定义):taskSCHEDULER_NOT_STARTED、taskSCHEDULER_RUNNING、taskSCHEDULER_SUSPENDED。

uxTaskGetNumberOfTasks 查询任务数量

UBaseType_t uxTaskGetNumberOfTasks( void );

Returns:
RTOS 内核当前正在管理的任务数。这包括所有准备就绪、阻塞和挂起的任务。已删除但尚未被闲置任务释放的任务也将包含在计数中。

vTaskList 查询每个任务的信息状态

显示每个任务的状态,包括任务的堆栈高水位线(高水位线数字越小, 任务越接近于溢出其堆栈)。

configUSE_TRACE_FACILITY 和 configUSE_STATS_FORMATTING_FUNCTIONS 必须在 FreeRTOSConfig.h 中定位为 1 ,才可使用此函数
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1

void vTaskList( char *pcWriteBuffer );

参数:
pcWriteBuffer  	一个缓冲区, 上面提到的细节将以 ASCII 形式写入其中。假设此缓冲区 的大小足以容纳产生的报告。 
				大约为每个任务分配 40 字节的缓冲区就足够了

示例用法:

char pcWriteBuffer[40 * Task_Number] = {0};

void vTask1( void * pvParameters )
{

    for( ;; )
    {
        /* Call any function. */
        vTaskDelay( 1000 );
        vTaskList( pcWriteBuffer );
        printf(%s,pcWriteBuffer);
    }
}

vTaskGetRunTimeStats 查询每个任务运行时间统计

需要三个宏。 这些宏可以在 FreeRTOSConfig.h 中定义。
configGENERATE_RUN_TIME_STATS
通过将 configGENERATE_RUN_TIME_STATS 定义为 1,启用收集运行时统计信息。 一旦完成此设置,还必须定义另外两个宏,以确保能成功 编译。

portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
运行时统计信息的时间基数需要比 tick 中断具有更高的分辨率—— 否则统计信息可能会不准确,无法真正发挥作用。 建议将 此时间基数设置为比 tick 中断快 10 到 100 倍。 时间基数越快, 统计数据就越准确——但定时器值也会越早溢出。

如果将 configGENERATE_RUN_TIME_STATS 定义为 1,那么 RTOS 内核会 在启动时自动调用 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() (从 vTaskStartScheduler() API 中进行调用)。 应用程序设计人员希望 使用宏来配置合适的时间基数。 下面举例说明。

portGET_RUN_TIME_COUNTER_VALUE()
此宏应只返回当前的时间,正如 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 所配置的那样。 下面提供了一些示例。

configGENERATE_RUN_TIME_STATS、
configUSE_STATS_FORMATTING_FUNCTIONS 和 configSUPPORT_DYNAMIC_ALLOCATION 必须被定义为 1,才可使用此函数

用法示例
#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1

char pcWriteBuffer[40 * Task_Number] = {0};
volatile unsigned long ulHighFrequencyTimerTicks;
TIM_HandleTypeDef        htim1;

/*启动任务调度器会调用这个宏,把ulHighFrequencyTimerTicks 清零*/
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ( ulHighFrequencyTimerTicks = 0UL )
/*此宏用于获取ulHighFrequencyTimerTicks 的计数值*/
#define portGET_RUN_TIME_COUNTER_VALUE()	ulHighFrequencyTimerTicks

/*函数声明*/
void vTaskGetRunTimeStats( char *pcWriteBuffer );
void Time_Updata_Interrupt(void);
void vTask1( void * pvParameters )

int main (void)
{
	Time_Updata_Interrupt();//初始化并开启定时器
	...创建任务
	vTaskStartScheduler();//开启任务调度器
}
void vTask1( void * pvParameters )
{

    for( ;; )
    {
    	vTaskDelay( 1000 );
		vTaskGetRunTimeStats( pcWriteBuffer );
		printf(%s,pcWriteBuffer);
    }
}
void Time_Updata_Interrupt(void)
{
	/* Enable TIM1 clock */
	__HAL_RCC_TIM1_CLK_ENABLE();	
  /* Initialize TIM1 */
  htim1.Instance = TIM1;
  htim1.Init.Period = (10000000U / 1000U) - 1U;//100us中断一次,Tick的10倍
  htim1.Init.Prescaler = uwPrescalerValue;
  htim1.Init.ClockDivision = 0;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

  HAL_TIM_Base_Init(&htim1);
  HAL_TIM_Base_Start_IT(&htim1);
  /*使能定时器更新中断*/
  HAL_NVIC_SetPriority(TIM1_UP_IRQn, 5, 0U);
  HAL_NVIC_EnableIRQ(TIM1_UP_IRQn);
}
/*中断更新回调函数*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if (htim->Instance == TIM1) 
  {
    ulHighFrequencyTimerTicks ++ ;
  }
}

vTaskGetIdleRunTimeCounter 查询空闲任务运行时间

返回空闲任务的运行时间计数器。 此函数可用于 确定空闲任务获得了多少 CPU 时间
必须提供 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
和 portGET_RUN_TIME_COUNTER_VALUE 的定义
和上面用法一至

configGENERATE_RUN_TIME_STATS 和 INCLUDE_xTaskGetIdleTaskHandle 必须 定义为 1,才可使用此函数
#define configGENERATE_RUN_TIME_STATS 1
#define INCLUDE_xTaskGetIdleTaskHandle 1

TickType_t xTaskGetIdleRunTimeCounter( void );
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值