contiki事件驱动

Contiki事件驱动

1 运行原理

嵌入式系统可以看作是一个运行着死循环主函数系统,Contiki内核是基于事件驱动的,系统运行可以视为不断处理事件的过程。Contiki整个运行是通过事件触发完成,一个事件绑定相应的进程。当事件被触发,系统把执行权交给事件所绑定的进程。一个典型基于Contiki 的系统运行示意图如下:

 

图1:Contiki OS运行流程

 

事实上,上述的框图几乎是主函数的流程图。通常情况下,应用程序作为一个进程放在自启动的指针数组中,系统启动后,先进行一系列的硬件初始化(包括串口、时钟 ) ,接着初始化进程,启动系统进程 ( 如管理 etimer 的系统进程etimer_process)和用户指定的自启动进程,然后进入处理事件的死循环(如上图右边框框所示,实际上是 process_run 函数的功能)。通过遍历执行完所有高优先级的进程,而后转去处理事件队列的一个事件,处理该事件(通常对应于执行一个进程)之后,需先满足高优先级进程才能转去处理下一个事件。将 process_run 代码展开加到 main 函数,保留关键代码,如下:

intmain()

{

clock_init();                             //时钟初始化

process_init();                          //进程初始化

process_start(&etimer_process,NULL);    //启动系统进程

autostart_start(autostart_processes);      //启动用户自启动进程

while(1)

{

/***函数process_run 的功能***/

if(poll_requested)

{

do_poll();                         //执行完所有高优先级的进程

}

do_event();                          //仅处理事件队列的一个事件

}

return0;

}

2  事件调度

事件驱动机制广泛应用于嵌入式系统,类似于中断机制,当有事件到来时(比如按键、数据到达),系统响应并处理该事件。相对于轮询机制,事件机制优势很明显,低功耗(系统处于休眠状态,当有事件到达时才被唤醒)和 MCU 利用率高。

Contiki 将事件机制融入Protothreads 机制,每个事件绑定一个进程(广播事件例外),进程间的消息传递也是通过事件来传递的。用无符号字符型来标识事件,事件结构体 event_data 定义如下:

structevent_data

{

process_event_tev;

process_data_tdata;

structprocess *p;

};

typedefunsigned char process_event_t;

typedefvoid *process_data_t;

用无符号字符型标识一个事件,Contiki定义了10个事件(0x80~0x8A),其他的供用户使用。每个事件绑定一个进程,如果p为NULL,表示该事件绑定所有进程(即广播事件 PROCESS_BROADCAST)。除此之外,事件可以携带数据data,可以利用这点进行进程间的通信(向另一进程传递带数据的事件)。

Contiki用一个全局的静态数组存放事件,这意味着事件数目在系统运行之前就要指定(用户可以通过PROCESS_CONF_NUMEVENTS自选配置大小),通过数组下标可以快速访问事件。系统还定义另两个全局静态变量nevents和fevent,分别用于记录未处理事件总数及下一个待处理的位置。事件逻辑组成环形队列,存储在数组里,如下图:


图2:事件存储示意图

可见对于Contiki 系统而言,事件并没有优先级之分,而是先到先服务的策略,全局变量 fevent 记录了下一次待处理事件的下标。

(1)事件产生

Conitki 有两种方式产生事件,即同步和异步。同步事件通过 process_post_synch函数产生,事件触发后直接处理(调用 call_process 函数)。而异步事件产生是由process_post 产生,并没有及时处理,而是放入事件队列等待处理,process_post流程图如下:


图3:process_post函数流程图

process_post首先判断事件队列是否已满,若满返回错误,否则取得下一个空闲位置(因为是环形队列,需做余操作),而后设置该事件并将未处理事件总数加 1。

(2)事件调度

事件没有优先级,采用先到先服务策略,每一次系统轮询(process_run 函数)只处理一个事件,do_event 函数用于处理事件,其流程图如下:

先附上int process_run(void)函数调用过程:

调用poll handlers(所有高优先级进程do_poll)和一个事件进程(do_event);该函数在main()主函数中被反复调用,进而运行contiki系统。它调用poll handlers(所有高优先级进程do_poll)和一个事件进程(do_event)。返回值为正在事件队列中等待的事件个数,调用者或许会令CPU休眠,在没有要处理的事件时。

process_run()调用:do_poll()和do_event()

前者是处理所有高优先级的进程,后者处理一个事件。

do_poll()和do_event()再调用call_procss()完成进程的处理。


图4:do_event函数流程图

do_event首先取出该事件(即将事件的值复制到一个新变量),更新总的未处理事件总数及下一个待处理事件的数组下标(环形队列,需要取余操作)。接着判断事件是否为广播事件 PROCESS_BROADCAST,若是,考虑到处理广播事件可能需要更多的时间,为保证系统实时性,先运行高优先级的进程,而后再去处理事件(调用 call_process 函数)。如果事件是初始化事件 PROCESS_EVENT_INIT  (创建进程的时候会触发此事件),需要将进程状态设为PROCESS_STATE_RUNNING。

(3)事件处理

实际的事件处理是在进程的函数体thread,正如上文所说的那样, call_process会调用 thread 函数,执行该进程。关键代码如下:ret = p->thread(&p->pt, ev, data);

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值