[应用相关] VSF平台之事件驱动PT

[复制链接]
2366|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中看到的。
代码最近才实现,不建议使用。

废话不多,直接上代码:
  1. struct vsfsm_pt_t
  2. {
  3.         vsfsm_evt_t (*thread)(struct vsfsm_pt_t *pt);
  4.         void *user_data;
  5.         
  6. #if VSFSM_CFG_PT_STACK_EN
  7.         // stack for the current pt, can be NULL to indicate using the main stack
  8.         // note that the size of the stack MUST be large enough for the interrupts
  9.         void *stack;
  10. #endif
  11.         
  12.         // protected
  13.         int state;
  14.         struct vsfsm_t *sm;
  15.         
  16.         // private
  17.         vsfsm_evt_t evt_waiting;
  18. };

PT结构还算简单,用户只需要定义一个线程函数,和用户自己的数据就行。让然,为了解决PT的自动变量使用问题,VSF中的PT可以支持一个线程的stack。
由于VSF中的PT不同于UIP中使用的基于轮训的PT,VSF中的是事件驱动的PT,所以线程函数也会固定的返回一个需要等待的事件,当然,如果线程运行完成,可以返回VSFSM_EVT_NONE。

  1. vsf_err_t vsfsm_pt_init(struct vsfsm_t *sm, struct vsfsm_pt_t *pt,
  2.                                                 bool add_to_top);
  3. #define vsfsm_pt_begin(pt)                                switch ((pt)->state) { case 0:
  4. // wait for event
  5. #define vsfsm_pt_wfe(pt, evt)                        (pt)->state = __LINE__; return (evt); case __LINE__:
  6. // wait for pt, slave pt uses the same stack as the master pt
  7. #define vsfsm_pt_wfpt(pt, ptslave)                do {\
  8.                                                                                         (ptslave)->state = 0;\
  9.                                                                                         (ptslave)->sm = (pt)->sm;\
  10.                                                                                         (pt)->state = __LINE__; case __LINE__:\
  11.                                                                                         {\
  12.                                                                                                 vsfsm_evt_t __evt = (ptslave)->thread(ptslave);\
  13.                                                                                                 if (__evt != VSFSM_EVT_NONE)\
  14.                                                                                                 {\
  15.                                                                                                         return __evt;\
  16.                                                                                                 }\
  17.                                                                                         }\
  18.                                                                                 } while (0)
  19. #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上拉(代码未编译,未检测过)。
  1. void usbd_pu(void)
  2. {
  3.         uint32_t start = interfaces->tickclk.get_count();
  4.         
  5.         interfaces->usbd.disconnect();
  6.         while ((interfaces->tickclk.get_count() - start) < 200);
  7.         interfaces->usbd.connect();
  8. }
VSF的EDA构架下,代码是这样的:
  1. static struct vsfsm_state_t *
  2. usbd_pu_evt_handler(struct vsfsm_t *sm, vsfsm_evt_t evt)
  3. {
  4.         switch (evt)
  5.         {
  6.         case VSFSM_EVT_INIT:
  7.                 interfaces->usbd.disconnect();
  8.                 vsftimer_register(&app.usbpu_timer);
  9.                 break;
  10.         case APP_EVT_USBPU_TO:
  11.                 interfaces->usbd.connect();
  12.                 vsftimer_unregister(&app.usbpu_timer);
  13.                 vsfsm_remove_subsm(&vsfsm_top, sm);
  14.                 break;
  15.         }
  16.         return NULL;
  17. }

INIT的时候,注册一个200毫秒的定时器,在定时器的超时事件中,移除定时器,并且移除本线程。

VSF的PT构架下,代码是这样的:
  1. vsfsm_evt_t usbd_pu_thread(struct vsfsm_pt_t *pt)
  2. {
  3.         vsfsm_pt_begin(pt);
  4.         interfaces->usbd.disconnect();
  5.         vsftimer_register(&app.usbpu_timer);
  6.         vsfsm_pt_wfe(pt, APP_EVT_USBPU_TO);
  7.         vsftimer_unregister(&app.usbpu_timer);
  8.         vsfsm_remove_subsm(&vsfsm_top, pt->sm);
  9.         interfaces->usbd.connect();
  10.         vsfsm_pt_end(pt);
  11.         return VSFSM_EVT_NONE;
  12. }

为了方便,这里展开PT的宏:
  1. vsfsm_evt_t usbd_pu_thread(struct vsfsm_pt_t *pt)
  2. {
  3.          switch (pt->state) { case 0:;
  4.         interfaces->usbd.disconnect();
  5.         vsftimer_register(&app.usbpu_timer);
  6.         pt->state = __LINE__; return APP_EVT_USBPU_TO; case __LINE__:;
  7.         vsftimer_unregister(&app.usbpu_timer);
  8.         vsfsm_remove_subsm(&vsfsm_top, pt->sm);
  9.         interfaces->usbd.connect();
  10.         };
  11.         return VSFSM_EVT_NONE;
  12. }

其中,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

粉丝
快速回复 在线客服 返回列表 返回顶部