本帖最后由 Simon21ic 于 2014-10-6 21:42 编辑
关于事件驱动构架,先看这个吧:https://bbs.21ic.com/icview-779218-1-1.html
国庆节放假的时候,为EDA加入了PT的支持。
PT全称为ptorothread,是UIP协议栈中使用的一种多线程机制。
使用C99的__LINE__作为状态机的状态标志,一般使用switch case的方式。
最近看了一个脚contiki的开源系统,貌似和我定义的构架非常类似,protothread的实现方式,应该还是我从他们的UIP中看到的。
代码最近才实现,不建议使用。
废话不多,直接上代码:
struct vsfsm_pt_t
{
vsfsm_evt_t (*thread)(struct vsfsm_pt_t *pt);
void *user_data;
#if VSFSM_CFG_PT_STACK_EN
// stack for the current pt, can be NULL to indicate using the main stack
// note that the size of the stack MUST be large enough for the interrupts
void *stack;
#endif
// protected
int state;
struct vsfsm_t *sm;
// private
vsfsm_evt_t evt_waiting;
};
PT结构还算简单,用户只需要定义一个线程函数,和用户自己的数据就行。让然,为了解决PT的自动变量使用问题,VSF中的PT可以支持一个线程的stack。
由于VSF中的PT不同于UIP中使用的基于轮训的PT,VSF中的是事件驱动的PT,所以线程函数也会固定的返回一个需要等待的事件,当然,如果线程运行完成,可以返回VSFSM_EVT_NONE。
vsf_err_t vsfsm_pt_init(struct vsfsm_t *sm, struct vsfsm_pt_t *pt,
bool add_to_top);
#define vsfsm_pt_begin(pt) switch ((pt)->state) { case 0:
// wait for event
#define vsfsm_pt_wfe(pt, evt) (pt)->state = __LINE__; return (evt); case __LINE__:
// wait for pt, slave pt uses the same stack as the master pt
#define vsfsm_pt_wfpt(pt, ptslave) do {\
(ptslave)->state = 0;\
(ptslave)->sm = (pt)->sm;\
(pt)->state = __LINE__; case __LINE__:\
{\
vsfsm_evt_t __evt = (ptslave)->thread(ptslave);\
if (__evt != VSFSM_EVT_NONE)\
{\
return __evt;\
}\
}\
} while (0)
#define vsfsm_pt_end(pt) }
这里就是标准的PT的几个宏定义,和初始化函数,了解的人应该一看就明白了。
VSFSM中的PT实现,在构架上,只需要30多行代码,对于CortexM的芯片,最高size优化的编译结果大概是70字节不到。
实际也只是实现了一个vsfsm_pt_evt_handler,用来为PT线程处理事件,vsfsm_pt_evt_handler就完全按照VSFSM构架设计实现的EDA的事件处理函数。
PT的实际应用,这里只是介绍一下简单的应用,实际VSF构架下,甚至还可以支持动态生成线程等功能。
比如,一个简单的阻塞方式实现上电后,200毫秒之后使能USB上拉(代码未编译,未检测过)。
void usbd_pu(void)
{
uint32_t start = interfaces->tickclk.get_count();
interfaces->usbd.disconnect();
while ((interfaces->tickclk.get_count() - start) < 200);
interfaces->usbd.connect();
}
VSF的EDA构架下,代码是这样的:
static struct vsfsm_state_t *
usbd_pu_evt_handler(struct vsfsm_t *sm, vsfsm_evt_t evt)
{
switch (evt)
{
case VSFSM_EVT_INIT:
interfaces->usbd.disconnect();
vsftimer_register(&app.usbpu_timer);
break;
case APP_EVT_USBPU_TO:
interfaces->usbd.connect();
vsftimer_unregister(&app.usbpu_timer);
vsfsm_remove_subsm(&vsfsm_top, sm);
break;
}
return NULL;
}
INIT的时候,注册一个200毫秒的定时器,在定时器的超时事件中,移除定时器,并且移除本线程。
VSF的PT构架下,代码是这样的:
vsfsm_evt_t usbd_pu_thread(struct vsfsm_pt_t *pt)
{
vsfsm_pt_begin(pt);
interfaces->usbd.disconnect();
vsftimer_register(&app.usbpu_timer);
vsfsm_pt_wfe(pt, APP_EVT_USBPU_TO);
vsftimer_unregister(&app.usbpu_timer);
vsfsm_remove_subsm(&vsfsm_top, pt->sm);
interfaces->usbd.connect();
vsfsm_pt_end(pt);
return VSFSM_EVT_NONE;
}
为了方便,这里展开PT的宏:
vsfsm_evt_t usbd_pu_thread(struct vsfsm_pt_t *pt)
{
switch (pt->state) { case 0:;
interfaces->usbd.disconnect();
vsftimer_register(&app.usbpu_timer);
pt->state = __LINE__; return APP_EVT_USBPU_TO; case __LINE__:;
vsftimer_unregister(&app.usbpu_timer);
vsfsm_remove_subsm(&vsfsm_top, pt->sm);
interfaces->usbd.connect();
};
return VSFSM_EVT_NONE;
}
其中,vsfsm_pt_wfe是wait for event,对于PT线程的子线程,还可以使用vsfsm_pt_wfpt来等待另一个PT线程运行完成。
这里的代码,虽然貌似是阻塞的,但实际上,实现的是非阻塞操作,而且,在wfe后,实际PT线程是休眠的,等到有需要的事件后,才会唤醒继续运行,这个就是事件驱动的PT。
子PT线程共享主PT线程的stack。当是能了PT的独立stack后,主PT线程可以设置一个stack(当然,stack的大小需要考虑中断的使用),这样,PT线程中的局部变量其实就都是静态的了,这样可以实现一些更加特殊的应用。
实际应用中,一般是从PT类派生出一个专用的PT线程类,并且实例化来实现特定的应用线程,这个应用线程类如果是动态生成的话,那就可以动态生成线程。
EDA构架和PT构架,在应用上的区别是,EDA注重事件处理,事件可以无序发送给状态机。但是,PT构架注重线程,事件必须有序发送给状态机。
在VSF的状态机构架下,EDA、EDA的PT、EDA的FSM、EDA的HSM使用的类都完全一样,并且代码向上兼容;理论上各种不同类型的现成,也都可以互相嵌套,支持动态增加和移除;代码的开销根据配置,EDA构架使用大概600多字节,PT构架使用大概70字节不到,EDA的FSM使用非常少,不过HSM由于LCA算法,使用比较多,一般应用不推荐使用。
相信经过一定时间和项目的检验,这个会是一个非常有意思的系统构架的实现。
|