码迷,mamicode.com
首页 > 其他好文 > 详细

OSAL之时间管理,软件定时器链表管理

时间:2015-05-18 13:01:58      阅读:1932      评论:0      收藏:0      [点我收藏+]

标签:定时器   链表   蓝牙   软件   

读源码写作,尊重原创;

本博文根据蓝牙4.0, 协议族版本是1.3.2


OSAL的时钟实现在OSAL_CockBLE.c与OSAL_Clock.h两个文件中。OSAL支持完整的UTC(世界统一时间),以2000年1月1日00:00:00为时间起点,可以精确到年、月、日、时、分、秒的时间值。

背景知识

// number of seconds since 0 hrs, 0 minutes, 0 seconds, on the 1st of January 2000 UTC存储自2000年1月1日开始的**秒数**,uint32可存储大约135年的数据,就是到2135年左右。
typedef uint32 UTCTime;
UTCTime OSAL_timeSeconds = 0;//定义一个存储秒数的变量

// To be used with
typedef struct
{
  uint8 seconds;  // 0-59
  uint8 minutes;  // 0-59
  uint8 hour;     // 0-23
  uint8 day;      // 0-30
  uint8 month;    // 0-11
  uint16 year;    // 2000+
} UTCTimeStruct;//这是将UTCTime转化为UTCTimeStruct的结构

// (MAXCALCTICKS * 5) + (max remainder) must be <= (uint16 max),
// so: (13105 * 5) + 7 <= 65535这是防止微妙转化为毫秒是uint16溢出
#define MAXCALCTICKS  ((uint16)(13105))

#define BEGYEAR         2000     // UTC started at 00:00:00 January 1, 2000

#define DAY             86400UL  // 24 hours * 60 minutes * 60 seconds
//全局变量
static uint16 previousLLTimerTick = 0;   //存储的是上次调用osalTimeUpdate获得的625us反转次数
static uint16 remUsTicks = 0;   //存储的是us转ms  *5/8的余数
static uint16 timeMSec = 0;     //存储的是ms转s   的余数

OSAL里面所有时间的根源都来自于硬件层一个定时器的更新,它是625us自动反转重新计时的定时器,只要计算前后两次获得它反转的次数想减那么就可以计算出之间花费了多少时间。
这个定时器由osalTimeUpdata调用。

void osalTimeUpdate( void )
{
  uint16 tmp;  //记录这次获得625us逝去的次数
  uint16 ticks625us;   //记录前后两次625us逝去的次数
  uint16 elapsedMSec = 0;  //记录前后两次625us逝去的毫秒数

  // Get the free-running count of 625us timer ticks
 tmp = ll_McuPrecisionCount();   //计算当前过去了多少个625us次数

  if ( tmp != previousLLTimerTick )
  {
    // Calculate the elapsed ticks of the free-running timer.
    ticks625us = tmp - previousLLTimerTick;

    // Store the LL Timer tick count for the next time through this function.存储这次过去的625us的次数
    previousLLTimerTick = tmp;

    /* It is necessary to loop to convert the usecs to msecs in increments so as
     * not to overflow the 16-bit variables.
     */
    while ( ticks625us > MAXCALCTICKS ) //主要为了数据过大转换为ms是溢出了。MAXCALCTICKS >13105就会溢出从零开始,对时间来说是不行的
    {
      ticks625us -= MAXCALCTICKS;
      elapsedMSec += MAXCALCTICKS * 5 / 8;  //计算逝去的毫秒;整除,MAXCALCTICKS =13105个节拍转换为ms是8190ms
      remUsTicks += MAXCALCTICKS * 5 % 8;   //计算当前循环的余数,余数为5,时间0.625ms;5除8 =0.625
    }
    }

    // update converted number with remaining ticks from loop and the
    // accumulated remainder from loop把余数加进去,组合成剩余的滴答数*5;这里限制了必须使得((ticks625us *5) + 7 < = 65535)
    tmp = (ticks625us * 5) + remUsTicks;

    // Convert the 625 us ticks into milliseconds and a remainder
    elapsedMSec += tmp / 8;   //将剩余的滴答数转化成ms
   remUsTicks = tmp % 8;      //将余数留到下次使用

    // Update OSAL Clock and Timers更新时钟和软件定时器
    if ( elapsedMSec )//
    {
      osalClockUpdate( elapsedMSec );//更新整个系统的时间,UTCTime以秒为单位记录  
      osalTimerUpdate( elapsedMSec );//传入过去流逝的时间值,进一步更新每个软件定时器,减去一定的时间  
    }
  }
}

个人觉得上面对MAXCALCTICKS处理有点问题,详情可以看下面OSAL时钟优化

osalClockUpdate( elapsedMSec );

用于更新系统时钟事件,保存到一个全局变量中。参数是毫秒数据,

static void osalClockUpdate( uint16 elapsedMSec )
{
  // Add elapsed milliseconds to the saved millisecond portion of time
  timeMSec += elapsedMSec;

  // Roll up milliseconds to the number of seconds
  if ( timeMSec >= 1000 )
  {
    OSAL_timeSeconds += timeMSec / 1000;
    timeMSec = timeMSec % 1000;   //存储ms转s余数
  }
}

osalTimerUpdate( elapsedMSec );

函数功能是用过去的毫秒值去更新软件定时器的值,减去时间;

软件定时器相关的背景知识

typedef union {
  uint32 time32;
  uint16 time16[2];
  uint8 time8[4];
} osalTime_t;

typedef struct
{
  void   *next;
  osalTime_t timeout;
  uint16 event_flag;
  uint8  task_id;
  uint32 reloadTimeout;
} osalTimerRec_t;
//上面是一个软件定时器的结构体

osalTimerRec_t *timerHead;  //定义一个头指针,指向软件定时器链表

例程讲解

void osalTimerUpdate( uint32 updateTime )
{
  halIntState_t intState;
  osalTimerRec_t *srchTimer;  //遍历软件定时器链表
  osalTimerRec_t *prevTimer;

  osalTime_t timeUnion;
  timeUnion.time32 = updateTime;//共用体,

  HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.
  // Update the system time
  osal_systemClock += updateTime;
  HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.

  // Look for open timer slot
  if ( timerHead != NULL )
  {
    // Add it to the end of the timer list
    srchTimer = timerHead;
    prevTimer = (void *)NULL;

    // Look for open timer slot
    while ( srchTimer )
    {
      osalTimerRec_t *freeTimer = NULL;

      HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.

      // To minimize time in this critical section, avoid 32-bit math为了避免32位匹配,计算最小时间
      if ((timeUnion.time16[1] == 0) && (timeUnion.time8[1] == 0))
      {
        // If upper 24 bits are zero, check lower 8 bits for roll over逝去的时间只有 1 byte
        if (srchTimer->timeout.time8[0] >= timeUnion.time8[0])
        {
          // 8-bit math减去逝去的时间
          srchTimer->timeout.time8[0] -= timeUnion.time8[0];
        }
        else
        {
          // 32-bit math
          if (srchTimer->timeout.time32 > timeUnion.time32)
          {
            srchTimer->timeout.time32 -= timeUnion.time32;
          }
          else
          {
            srchTimer->timeout.time32 = 0;
          }
        }
      }
      else
      {
          // 32-bit math
        if (srchTimer->timeout.time32 > timeUnion.time32)
        {
          srchTimer->timeout.time32 -= timeUnion.time32;//减去逝去的时间
        }
        else
        {
          srchTimer->timeout.time32 = 0;//定时时间到
        }
      }

      // Check for reloading对于重载的软件定时器处理
      if ( (srchTimer->timeout.time16[0] == 0) && (srchTimer->timeout.time16[1] == 0) &&
           (srchTimer->reloadTimeout) && (srchTimer->event_flag) )
      {
        // Notify the task of a timeout时间到,设定事件
        osal_set_event( srchTimer->task_id, srchTimer->event_flag );

        // Reload the timer timeout value重新设定时间
        srchTimer->timeout.time32 = srchTimer->reloadTimeout;
      }

      // When timeout or delete (event_flag == 0)对于只定时一次的软件定时器处理和要删除的软件定时器处理
      if ( ((srchTimer->timeout.time16[0] == 0) && (srchTimer->timeout.time16[1] == 0)) ||
            (srchTimer->event_flag == 0) )
      {
        // Take out of list
        if ( prevTimer == NULL )
        {
          timerHead = srchTimer->next;
        }
        else
        {
          prevTimer->next = srchTimer->next;
        }

        // Setup to free memory
        freeTimer = srchTimer;//记录当前待释放的定时器

        // Next
        srchTimer = srchTimer->next;
      }
      else
      {
        // Get next如果没有软件定时器到时,则遍历下一个软件定时器
        prevTimer = srchTimer;
        srchTimer = srchTimer->next;
      }

      HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.

      if ( freeTimer )
      {
        if ( (freeTimer->timeout.time16[0] == 0) && (freeTimer->timeout.time16[1] == 0) )
        {
          osal_set_event( freeTimer->task_id, freeTimer->event_flag );
        }
        osal_mem_free( freeTimer );//释放软件定时器
      }
    }
  }
}

上面这个函数是对软件定时器链表的所有定时器时间进行更新。

OSAL维护着一个定时器全局链表timerHead,每当调用osal_start_timerEx或者osal_start_reload_timer实际再调用osalAddTimer,来创建一个新的软件Timer。

osalTimerRec_t * osalAddTimer( uint8 task_id, uint16 event_flag, uint32 timeout )
{
  osalTimerRec_t *newTimer;
  osalTimerRec_t *srchTimer;

  // Look for an existing timer first
  newTimer = osalFindTimer( task_id, event_flag );查找是否已存在这个任务对应事件的的定时器;
  if ( newTimer )
  {
    // Timer is found - update it.如果存在更新超时时间
    newTimer->timeout.time32 = timeout;

    return ( newTimer );
  }
  else
  {
    // New Timer若没有,只要创建新的软件定时器
    newTimer = osal_mem_alloc( sizeof( osalTimerRec_t ) );

    if ( newTimer )
    {
      // Fill in new timer
      newTimer->task_id = task_id;
      newTimer->event_flag = event_flag;
      newTimer->timeout.time32 = timeout;
      newTimer->next = (void *)NULL;
      newTimer->reloadTimeout = 0;

      // Does the timer list already exist
      if ( timerHead == NULL )
      {
        // Start task list如果软件定时器为空,那么指向第一个软件定时器
        timerHead = newTimer;
      }
      else
      {
        // Add it to the end of the timer list不然加到线性表末尾
        srchTimer = timerHead;

        // Stop at the last record
        while ( srchTimer->next )
          srchTimer = srchTimer->next;

        // Add to the list
        srchTimer->next = newTimer;
      }

      return ( newTimer );
    }
    else
    {
      return ( (osalTimerRec_t *)NULL );
    }
  }
}

OSAL之时间管理,软件定时器链表管理

标签:定时器   链表   蓝牙   软件   

原文地址:http://blog.csdn.net/jq_ak47/article/details/45815991

(0)
(2)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!