Contiki Etimer 模块

一、Etimer概述

Etimer提供产生时间事件(timed event)的机制,当设定好的timer到期时,将会给设定etimer的process发送一个PROCESS_EVENT_TIMER 事件。

Etimer模块调用clock_time获得当前系统的时间。

The Contiki etimer library provides a timer mechanism that generate timed events. An event timer will post the event PROCESS_EVENT_TIMER to the process that set the timer when the event timer expires. The etimer library use clock_time() in the clock module to get the current system time.

二、Etimer数据结构

/**
 * A timer.
 *
 * This structure is used for declaring a timer. The timer must be set
 * with etimer_set() before it can be used.
 *
 * hideinitializer
 */
struct etimer {
  struct timer timer;//timer 记录时间
  struct etimer *next;//下一个etimer,为链表准备
  struct process *p;//etimer对应的进程process,即设置etimer的进程
};

全局变量:

static struct etimer *timerlist;//链表头

最后所有的etimer的组织形式如下所示:其中timerlist指向etimer0

图参考:http://blog.chinaunix.net/uid-9112803-id-2976929.html

1、etimer的插入

static void
add_timer(struct etimer *timer)
{
  struct etimer *t;

  etimer_request_poll();

    //以下部分,判断timer是否在当前链表中,且其没有被处理过(没有expired)
  if(timer->p != PROCESS_NONE) {//timer 没被处理过。etimer进程中,如果有etimer到期后,其相应的process会被设置为PROCESS_NONE
    /* Timer not on list. */

    for(t = timerlist; t != NULL; t = t->next) {//遍历,查找是否在当前链表中
      if(t == timer) {//找到
        /* Timer already on list, bail out. */
        timer->p = PROCESS_CURRENT();//设为当前进程
        update_time();//更新时间,有新的etimer加入,重新更新下next_expiration
        return;//返回
      }
    }
  }

    //添加进链表中
    //有两种情况,会执行以下代码
    //timer被处理过(expired)或者第一次添加进链表
  timer->p = PROCESS_CURRENT();
  timer->next = timerlist;
  timerlist = timer;

  update_time();//更新时间,有新的etimer加入,重新更新下next_expiration
 }

 调用add_timer的函数有:etimer_set、etimer_reset、etimer_restart

etimer_set是初始化,使用前要先调用etimer_set函数,etimer第一次添加进链表

调用etimer_reset和etimer_restart这两个函数时,相应的etimer已经在etimer_process中被处理过了(已经从链表中删除了,而且etimer相应的process被设置为PROCESS_NONE),或者还没被处理过(还在链表中)。

注:etimer_process对expired的etimer进行处理时,会将其process设为PROCESS_NONE,且从timerlist链表中删除

2、etimer的删除

有三个地方会引起etimer的删除操作:

etimer_process中,当相应的etimer到期后,并向相应process发送PROCESS_EVENT_TIMER事件之后。

/*---------------------------------------------------------------------------*/
PROCESS_THREAD(etimer_process, ev, data)
{
……
again:

    u = NULL;

    for(t = timerlist; t != NULL; t = t->next) {
      if(timer_expired(&t->timer)) {//到期
#if WITH_GUARD
        if(!sheph_ok() || process_post(t->p, PROCESS_EVENT_TIMER, t) == PROCESS_ERR_OK)
#else
        if(process_post(t->p, PROCESS_EVENT_TIMER, t) == PROCESS_ERR_OK)//发送PROCESS_EVENT_TIMER事件
#endif
        {
          /* Reset the process ID of the event timer, to signal that the
             etimer has expired. This is later checked in the
             etimer_expired() function. */
          t->p = PROCESS_NONE;
          if(u != NULL) {
            u->next = t->next;
          } else {
            timerlist = t->next;
          }
          t->next = NULL;
          update_time();//有变动,更新时间
          goto again;
        } else {
          etimer_request_poll();
        }
      }
      u = t;//记录上一个t
    }

  }

  PROCESS_END();

etimer_process中,当有进程退出时,对应的etimer也要删除。

PROCESS_THREAD(etimer_process, ev, data)
{
  struct etimer *t, *u;

  PROCESS_BEGIN();

  timerlist = NULL;

  while(1) {
    PROCESS_YIELD();

    if(ev == PROCESS_EVENT_EXITED) {
      struct process *p = data;

      while(timerlist != NULL && timerlist->p == p) {
        timerlist = timerlist->next;
      }

      if(timerlist != NULL) {
        t = timerlist;
        while(t->next != NULL) {
          if(t->next->p == p) {
            t->next = t->next->next;
          } else
            t = t->next;
        }
      }
      continue;
    } else if(ev != PROCESS_EVENT_POLL) {
      continue;
    }

……

  PROCESS_END();
}

etimer_stop函数

void
etimer_stop(struct etimer *et)
{
  struct etimer *t;

  /* First check if et is the first event timer on the list. */
  if(et == timerlist) {
    timerlist = timerlist->next;
    update_time();//有变动,更新下一个expired时间
  } else {
    /* Else walk through the list and try to find the item before the
       et timer. */
    for(t = timerlist; t != NULL && t->next != et; t = t->next);

    if(t != NULL) {
      /* We've found the item before the event timer that we are about
         to remove. We point the items next pointer to the event after
         the removed item. */
      t->next = et->next;

      update_time();//有变动,更新next_expiration
    }
  }

  /* Remove the next pointer from the item to be removed. */
  et->next = NULL;
  /* Set the timer as expired */
  et->p = PROCESS_NONE;
}

三、Etimer相关API

1、updata_time(static)

这个函数最主要的就是更新next_expiration这个变量,即下次expire的时间。

static void
update_time(void)
{
  clock_time_t tdist;
  clock_time_t now;
  struct etimer *t;

  if (timerlist == NULL) {
    next_expiration = 0;
  } else {
    now = clock_time();
    t = timerlist;
    /* Must calculate distance to next time into account due to wraps */
    tdist = t->timer.start + t->timer.interval - now;
    for(t = t->next; t != NULL; t = t->next) {
      if(t->timer.start + t->timer.interval - now < tdist) {
        tdist = t->timer.start + t->timer.interval - now;
      }
    }
#if USE_RTC_CLK
    clock_set_expire(tdist);
#endif
    next_expiration = now + tdist;
  }
}

注意:

这里不用担心clock_time wrap而出错的问题,具体可查看http://www.cnblogs.com/songdechiu/p/5397070.html

如果USE_RTC_CLK,则etimer还没到期时,MCU进入低功耗模式。

2、etimer_set、etimer_reset、etimer_restart等

代码如下:

/*---------------------------------------------------------------------------*/
void
etimer_set(struct etimer *et, clock_time_t interval)
{
  timer_set(&et->timer, interval);
  add_timer(et);
}
/*---------------------------------------------------------------------------*/
void
etimer_reset(struct etimer *et)
{
  timer_reset(&et->timer);
  add_timer(et);
}
/*---------------------------------------------------------------------------*/
void
etimer_restart(struct etimer *et)
{
  timer_restart(&et->timer);
  add_timer(et);
}
void
etimer_adjust(struct etimer *et, int timediff)
{
  et->timer.start += timediff;
  update_time();
}
/*---------------------------------------------------------------------------*/
int
etimer_expired(struct etimer *et)
{
  return et->p == PROCESS_NONE;
}
/*---------------------------------------------------------------------------*/
clock_time_t
etimer_expiration_time(struct etimer *et)
{
  return et->timer.start + et->timer.interval;
}
/*---------------------------------------------------------------------------*/
clock_time_t
etimer_start_time(struct etimer *et)
{
  return et->timer.start;
}

Like the previous timers, an event timer is always initialized by a call to etimer_set() which sets the timer to expire the specified delay from current time. etimer_reset() can then be used to restart the timer from previous expire time andetimer_restart() to restart the timer from current time, both using the same time interval that was originally set by etimer_set(). The difference between etimer_reset() andetimer_restart() is that the former schedules the timer from previous expiration time while the latter schedules the timer from current time thus allowing time drift. An event timer can be stopped by a call to etimer_stop() which means it will be immediately expired without posting a timer event. etimer_expired() is used to determine if the event timer has expired.

四、Porting the Etimer Library

clock底层需要通知etimer_process,有etimer即将到期。然后etimer_process进行相应的处理。

The clock module implementation usually also handles the notifications to the etimer library when it is time to check for expired event timers.

主要用到三个函数:

if(etimer_pending() && etimer_next_expiration_time() <= current_clock) {
    etimer_request_poll();
  }

 clock模块用于通知的主要代码。

1、etimer_pending

int
etimer_pending(void)
{
  return timerlist != NULL;
}

判断timerlist是否为空

2、etimer_next_expiration_time

clock_time_t
etimer_next_expiration_time(void)
{
  return etimer_pending() ? next_expiration : 0;
}

最近一个etimer到期的时间点next_expiration

每次调用update_time,这个变量都会更新。

3、etimer_request_poll

void
etimer_request_poll(void)
{
  process_poll(&etimer_process);
}

通知etimer_process是时候检查etimer的expired了,并进行处理。

五、etimer_process进程

PROCESS(etimer_process, "Event timer");
PROCESS_THREAD(etimer_process, ev, data) {
struct etimer *t, *u; PROCESS_BEGIN(); timerlist = NULL;//初始化timerlist while(1) { PROCESS_YIELD();//等待事件 if(ev == PROCESS_EVENT_EXITED) {//有别的进程要exit //这个时候需要把跟p相关的etimer从timerlist中remove struct process *p = data; while(timerlist != NULL && timerlist->p == p) {//timerlist就是remove对象 timerlist = timerlist->next; } if(timerlist != NULL) {//timerlist后边的remove操作 t = timerlist; while(t->next != NULL) { if(t->next->p == p) { t->next = t->next->next; } else t = t->next; } } continue;//删除完毕,继续返回循环,等待事件 } else if(ev != PROCESS_EVENT_POLL) { continue;//不是PROCESS_EVENT_POLL,继续等待 //直到PROCESS_EVENT_POLL事件到来,才能执行下边的操作 //因为只有PROCESS_EVENT_POLL才是标志着有etimer到期了,需要进行处理 } again://这部分是对etimer的expired进行处理 u = NULL; for(t = timerlist; t != NULL; t = t->next) {//遍历 if(timer_expired(&t->timer)) {//有etimer到期 #if WITH_GUARD if(!sheph_ok() || process_post(t->p, PROCESS_EVENT_TIMER, t) == PROCESS_ERR_OK) #else if(process_post(t->p, PROCESS_EVENT_TIMER, t) == PROCESS_ERR_OK)//向相关process发送PROCESS_EVENT_TMER事件成功 #endif { /* Reset the process ID of the event timer, to signal that the etimer has expired. This is later checked in the etimer_expired() function. */ t->p = PROCESS_NONE;//标志这个etimer已经被处理过 //将etimer从timerlist中remove if(u != NULL) {//timerlist后边的remove操作 u->next = t->next; } else {//timerlist的remove操作 timerlist = t->next; } t->next = NULL;//将remove去的对象的next设置为NULL update_time();//更新时间 goto again;//继续回到again,进行expired检查,处理 } else { etimer_request_poll();//发送事件不成功,继续poll } } u = t;//记录上一个t,方便后续进行链表的remove操作 } } PROCESS_END(); }

 六、etimer的使用

#include "sys/etimer.h"
 
 PROCESS_THREAD(example_process, ev, data)
 {
   static struct etimer et;//声明etimer
   PROCESS_BEGIN();
 
   /* Delay 1 second */
   etimer_set(&et, CLOCK_SECOND);//初始化etimer,即一秒钟后到期,并将process设置为example_process
 
   while(1) {
    //等待事件,并且当条件etimer_expired(&et)成立时
    //从上文,我们知道etimer到期时,etimer_process会将相应的etimer的p设置为PROCESS_NONE,并向相应的process发送PROCESS_EVENT_TIMER事件
     PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
     /* Reset the etimer to trig again in 1 second */
     etimer_reset(&et);
     /* ... */
   }
   PROCESS_END();
 }

 The etimer library cannot safely be used from interrupts.

原文地址:https://www.cnblogs.com/songdechiu/p/5954783.html