一、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.