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

  • 相关阅读:
    property 中的strong 与weak
    ios5 StoryBoard
    PLINQ中的分区
    ZOJ3704 I am Nexus Master! 模拟
    POJ1470 Closest Common Ancestors TarjanLCA
    XTU1170 Coin 线段树
    HDU2586 How far away ? LCATarjan Or spfa
    CF#303C Minimum Modular 数学分析
    CF#303B Rectangle Puzzle II 数学分析
    ZOJ3698 Carrot Fantasy 恶心模拟
  • 原文地址:https://www.cnblogs.com/songdechiu/p/5954783.html
Copyright © 2020-2023  润新知