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

读源码写作,尊重原创;

本博文根据蓝牙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 );
    }
  }
}
时间: 2024-07-29 04:15:50

OSAL之时间管理,软件定时器链表管理的相关文章

上网行为管理软件真的对企业管理员工有好处吗?看了案例你就知道

上海**商贸有限公司负责人邓总为了更好的管理员工上班的工作积极性和合理上网行为,安装了上网行为管理软件,通过对员工电脑操作的可视化管理,针对性指导和提升员工工作能力. 邓总知道,自己公司员工存在部分员工上班工作比较懒散,工作马马虎虎完成,为了更好的管理他们,网上了解到给员工安装上网行为管理软件,可以通过查看他们上网行为,进而可以对相应员工进行管理,减少他们上网行为,多花精力在工作上,也是提升员工工作能力的一个重要方式. 但是,在给员工安装上网行为管理软件之前,邓总也有这样的疑惑:我给员工安装管理

客户服务支持管理软件—工单管理

(1)邮件工单管理 根据设定的规则,自动将收到的邮件转为工单. 可对每个业务单元配置发件人名称.ID.邮件模板等. 设置过滤规则,过滤垃圾邮件或不相关邮件,防止其进入服务支持系统. (2)客户沟通管理 多种沟通渠道,如电话.邮件.论坛等. 当请求发起.更新或关闭时,自动发送通知. (3)知识库 轻松构建企业知识库,积累企业知识财富. 为终端用户开放知识库,方便其查找常见问题的解决方案. 查看最常见或最近的解决方案. (4)自动回复 创建默认的自动回复邮件并共享给其它支持代表. 在邮件中添加字段,

机械加工生产管理软件-原材料出入库跟踪-自动汇总报表

信华erp生产管理系统 根据五金.机械行业具有明显的订货量小.产品品种变化多.五金件表面处理颜色多.加工工艺不断变化等特征而设计的生产管理软件.而大部分五金企业的管理现状仍然停留于靠人工管理.经验决策阶段,软件能够使决策更为合理与科学,使生产与销售.仓库.采购.财务能更好的协作,能更有序地按计划进行生产,以提高企业对客户订单的交付能力与客户满意度,使企业能快速应对市场不断变化的需求,需求更快更好的发展.信华erp生产管理软件的标准管理流程:[下生产单-生产审批-物料采购.领用-生产过程跟踪-生产

餐饮管理软件代理商之痛

导读:餐饮管理软件代理商从"坐等"获利进入辛勤"打单"的薄利时期,这一现象竟是传统管理软件厂商一手造成的.是逆来顺受等待它们"良心发现",还是寻求突破把命运掌握自己手里?当下管理软件市场中新兴起的品牌和产品是如何拯救代理商? 正文:如今国内各行业管理软件市场早已进入成熟期,其中餐饮管理软件代理商们已从"坐等"获利进入辛勤"打单"的薄利时期,不但面临着新老软件代理商间不断压缩着市场份额,还要应对餐饮商家对餐饮

如何选择适合企业自己的信息化管理软件

如何选择适合企业自己的信息化管理软件:1.管理观念的转变.①ERP系统的实施不仅仅是一个IT项目,更是一个管理项目.但大多数企业高层管理人员并没有意识到这一点,他们只是将ERP系统在企业的应用作为一项技术工作处理,认为ERP系统的实施是纯技术问题,是技术人员的任务,而与管理人员无关,结果在选择系统和实施系统时步入误区.在选译系统时仅由技术主管负责,缺少高层管理人员和业务主管的参与.在实施系统时仅由技术部门负责,缺少管理人员和业务人员的积极参与.项目负责人由技术部门的领导担任,高层管理人员,尤其是

oa管理软件如何实现全面的协同办公管理?

随着科技的不断发展,市场上oa管理软件厂商越来越多,要想在众多oa管理软件厂商中脱颖而出,必须要有其"过人之处".新一代oa管理软件,加强企业业务监管.推动企业提升执行力,助领导做出快速.准确的决策等.那么oa管理软件如何实现全面的协同办公管理? 1.全面的功能和流程oa管理软件具有多种功能模块,如目标管理.信息管理.行政管理.人事管理.财务管理等,同时包含出差.报销.请假.订单等多种工作流程,协助企业实现全面协同办公管理. 2.与ERP对接oa管理软件支持与各大ERP厂商对接,数据自

采购管理软件有哪些系统功能?

目前市场上存在着参差不齐的各种采购管理软件,采购管理软件通常还会跟供应商管理系统一起使用,采购和供应是链接起来必不可少的一部分;它们也是ERP管理系统的一部分,从进销存模块中抽离出来的.采购管理软件更多是进行自己内部的管理,对于采购人,采购物品等相关信息的记录,分析,权限管理等;而供应商管理系统更多是对供应商层面进行管理,细化供应商,优化供应商,并对于供应产品和物料进行统一管理.那么,采购管理软件都有哪些系统功能呢?下面一起去了解下! 1.供应商关系管理 供应商信息管理:包括供应商基本信息.组织

C语言——软件定时器

都说程序设计 = 算法 + 数据结构.数据结构是挨踢必修课,不过好像学了数据结构之后也没用来做过啥.不知道做啥,就写个软件定时器. 软件定时器数据结构 typedef struct __software_timer{ u32 timeout; //初始化时间计数器 u32 repeat; //运行间隔:repeat > 0 :周期定时时间 repeat == 0 :只定时一次 void (*timeout_callback_handler)(void *para); //超时回调函数 struc

【TencentOS tiny】深度源码分析(8)——软件定时器

软件定时器的基本概念 TencentOS tiny 的软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受硬件定时器资源限制的定时器服务,本质上软件定时器的使用相当于扩展了定时器的数量,允许创建更多的定时业务,它实现的功能与硬件定时器也是类似的. 硬件定时器是芯片本身提供的定时功能.一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断.硬件定时器的精度一般很高,可以达到纳秒级别,并且是中