Chinaunix首页 | 论坛 | 博客
  • 博客访问: 26930
  • 博文数量: 12
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 82
  • 用 户 组: 普通用户
  • 注册时间: 2015-11-30 14:36
文章分类

全部博文(12)

文章存档

2016年(7)

2015年(5)

我的朋友

分类: 嵌入式

2016-09-13 09:37:00

etimer事件

etimer也是用时间列表管理,结构代码如下。

点击(此处)折叠或打开

  1. struct etimer {
  2.   struct timer timer;
  3.   struct etimer *next;
  4.   struct process *p;
  5. };
  6. struct timer {
  7.   clock_time_t start;
  8.   clock_time_t interval;
  9. }
由timer确定定时时间,next指向下一个etimer节点,p为绑定与etimer相关联的任务,在contiki中谁调用etimer_set函数,该进程绑定到p.
用户调用etimer_set -> add_timer将etimer挂载到时间管理链表,并将etimer与该进程关联起来,该链表的插入也是从头开始插入。

main函数启动一个etimer定时器,初始化后链表如下,

evt事件结构

点击(此处)折叠或打开

  1. struct event_data {
  2.   process_event_t ev;
  3.   process_data_t data;
  4.   struct process *p;
  5. };
ev为产生的事件,data产生事件所带的数据, p绑定获取该事件的进程。时间事件也是一个事件。事件管理使用一个环形FIFO队类管理,nevents表示队列里事件的总数(放入事件++,取出事件--),fevent表示取事件时下一个事件的下标(取事件++)。process_post_synch()向事件队列里发送一个事件,do_event()从事件队列中取出事件并处理绑定的进程。

广播事件

在事件中,会绑定获取该事件进程的指针,如果未绑定进程指针,则该事件是广播事件,所谓广播事件是所有进程均会获取该事件,所有进程均执行该事件。


点击(此处)折叠或打开

  1. if(receiver == PROCESS_BROADCAST) {
  2.       for(= process_list; p != NULL; p = p->next) {

  3.     /* If we have been requested to poll a process, we do this in
  4.      between processing the broadcast event. */
  5.     if(poll_requested) {
  6.      do_poll();
  7.     }
  8.     call_process(p, ev, data);
  9.       }
  10.     }

etimer源码

首先需要添加一个定时器:

点击(此处)折叠或打开

  1. void etimer_set(struct etimer *et, clock_time_t interval)
  2. {
  3.   timer_set(&et->timer, interval); /**< 设置定时时间间隔*/
  4.   add_timer(et);   /**< 将定时器添加到链表*/
  5. }

点击(此处)折叠或打开

  1. static void add_timer(struct etimer *timer)
  2. {
  3.   struct etimer *t;

  4.   etimer_request_poll(); /**< 使etimer有高的优先级,为什么在这里添加我也不知道*/
  5.   /**例如一个定时器已经被一个线程绑定(定时还未到期),另一个线程也要绑定该定时器
  6.    * 此时就需要更新定时器绑定的进程,但不需要重新放入绑定列表中
  7.    */
  8.   if(timer->p != PROCESS_NONE) {
  9.     for(t = timerlist; t != NULL; t = t->next) {
  10.       if(t == timer) {
  11.     /**该定时器已被重新绑定线程,需要寻找快要到达时间的定时器
  12.      */
  13.     update_time();
  14.     return;
  15.       }
  16.     }
  17.   }

  18.   /**定时器未放入绑定列表中,将定时器放入绑定列表
  19.    */
  20.   timer->p = PROCESS_CURRENT();
  21.   timer->next = timerlist;
  22.   timerlist = timer;

  23.   update_time();   /**< 新添加定时器,需要更新时间*/
  24. }
到此,定时器添加完成。下面定时器靠中断运行,并且当定时到达后,会触发定时事件。详细分析见下:

点击(此处)折叠或打开

  1. void SysTick_Handler(void)
  2. {
  3.         current_clock++;
  4.         /**定时列表中有定时器,并且定时时间到达
  5.          */
  6.         if(etimer_pending()&& etimer_next_expiration_time()<= current_clock) { 
  7.                 etimer_request_poll(); /**<让etimer有较高的优先级,主函数查询,执行etimer进程 */
  8.         }
  9.         if (--second_countdown== 0) { /**< 秒定时*/
  10.                 current_seconds++;
  11.                 second_countdown = CLOCK_SECOND;
  12.         }
  13. }
定时时间到后,执行etimer进程,源码如下:

点击(此处)折叠或打开

  1. PROCESS_THREAD(etimer_process, ev, data)
  2. {
  3.   struct etimer *t, *u;
  4.     
  5.   PROCESS_BEGIN();

  6.   timerlist = NULL;
  7.   
  8.   while(1) {
  9.     PROCESS_YIELD();/**< 进程主动挂起*/
  10.     /**事件是其他事件的退出事件,移除与退出事件相关的定时器
  11.      */
  12.     if(ev == PROCESS_EVENT_EXITED) {
  13.       struct process *p = data;

  14.       while(timerlist != NULL && timerlist->p == p) {
  15.         timerlist = timerlist->next;
  16.       }

  17.       if(timerlist != NULL) {
  18.         t = timerlist;
  19.         while(t->next != NULL) {
  20.           if(t->next->p == p) {
  21.           t->next = t->next->next;
  22.         } else
  23.           t = t->next;
  24.        }
  25.       }
  26.       continue; /**< 事件执行完成,进行下次循环,即挂起该进程*/
  27.     } else if(ev != PROCESS_EVENT_POLL) {
  28.       continue;
  29.     }

  30.   again:      
  31.      /**定时时间到事件,将该事件放入事件队列中
  32.       */
  33.     u = NULL;
  34.     
  35.     for(t = timerlist; t != NULL; t = t->next) {
  36.       if(timer_expired(&t->timer)) {    /**< 定时时间到*/
  37.     if(process_post(t->p, PROCESS_EVENT_TIMER, t) == PROCESS_ERR_OK) {/**放入事件队列成功*/
  38.      
  39.      t->p = PROCESS_NONE; /**将绑定事件指针置NULL,表示etimer定时时间到,此时定时器已无绑定进程,再次使用定时器需要调用etimer_reset()重新设置定时器*/
  40.      if(u != NULL) {
  41.      u->next = t->next;
  42.      } else {
  43.      timerlist = t->next;
  44.      }
  45.      t->next = NULL;
  46.      update_time();   /**< 寻找最新快要到达的定时器*/
  47.      goto again;
  48.     } else {
  49.      etimer_request_poll(); /**< 事件列表中添加失败,需要再次添加,将优先级置高*/
  50.     }
  51.       }
  52.       u = t;
  53.     }
  54.     
  55.   }
  56.   
  57.   PROCESS_END();
  58. }

定时时间到需要将定时器事件放入事件队列,也就是将与该定时器绑定的进程、定时器事件放入事件队列,

点击(此处)折叠或打开

  1. int process_post(struct process *p, process_event_t ev, process_data_t data)
  2. {
  3.   static process_num_events_t snum;
  4.   
  5.   if(nevents == PROCESS_CONF_NUMEVENTS) { /**< 队列满*/

  6.     return PROCESS_ERR_FULL;
  7.   }
  8.   /**将事件放入队列
  9.    */
  10.   snum = (process_num_events_t)(fevent + nevents) % PROCESS_CONF_NUMEVENTS;
  11.   events[snum].ev = ev;
  12.   events[snum].data = data;
  13.   events[snum].p = p;
  14.   ++nevents;
  15.   
  16.   return PROCESS_ERR_OK;
  17. }
将事件放入队列后,函数从队列中取出事件,执行:

点击(此处)折叠或打开

  1. static void
  2. do_event(void)
  3. {
  4.   static process_event_t ev;
  5.   static process_data_t data;
  6.   static struct process *receiver;
  7.   static struct process *p;
  8.   
  9.   if(nevents > 0) {
  10.     
  11.     /**从队列中提取事件
  12.      */
  13.     ev = events[fevent].ev;
  14.     
  15.     data = events[fevent].data;
  16.     receiver = events[fevent].p;
  17.     fevent = (fevent + 1) % PROCESS_CONF_NUMEVENTS;
  18.     --nevents;
  19.     /**如果事件类型是广播事件(绑定进程指针为NULL),例如某个进程可以调用process_post发送广播事件
  20.      * 调用定时器时,在主函数中调用,则可将定时器事件做成广播事件
  21.      * 得到广播事件后,遍历进程列表,执行各个进程
  22.      */
  23.     if(receiver == PROCESS_BROADCAST) {
  24.       for(p = process_list; p != NULL; p = p->next) {
  25.     
  26.     if(poll_requested) {
  27.      do_poll();
  28.     }
  29.     call_process(p, ev, data);
  30.       }
  31.     } else {
  32.       /**非广播事件,则是指向特定任务的事件。
  33.         */
  34.       if(ev == PROCESS_EVENT_INIT) {   /**< 如果是初始化事件,个人觉得初始化事件没有什么意义,例如如果状态为NONE,调用exit,该进程已经从进程链表中删除*/
  35.     receiver->state = PROCESS_STATE_RUNNING;
  36.       }

  37.      
  38.       call_process(receiver, ev, data);/**< 让进程运行*/
  39.     }
  40.   }
  41. }
以上代码分析,即把时间事件和事件联系在一起,继续分析timer源码:


点击(此处)折叠或打开

  1. static void
  2. update_time(void)
  3. {
  4.   clock_time_t tdist;
  5.   clock_time_t now;
  6.   struct etimer *t;

  7.   if (timerlist == NULL) {
  8.     next_expiration = 0;   /**< 下一个到达定时的时间,该值与系统当前时间比较,如果小于系统当前时间,说明定时时间到达*/
  9.   } else {
  10.     now = clock_time();
  11.     t = timerlist;
  12.     /**遍历链表,获取系统最快到达定时时间的定时器*/
  13.     tdist = t->timer.start + t->timer.interval - now;
  14.     for(t = t->next; t != NULL; t = t->next) {
  15.       if(t->timer.start + t->timer.interval - now < tdist) {
  16.     tdist = t->timer.start + t->timer.interval - now;
  17.       }
  18.     }
  19.     next_expiration = now + tdist;
  20.   }
  21. }



阅读(525) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~