etimer事件
etimer也是用时间列表管理,结构代码如下。
-
struct etimer {
-
struct timer timer;
-
struct etimer *next;
-
struct process *p;
-
};
-
struct timer {
-
clock_time_t start;
-
clock_time_t interval;
-
}
由timer确定定时时间,next指向下一个etimer节点,p为绑定与etimer相关联的任务,在contiki中谁调用etimer_set函数,该进程绑定到p.
用户调用etimer_set -> add_timer将etimer挂载到时间管理链表,并将etimer与该进程关联起来,该链表的插入也是从头开始插入。
main函数启动一个etimer定时器,初始化后链表如下,
evt事件结构
-
struct event_data {
-
process_event_t ev;
-
process_data_t data;
-
struct process *p;
-
};
ev为产生的事件,data产生事件所带的数据, p绑定获取该事件的进程。时间事件也是一个事件。事件管理使用一个环形FIFO队类管理,nevents表示队列里事件的总数(放入事件++,取出事件--),fevent表示取事件时下一个事件的下标(取事件++)。process_post_synch()向事件队列里发送一个事件,do_event()从事件队列中取出事件并处理绑定的进程。
广播事件
在事件中,会绑定获取该事件进程的指针,如果未绑定进程指针,则该事件是广播事件,所谓广播事件是所有进程均会获取该事件,所有进程均执行该事件。
-
if(receiver == PROCESS_BROADCAST) {
-
for(p = process_list; p != NULL; p = p->next) {
-
-
/* If we have been requested to poll a process, we do this in
-
between processing the broadcast event. */
-
if(poll_requested) {
-
do_poll();
-
}
-
call_process(p, ev, data);
-
}
-
}
etimer源码
首先需要添加一个定时器:
-
void etimer_set(struct etimer *et, clock_time_t interval)
-
{
-
timer_set(&et->timer, interval); /**< 设置定时时间间隔*/
-
add_timer(et); /**< 将定时器添加到链表*/
-
}
-
static void add_timer(struct etimer *timer)
-
{
-
struct etimer *t;
-
-
etimer_request_poll(); /**< 使etimer有高的优先级,为什么在这里添加我也不知道*/
-
/**例如一个定时器已经被一个线程绑定(定时还未到期),另一个线程也要绑定该定时器
-
* 此时就需要更新定时器绑定的进程,但不需要重新放入绑定列表中
-
*/
-
if(timer->p != PROCESS_NONE) {
-
for(t = timerlist; t != NULL; t = t->next) {
-
if(t == timer) {
-
/**该定时器已被重新绑定线程,需要寻找快要到达时间的定时器
-
*/
-
update_time();
-
return;
-
}
-
}
-
}
-
-
/**定时器未放入绑定列表中,将定时器放入绑定列表
-
*/
-
timer->p = PROCESS_CURRENT();
-
timer->next = timerlist;
-
timerlist = timer;
-
-
update_time(); /**< 新添加定时器,需要更新时间*/
-
}
到此,定时器添加完成。下面定时器靠中断运行,并且当定时到达后,会触发定时事件。详细分析见下:
-
void SysTick_Handler(void)
-
{
-
current_clock++;
-
/**定时列表中有定时器,并且定时时间到达
-
*/
-
if(etimer_pending()&& etimer_next_expiration_time()<= current_clock) {
-
etimer_request_poll(); /**<让etimer有较高的优先级,主函数查询,执行etimer进程 */
-
}
-
if (--second_countdown== 0) { /**< 秒定时*/
-
current_seconds++;
-
second_countdown = CLOCK_SECOND;
-
}
-
}
定时时间到后,执行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;
-
}
-
-
again:
-
/**定时时间到事件,将该事件放入事件队列中
-
*/
-
u = NULL;
-
-
for(t = timerlist; t != NULL; t = t->next) {
-
if(timer_expired(&t->timer)) { /**< 定时时间到*/
-
if(process_post(t->p, PROCESS_EVENT_TIMER, t) == PROCESS_ERR_OK) {/**放入事件队列成功*/
-
-
t->p = PROCESS_NONE; /**将绑定事件指针置NULL,表示etimer定时时间到,此时定时器已无绑定进程,再次使用定时器需要调用etimer_reset()重新设置定时器*/
-
if(u != NULL) {
-
u->next = t->next;
-
} else {
-
timerlist = t->next;
-
}
-
t->next = NULL;
-
update_time(); /**< 寻找最新快要到达的定时器*/
-
goto again;
-
} else {
-
etimer_request_poll(); /**< 事件列表中添加失败,需要再次添加,将优先级置高*/
-
}
-
}
-
u = t;
-
}
-
-
}
-
-
PROCESS_END();
-
}
定时时间到需要将定时器事件放入事件队列,也就是将与该定时器绑定的进程、定时器事件放入事件队列,
-
int process_post(struct process *p, process_event_t ev, process_data_t data)
-
{
-
static process_num_events_t snum;
-
-
if(nevents == PROCESS_CONF_NUMEVENTS) { /**< 队列满*/
-
-
return PROCESS_ERR_FULL;
-
}
-
/**将事件放入队列
-
*/
-
snum = (process_num_events_t)(fevent + nevents) % PROCESS_CONF_NUMEVENTS;
-
events[snum].ev = ev;
-
events[snum].data = data;
-
events[snum].p = p;
-
++nevents;
-
-
return PROCESS_ERR_OK;
-
}
将事件放入队列后,函数从队列中取出事件,执行:
-
static void
-
do_event(void)
-
{
-
static process_event_t ev;
-
static process_data_t data;
-
static struct process *receiver;
-
static struct process *p;
-
-
if(nevents > 0) {
-
-
/**从队列中提取事件
-
*/
-
ev = events[fevent].ev;
-
-
data = events[fevent].data;
-
receiver = events[fevent].p;
-
fevent = (fevent + 1) % PROCESS_CONF_NUMEVENTS;
-
--nevents;
-
/**如果事件类型是广播事件(绑定进程指针为NULL),例如某个进程可以调用process_post发送广播事件
-
* 调用定时器时,在主函数中调用,则可将定时器事件做成广播事件
-
* 得到广播事件后,遍历进程列表,执行各个进程
-
*/
-
if(receiver == PROCESS_BROADCAST) {
-
for(p = process_list; p != NULL; p = p->next) {
-
-
if(poll_requested) {
-
do_poll();
-
}
-
call_process(p, ev, data);
-
}
-
} else {
-
/**非广播事件,则是指向特定任务的事件。
-
*/
-
if(ev == PROCESS_EVENT_INIT) { /**< 如果是初始化事件,个人觉得初始化事件没有什么意义,例如如果状态为NONE,调用exit,该进程已经从进程链表中删除*/
-
receiver->state = PROCESS_STATE_RUNNING;
-
}
-
-
-
call_process(receiver, ev, data);/**< 让进程运行*/
-
}
-
}
-
}
以上代码分析,即把时间事件和事件联系在一起,继续分析timer源码:
-
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;
-
/**遍历链表,获取系统最快到达定时时间的定时器*/
-
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;
-
}
-
}
-
next_expiration = now + tdist;
-
}
-
}
阅读(525) | 评论(0) | 转发(0) |