打印
[应用相关]

VSF平台之事件驱动PT

[复制链接]
2079|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Simon21ic|  楼主 | 2014-10-6 09:50 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 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算法,使用比较多,一般应用不推荐使用。
相信经过一定时间和项目的检验,这个会是一个非常有意思的系统构架的实现。
沙发
myxiaonia| | 2014-10-6 11:04 | 只看该作者
大牛你好。。。

使用特权

评论回复
板凳
zh113214| | 2014-10-6 21:52 | 只看该作者
厉害啊!!!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:www.versaloon.com --- under construction

266

主题

2597

帖子

104

粉丝