十三、FreeRTOS之FreeRTOS时间管理

本节主要介绍以下内容:

1,延时函数介绍(了解)

2,延时函数解析(熟悉)

3,延时函数演示实验(掌握)

4,课堂总结(掌握)

 一、延时函数介绍(了解)      

函数

描述

vTaskDelay()

相对延时

xTaskDelayUntil()

绝对延时

相对延时:指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束 

举个例子:从vTaskDelay()的前一句到vTaskDelay()执行结束,延迟100ms。

绝对延时:指将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务

举个例子:任务从开始执行到结束共100ms。

可以看下面这张图,xTimeIncrement就是绝对延时的时间,这三部分加起来就是绝对延时时间。

这个函数在官网里面也有说明:必须任务主体小于延迟时间

 (1)为任务主体,也就是任务真正要做的工作

(2)是任务函数中调用vTaskDelayUntil()对任务进行延时

(3)为其他任务在运行

1.1 vTaskDelay()

参数:

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 );
     }
}

 1.2 xTaskDelayUntil()

参数

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.
     }
 }
   

,延时函数解析(熟悉)

又开始了我们对源码进行解读的时间了,大家有没有觉得解读源码是一件很有成就感的事情呢?

2.1  void vTaskDelay( const TickType_t xTicksToDelay )

#if ( INCLUDE_vTaskDelay == 1 )

    void vTaskDelay( const TickType_t xTicksToDelay )
    {
        /* 只有在延时时间大于0的时候, 
         * 才需要进行任务阻塞, 
         * 否则相当于强制进行任务切换,而不阻塞任务 */
        BaseType_t xAlreadyYielded = pdFALSE;

        /* A delay time of zero just forces a reschedule. */
        if( xTicksToDelay > ( TickType_t ) 0U )
        {
            configASSERT( uxSchedulerSuspended == 0 );
            /* 挂起任务调度器 */
            vTaskSuspendAll();
            {
                /* 用于调试,不用理会 */
                traceTASK_DELAY();

                /* A task that is removed from the event list while the
                 * scheduler is suspended will not get placed in the ready
                 * list or removed from the blocked list until the scheduler
                 * is resumed.
                 *
                 * This task cannot be in an event list as it is the currently
                 * executing task. */
                /* 将任务添加到阻塞态任务列表中 */
                prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
            }
            /* 恢复任务调度器运行, 
             * 调用此函数会返回是否需要进行任务切换 */
            xAlreadyYielded = xTaskResumeAll();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        /* Force a reschedule if xTaskResumeAll has not already done so, we may
         * have put ourselves to sleep. */
        /* 根据标志进行任务切换 */
        if( xAlreadyYielded == pdFALSE )
        {
            portYIELD_WITHIN_API();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }

#endif /* INCLUDE_vTaskDelay */
/*-----------------------------------------------------------*/

注意:

1. 使用函数 vTaskDelay()进行任务延时时,被延时的任务为调用该函数的任务,及调用该
函数时,系统中正在运行的任务,此函数无法指定将其他任务进行任务延时。

2. 函数 vTaskDelay()传入的参数 xTicksToDelay是任务被延时的具体延时时间,时间的单
位为系统时钟节拍,这里要特别注意,很多 FreeRTOS的初学者可能会一会 此函数延时的时间
单位为微妙、毫秒、秒等物理时间单位,
FreeRTOS是以系统时钟节拍作为计量的时间单
位的 ,而系统时钟节拍对应的物理时间长短于 FreeRTOSConfig.h文件中的配置项ConfigTICK_RATE_HZ有关,配置项 configTICK_RATE_HZ是用于配置系统时钟节拍的频率的,

3. 在使用此函数进行任务延时时,如果传入的参数为 0,那表明不进行任务延时,而是强
制进行一次任务切换。

2.2 vTaskDelayUntil( pxPreviousWakeTime, xTimeIncrement ) 

函数vTaskDelayUntil()用于以一个绝对的时间阻塞任务,适用于需要按照一定频率运行的任务,函数 vTaskDelayUntil()实际上是一个宏,在 task.h文件中有定义,具体的代码如下所示:

/*
 * vTaskDelayUntil() is the older version of xTaskDelayUntil() and does not
 * return a value.
 */
#define vTaskDelayUntil( pxPreviousWakeTime, xTimeIncrement )           \
    {                                                                   \
        ( void ) xTaskDelayUntil( pxPreviousWakeTime, xTimeIncrement ); \
    }

 从上面的代码可以看出,宏vTaskDelayUntil()实际上就是函数 xTaskDelayUntil(),函数
xTaskDelayUntil()在 task.c文件中有定义,具体的代码如下所示:

#if ( INCLUDE_xTaskDelayUntil == 1 )

    BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
                                            /* 上一次阻塞超时时间*/
                                const TickType_t xTimeIncrement 
                                            /* 延时的时间 */)
    {
        TickType_t xTimeToWake;
        BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;

        configASSERT( pxPreviousWakeTime );
        configASSERT( ( xTimeIncrement > 0U ) );
        configASSERT( uxSchedulerSuspended == 0 );
        
        /* 挂起任务调度器 */
        vTaskSuspendAll();
        {
            /* Minor optimisation.  The tick count cannot change in this
             * block. */
            const TickType_t xConstTickCount = xTickCount;

            /* 计算任务下一次阻塞超时的时间, 
             * 这个阻塞超时时间是相对于上一次阻塞超时的时间的 
             */
            xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;

            if( xConstTickCount < *pxPreviousWakeTime )
            {
                /* 只有在下一次阻塞超时时间也溢出,
                 * 并且下一次阻塞超时时间大于系统时钟节拍计数器的值时, 
                 * 需要做相应的溢出处理,否则就好像没有溢出 
                 */
                if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )
                {
                    /* 标记因为溢出,需要做相应的处理 */
                    xShouldDelay = pdTRUE;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                /* 系统时钟节拍计数器没有溢出, 
                 * 但是下一次阻塞超时时间溢出了,
                 * 并且下一次阻塞超时时间大于系统时钟节拍计数器的值时,
                 * 需要做相应的溢出处理 
                 */

                if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )
                {
                    xShouldDelay = pdTRUE;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }

            /* 更新上一次阻塞超时时间为下一次阻塞超时时间 */
            *pxPreviousWakeTime = xTimeToWake;
            /* 根据标记,做相应的溢出处理 */
            if( xShouldDelay != pdFALSE )
            {
                /* 用于调试,不用理会 */
                traceTASK_DELAY_UNTIL( xTimeToWake );

                /* 将任务添加到阻塞态任务列表中 */
                prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        /* 恢复任务调度器运行, 
         * 调用此函数会返回是否需要进行任务切换 
         */
        xAlreadyYielded = xTaskResumeAll();

        /* 根据标志进行任务切换 */
        if( xAlreadyYielded == pdFALSE )
        {
            portYIELD_WITHIN_API();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        return xShouldDelay;
    }

#endif /* INCLUDE_xTaskDelayUntil */
/*-----------------------------------------------------------*/

从上面的代码可以看出,函数xTaskDelayUntil()对任务进行延时的操作,是相对于任务上
一次阻塞超时的时间,而不是相对于系统当前的时钟节拍计数器的值,因此,函数能够更准确
地以一定的频率进行任务延时,更加适用于需要按照一定频率运行的任务。 

三,延时函数演示实验(掌握)

2.1、实验目的:

学习 FreeRTOS 相对延时和绝对延时 API 函数的使用

2.2、实验设计:

将设计三个任务:start_task、task1、task2

三个任务的功能如下:

  • start_task:用来创建task1和task2任务
  • task1:用于展示相对延时函数vTaskDelay ( )的使用
  • task2:用于展示绝对延时函数vTaskDelayUntil( )的使用

为了直观显示两个延时函数的区别,将使用LED0(PB1) LED1(PB0) 的翻转波形来表示

2.3 实验代码

demo.c

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         5
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );


/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );
/******************************************************************************************************/

/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,演示相对延时函数 */
void task1( void * pvParameters )
{
    while(1)
    {
        LED0_TOGGLE();      /* PB1 */
        delay_ms(20);
        vTaskDelay(500);
    }
}

/* 任务二,演示绝对延时函数 */
void task2( void * pvParameters )
{
    TickType_t xLastWakeTime;
    xLastWakeTime = xTaskGetTickCount();/*获取当前系统节拍计数值*/
    while(1)
    {
        LED1_TOGGLE();      /* PB0 */
        delay_ms(20);
        vTaskDelayUntil(&xLastWakeTime,500);
    }
}

四,总结

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I am Supreme

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值