FreeRTOS — 系统时钟节拍和时间管理

以下内容转载自安富莱电子:http://forum.armfly.com/forum.php

1 、FreeRTOS 的 时钟 节拍

  任何操作系统都需要提供一个时钟节拍,以供系统处理诸如延时、超时等与时间相关的事件。

  时钟节拍是特定的周期性中断,这个中断可以看做是系统心跳。中断之间的时间间隔取决于不同的应用,一般是 1ms – 100ms。时钟的节拍中断使得内核可以将任务延迟若干个时钟节拍,以及当任务等待事件发生时,提供等待超时等依据。时钟节拍率越快,系统的额外开销就越大。

  

FreeRTOS 的系统时钟节拍可以在配置文件 FreeRTOSConfig.h 里面设置:
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
如上所示的宏定义配置表示系统时钟节拍是 1KHz,即 1ms。

2 、FreeRTOS 的 时 间 管 理

  时间管理功能是 FreeRTOS 操作系统里面最基本的功能,同时也是必须要掌握好的。

  2.1 时间延迟

  FreeRTOS 中的时间延迟函数主要有以下两个作用:

       

             

上面就是一个简单的任务运行状态的切换过程。

   2.2 FreeRTOS 的时间相关函数

FreeRTOS 时间相关的函数主要有以下 4 个:
 vTaskDelay ()
 vTaskDelayUntil ()
 xTaskGetTickCount()
 xTaskGetTickCountFromISR()
下面我们对这 4 个函数依次进行说明:

   2.3 函数 vTaskDelay

使用举例:

  2.4 函数 vTaskDelayUntil

函数原型:
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, /* 存储任务上次处于非阻塞状态时刻的变量地址 */
          const TickType_t xTimeIncrement ); /* 周期性延迟时间 */


函数描述:
函数 vTaskDelayUntil 用于周期性延迟。
 第 1 个参数,存储任务上次处于非阻塞状态时刻的变量地址。
 第 2 个参数,周期性延迟时间。
使用这个函数要注意以下问题:
1. 使用此函数需要在 FreeRTOSConfig.h 配置文件中配置如下宏定义为 1
#define INCLUDE_vTaskDelayUntil 1

2. 用户要注意此函数跟 vTaskDelay 的区别,2.7 小节详细讲解。

使用举例:

 

2.5 函 数 xTaskGetTickCount

函数原型:
volatile TickType_t xTaskGetTickCount( void );
函数描述:
函数 xTaskGetTickCount 用于获取系统当前运行的时钟节拍数。
使用这个函数要注意以下问题:
1. 此函数用于在任务代码里面调用,如果在中断服务程序里面调用的话,需要使用函数xTaskGetTickCountFromISR,这两个函数切不可混用

使用举例:

  2.6 函 数 xTaskGetTickCountFromISR

函数原型:
volatile TickType_t xTaskGetTickCountFromISR( void );
函数描述:
函数 xTaskGetTickCountFromISR 用于获取系统当前运行的时钟节拍数。
使用这个函数要注意以下问题:
1. 此函数用于在中断服务程序里面调用,如果在任务里面调用的话,需要使用函数 xTaskGetTickCount,
这两个函数切不可混用。

使用举例:

 

/*
*********************************************************************************************************
*  函 数 名: TIM6_IRQHandler
*  功能说明: TIM6 中断服务程序。
*  形 参: 无
*  返 回 值: 无
*********************************************************************************************************
*/
void TIM6_IRQHandler( void )
{
TickType_t xTickCount;
xTickCount = xTaskGetTickCountFromISR();
}

 

  2.7 函数 vTaskDelay 和 vTaskDelayUntil 的区别

函数 vTaskDelayUntil 实现的是周期性延迟,而函数 vTaskDelay 实现的是相对性延迟,反映到实际
应用上有什么区别呢,下面就给大家举一个简单的例子。
运行条件:
 有一个 bsp_KeyScan 函数,这个函数处理时间大概耗时 2ms。
 有两个任务,一个任务 Task1 是用的 vTaskDelay 延迟,延迟 10ms,另一个任务 Task2 是用的
vTaskDelayUntil 延迟,延迟 10ms。
 不考虑任务被抢占而造成的影响。
实际运行过程效果:
 Task1:
bsp_KeyScan+ vTaskDelay (10) ---> bsp_KeyScan + vTaskDelay (10)
|----2ms + 10ms 为一个周期------| |----2ms + 10ms 为一个周期----|
这个就是相对性的含义
 Task2:
bsp_KeyScan + vTaskDelayUntil ---------> bsp_KeyScan + vTaskDelayUntil
|----10ms 为一个周期(2ms 包含在 10ms 内)---| |----10ms 为一个周期------|
这就是周期性的含义。
下面我们通过函数 vTaskDelay 来实现 vTaskDelayUntil,会有一个更加全面的认识:

/*
*********************************************************************************************************
*  函 数 名: vTaskMsgPro
*  功能说明: 消息处理,这里是用作 LED 闪烁 
*  形 参: pvParameters 是在创建该任务时传递的形参
*  返 回 值: 无
* 优 先 级: 3
*********************************************************************************************************
*/
static void vTaskMsgPro(void *pvParameters)
{
  TickType_t xDelay, xNextTime;
  const TickType_t xFrequency = 200;
  /* 获取 xFrequency 个时钟节拍后的时间 */
  xNextTime = xTaskGetTickCount() + xFrequency;
  while(1)
  {
    bsp_LedToggle(3);
    /* 用 vTaskDelay 实现 vTaskDelayUntil() */
    xDelay = xNextTime - xTaskGetTickCount();
    xNextTime += xFrequency;
    if(xDelay <= xFrequency)
    {
      vTaskDelay(xDelay);
    }
  }
}

3、实验 例程

实验目的:
1. 学习 FreeRTOS 的周期性延迟和相对性延迟函数。
2. 注意相对性延迟函数 vTaskDelay 和周期性延迟函数 vTaskDelayUntil 的区别。

实验内容:
1. K1 按键按下,串口打印任务执行情况(波特率 115200,数据位 8,奇偶校验位无,停止位 1)。
2. K2 键按下,串口打印系统时钟节拍数。

 

static void vTaskTaskUserIF(void *pvParameters)
{
    uint8_t ucKeyCode;
    uint8_t pcWriteBuffer[500];

    while(1)
    {
        ucKeyCode = bsp_GetKey();
        
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                /* K1键按下 打印任务执行情况 */
                case KEY_DOWN_K1:             
                    printf("=================================================\r\n");
                    printf("任务名      任务状态 优先级   剩余栈 任务序号\r\n");
                    vTaskList((char *)&pcWriteBuffer);
                    printf("%s\r\n", pcWriteBuffer);
                
                    printf("\r\n任务名       运行计数         使用率\r\n");
                    vTaskGetRunTimeStats((char *)&pcWriteBuffer);
                    printf("%s\r\n", pcWriteBuffer);
                    break;
                
                /* K1键按下 打印系统时钟节拍数 */
                case KEY_DOWN_K2:             
                    printf("当前的系统时钟节拍数 = %d\r\n", xTaskGetTickCount());
                    break;
                
                /* 其他的键值不处理 */
                default:                     
                    break;
            }
        }
        
        vTaskDelay(20);
    }
}

 

 

 

posted @ 2017-08-01 12:31  Liu_Jing  Views(5189)  Comments(0Edit  收藏  举报