搜索
bottom↓
回复: 25

Protothread学习

[复制链接]

出0入0汤圆

发表于 2009-3-25 21:29:47 | 显示全部楼层 |阅读模式
使用keil 以at89s52为目标成功编译的代码.rarourdev_427984.rar(文件大小:29K) (原文件名:使用keil 以at89s52为目标成功编译的代码.rar)
标签: RTOS  
Protothread学习
Protothreads简介
http://blog.csdn.net/t_larry/archive/2008/10/18/3097191.aspx
    原来在用RTOS时感觉特别方便的就是任务函数中可以调用系统提供的延时,等待等函数,切换到其它任务上执行,这样写程序又简单而且又能充分利用硬件资源,但这样做在切换任务时浪费了很多的系统时间及占用很多RAM,而且对于8位或16位小内存的 单片机也不太适合。最近几天在看开源网络协议UIP1.0,居然在DHCP应用实例中看到了一种以很简单的方法实现类似多任务功能的编程方法,只需占用只个字节的RAM资源,而且非常的容易使用。
    要实现这种这种编程方法,只需下载到UIP1.0的源代码,找到其中的三个文件分别是pt.h,lc.h,lc-switch.h,在自己项目中把pt.h包含进去就行。
   在DHCP中有完整的应用函数static PT_THREAD(handle_dhcp(void)),这个函数年看起来有点怪,但其实将其宏展开就是static char handle_dhcp(void),返回char类型静态函数,其实就是向外部返回任务执行状态。pt.h中提供很多功能的宏定义:
PT_INIT(pt)   初始化任务变量,只在初始化函数中执行一次就行
PT_BEGIN(pt)   启动任务处理,放在函数开始处
PT_END(pt)   结束任务,放在函数的最后
PT_WAIT_UNTIL(pt, condition) 等待某个条件(条件可以为时钟或其它变量,IO等)成立,否则直接退出本函数,下一次进入本     函数就直接跳到这个地方判断
PT_WAIT_WHILE(pt, cond)  和上面一个一样,只是条件取反了
PT_WAIT_THREAD(pt, thread) 等待一个子任务执行完成
PT_SPAWN(pt, child, thread) 新建一个子任务,并等待其执行完退出
PT_RESTART(pt)   重新启动某个任务执行
PT_EXIT(pt)   任务后面的部分不执行,直接退出重新执行
PT_YIELD(pt)   锁死任务
PT_YIELD_UNTIL(pt, cond) 锁死任务并在等待条件成立,恢复执行
在pt中一共定义四种线程状态,在任务函数退出到上一级函数时返回其状态
PT_WAITING  等待
PT_EXITED  退出
PT_ENDED  结束
PT_YIELDED  锁死


利用Protothread实现实时多任务系统
http://www.mesnet.com.cn/htm/article_view.asp?id=2581
摘要在许多系统资源非常紧张的单片机应用中,使用实时操作系统进行任务调度来实现实时多任务系统时,由操作系统带来的系统开销往往是不可接受的。通过升级硬件来改善系统资源紧张,意味着成本的增加,降低产品的竞争力。本文介绍采用Protothread在非常小的系统开销下实现实时多任务系统的方法。
关键词 Protothread 实时 多任务 线程模型
  嵌入式程序框架一般类似于程序1所示结构:系统中有3个任务——TaskA、TaskB、TaskC,均放置于主循环内,在每一个循环周期内都被执行一次。在这种结构中,能满足系统实时性要求的条件是: (当且仅当)TaskA 、TaskB、TaskC三个任务的运行时间之和要小于系统实时响应的时间要求。在系统较为简单、任务运行时间能满足实时要求的情况下,可以采用这种最简单、最直接的顺序执行方式。但是更多的情形是,系统不仅要对一些事件做出实时响应,并且还要承担很多其他的非实时任务,并且这些非实时任务的运行时间要远远超出了实时响应时间的要求。传统的这种程序结构显然不能满足系统的实时性要求。通常的解决方案是,引入实时操作系统,由操作系统进行任务的调度,优先执行实时任务,达到满足系统实时性的要求。
  程序1嵌入式程序框架
  void main(void) {
    Init();
    while(1) {
      TaskA();
      TaskB();
      TaskC();
    }
  }
  void Interrupt_1(void) interrupt 1 {
    …
  }
  void Interrupt_2(void) interrupt 2 {
    …
  }
  一般来说,在嵌入式系统开发中引入实时操作系统有诸多优点:
  ◆ 更好地支持多任务,实时性要求能够得以保障;
  ◆ 程序开发更加容易,也更便于维护;
  ◆ 有利于提高系统的稳定性和可靠性。但是,操作系统的引入也将带来较多的系统开销:
  ◆ 实时操作系统往往使用定时器中断来切换任务,需要消耗不少的CPU处理时间;
  ◆ 实时操作系统在切换任务时需要保护当前任务的执行现场,这就需要为每个任务准备足够多的RAM空间来实现任务切换;
  ◆ 实时操作系统的本身也需要占用相当数量的Flash空间和RAM空间。
  如果这些系统开销都在可承受的范围内,那么采用实时操作系统将是最佳的选择。但是在很多应用的场合,特别是系统的资源非常紧张的单片机应用,实时操作系统带来的系统开销往往是不可接受的。而更换速度更快、RAM更大、Flash更多的CPU意味着成本的增加,且会降低产品的竞争力。当系统中的任务不须进行非常复杂的优先级调度,而且其任务也相对简单时,引入实时操作系统似有杀鸡用牛刀之嫌。
1 Protothread的特点
  Protothread是专为资源有限的系统设计的一种耗费资源特别少并且不使用堆栈的线程模型,其特点是:
  ◆ 以纯C语言实现,无硬件依赖性;
  ◆ 极少的资源需求,每个Protothread仅需要2个额外的字节;
  ◆ 可以用于有操作系统或无操作系统的场合;
  ◆ 支持阻塞操作且没有栈的切换。
  使用Protothread实现多任务的最主要的好处在于它的轻量级。每个Protothread不需要拥有自已的堆栈,所有的Protothread 共享同一个堆栈空间,这一点对于RAM资源有限的系统尤为有利。相对于操作系统下的多任务而言,每个任务都有自已的堆栈空间,这将消耗大量的RAM资源,而每个Protothread仅使用一个整型值保存当前状态。
2 Protothread的阻塞运行机制
  以下是一个典型的Protothread程序示例:
  程序2Protothread程序示例
  PT_THREAD(radio_wake_thread(struct pt *pt)) {
    PT_BEGIN(pt);
    while(1) {
      radio_on();
      timer_set(&timer, T_AWAKE);
      PT_WAIT_UNTIL(pt, timer_expired(&timer));
      timer_set(&timer, T_SLEEP);
      if(!communication_complete()) {
        PT_WAIT_UNTIL(pt, communication_complete()‖timer_expired(&timer));
      }
      if(!timer_expired(&timer)) {
        radio_off();
        PT_WAIT_UNTIL(pt, timer_expired(&timer));
      }
    }
    PT_END(pt);
  }
  这是一个非常简单的无线通信的状态切换程序①,展开Protothread的宏定义,便可以得到程序3所示的展开代码:
  程序3Protothread宏展开代码
  void radio_wake_thread(struct pt *pt) {
    switch(pt﹥lc) {
      case 0:
      while(1) {
        radio_on();
        timer_set(&timer, T_AWAKE);
        pt﹥lc = 8;
        case 8:
          if(!timer_expired(&timer)) {
            return;
          }
          timer_set(&timer, T_SLEEP);
          if(!communication_complete()) {
            pt﹥lc = 13;
            case 13:
            if(!(communication_complete() ||timer_expired(&timer))) {
              return;
            }
          }
          if(!timer_expired(&timer)) {
            radio_off();
            pt﹥lc = 18;
            case 18:
            if(!timer_expired(&timer)) {
              return;
            }
          }
        }
      }
    }
  当Protothread程序运行到PT_WAIT_UNTIL时,判断其运行条件是否满足,若不满足,则阻塞。通过比对程序2和程序3的程序代码可以得知,Protothread的阻塞其实质就是函数返回,只不过在返回前保存了当前的阻塞位置,待下一次Protothread被调用时,直接跳到阻塞位置执行,再次判断运行条件是否满足,并执行后续程序或继续阻塞。
3 利用Protothread构造实时多任务系统
  与操作系统下的多任务不同,操作系统下的每个任务可在任意时刻被打断并阻塞,Protothread仅能在程序员指定位置阻塞。用Protothread实现实时多任务,正是利用了Protothread在指定位置阻塞的特点,让出执行权限给更高优先级的任务先运行。
  下面举例说明如何利用Protothread构造实时多任务系统。
  系统要求:
  TaskA实时任务,30 ms内响应,运行时间<20 ms;
  TaskB实时任务,200 ms内响应,运行时间<40 ms;
  TaskC非实时任务,响应时间无要求,运行时间>30 ms。
  设计思路:
  将TaskB和TaskC分成若干步,每步运行时间不超过10 ms(这个时间可视系统需求而定,例如TaskA若为40 ms内响应,则每步可扩至20 ms)。任务以3个Protothread的方式运行。首先执行TaskA,在TaskA执行完成1次后,释放执行权限,让TaskB和TaskC执行。 TaskB或TaskC在每执行1步之前检查运行时间,一旦发现30 ms内不够执行1步时,阻塞运行,让出执行权限给TaskA。同样,TaskB和TaskC的调度关系也类似,先运行TaskB,完成时释放执行权限,让 TaskC执行;TaskC在每执行1步之前检查运行时间,若发现200 ms内不够执行1步时,阻塞运行,让出执行权限重新交给TaskB。
  源程序(Task0TimeCounter、Task1TimeCounter为计数器,每毫秒加1):
  #include "ptsem.h"
  #define TASKA_MAX_RUN_TIME20
  #define TASKA_CYCLE_TIME30
  #define TASKB_CYCLE_TIME200
  #define TASK_STEP_TIME10
  #define TASK0_VALID_TIME TASKA_CYCLE_TIMETASK_STEP_TIME
  #define TASK1_VALID_TIME TASKB_CYCLE_TIMETASK_STEP_TIMETASKA_MAX_RUN_TIME *2
  /*按照PT_WAIT_UNTIL 的宏定义扩展一个新宏:当程序进入阻塞时发送一信号,告知高优先级任务获得执行权限*/
  #define LC_STEP_SET(s,n) s = __LINE__ + n; case __LINE__ + n:
  #define PT_SEM_WAIT_UNTIL(pt, s, condition, n)\
    do { \
      LC_STEP_SET((pt)-﹥lc,n); \
      if(!(condition)) {if((s)-﹥count==0) \
        PT_SEM_SIGNAL(pt,s); \
        return PT_WAITING; \
      } \
    } while(0)
  struct pt TaskAPt;
  struct pt TaskBPt;
  struct pt TaskCPt;
  struct pt_sem SemRunTaskA;
  struct pt_sem SemRunTaskB;
  /*若30 ms内已经不够时间执行1步,则让出TaskA的执行权限*/
  #define TASKB_STEP(pt) \\
    PR_SEM_WAIT_UNTIL(pt, & SemRunTaskA,Task0TimeCounter<=TASK0_VALID_TIME,0)
  /*若200 ms内已经不够时间执行1步,则让出TaskB的执行权限*/
  /*若30 ms内已经不够时间执行1步,则让出TaskA的执行权限*/
  #define TASKC_STEP(pt) \
    PT_SEM_WAIT_UNTIL(pt, &SemRunTaskB,Task1TimeCounter<=TASK1_VALID_TIME,0);\
    PT_SEM_WAIT_UNTIL(pt, &SemRunTaskA,Task0TimeCounter<=TASK0_VALID_TIME,1);
  int ProtothreadTaskA(struct pt *pt) {
    PT_BEGIN(pt);
    PT_SEM_WAIT(pt, &SemRunTaskA);/*等待其他任务让出执行权限*/
    ResetTask0TimeCounter;/*对时间计数器置0*/
    TaskA();/*TaskA任务*/
    PT_END(pt);
  }
  int ProtothreadTaskB(struct pt *pt) {
    PT_BEGIN(pt);
    PT_SEM_WAIT(pt, &SemRunTaskB);/*等待TaskC让出执行权限*/
    ResetTask1TimeCounter;/*对时间计数器置0*/
    TASKB_STEP(pt);/*如果不够1步执行,则阻塞,让出执行权限*/
    TaskB_1();/*TaskB任务的第1步*/
    TASKB_STEP(pt);
    TaskB_2();/*TaskB任务的第2步*/
    TASKB_STEP(pt);
    TaskB_3();/*…*/
    PT_END(pt);
  }
  int ProtothreadTaskC(struct pt *pt) {
    PT_BEGIN(pt);
    TASKC_STEP(pt);/*如果不够1步执行,则阻塞,让出执行权限*/
    TaskC_1();/*TaskB任务的第1步*/
    TASKC_STEP(pt);
    TaskC_2();/*TaskB任务的第2步*/
    TASKC_STEP(pt);
    TaskC_3();/*…*/
    TASKC_STEP(pt);
    TaskC_4();
    PT_END(pt);
  }
  void main(void) {/*系统初始化*/
    PT_INIT(&TaskAPt);
    PT_INIT(&TaskBPt);
    PT_INIT(&TaskCPt);
    PT_SEM_INIT(&SemRunTaskA,1);
    PT_SEM_INIT(&SemRunTaskB,1);/*运行任务*/
    while(1) {
      ProtothreadTaskA(&TaskAPt);
      ProtothreadTaskB(&TaskBPt);
      ProtothreadTaskC(&TaskCPt);
    }
  }
  模拟运行结果如表1所列。运行结果显示,3个任务的运行情况完全满足系统的设计要求。从资源需求来看,完成此例的系统设计,共需要12个字节的RAM空间。笔者进一步对Protothread定义文件做了少许修改和优化,最终仅耗费6个字节。
结语
  本文旨在解决资源紧张型应用的、多任务环境下的实时性问题。 通过借助Protothread的阻塞运行机制, 成功实现了低开销的实时多任务系统。
表1 模拟运行结果运行
            
参考文献
[1] Adam Dunkels, Oliver Schmidt, Thiemo Voigt. Using Protothreads for Sensor Node Programming[C]. REALWSN'05 Workshop on RealWorld Wireless Sensor Networks, Stockholm, Sweden, June 2005
[2] Adam Dunkels, Oliver Schmidt, Thiemo Voigt, et al. Protothreads: Simplifying EventDriven Programming of MemoryConstrained Embedded Systems[C]. In Proceedings of the Fourth ACM Conference on Embedded Networked Sensor Systems (SenSys 2006), Boulder, Colorado, USA, November 2006.
[3] Labrosse Jean J. MicroC/OSII The Real Time Kernel Second Edition[M]. CMP Books, CMP Media.
[4] 冉全. 单片机中基于多线程机制的实时多任务研究[J] .微型机与应用,2003(8): 39-40.

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

发表于 2009-3-25 21:50:25 | 显示全部楼层
厉害。

顶。

Protothreads的调度方法据说叫“协程”,占用RAM小。
freertos也有类似调度方法。

不过Protothreads有时候用起来也有些麻烦,不能像RTOS那随心所欲。

好像论坛关注Protothreads人很少。

搜索了一下"Protothreads",发现全部是我发的帖子。

出0入0汤圆

发表于 2009-3-26 00:33:05 | 显示全部楼层
支持LZ,又学到新东西

出0入0汤圆

发表于 2009-3-26 08:35:12 | 显示全部楼层
能满足系统实时性要求的条件是: (当且仅当)TaskA 、TaskB、TaskC三个任务的运行时间之和要小于系统实时响应的时间要求。

协程式的调度就是这个毛病,或者说不能做抢占。

出0入0汤圆

发表于 2009-6-2 11:02:19 | 显示全部楼层
在8位机里比较合适,注意一下时间就好了

出0入0汤圆

发表于 2010-10-30 21:29:18 | 显示全部楼层
在  #define LC_STEP_SET(s,n) s = __LINE__ + n; case __LINE__ + n:  这条语句中, “__LINE__ ”是在哪里定义的?

出0入0汤圆

发表于 2010-11-1 15:20:52 | 显示全部楼层
收下,谢谢。

出0入0汤圆

发表于 2010-11-2 17:06:15 | 显示全部楼层
马克

出0入0汤圆

发表于 2010-11-11 17:53:27 | 显示全部楼层
protothread很有用的,
正在学习ING

出0入0汤圆

发表于 2010-11-11 19:32:36 | 显示全部楼层
我也在用protothread,已经用在好几个产品上了

出0入0汤圆

发表于 2010-11-11 19:34:57 | 显示全部楼层
回复【9楼】lyhui2008
-----------------------------------------------------------------------

你好,可以传几个prototehread的实例学习下吗?谢谢
例如键盘。。。

出0入0汤圆

发表于 2011-12-21 03:13:49 | 显示全部楼层
学习下

出0入0汤圆

发表于 2012-5-20 14:29:28 | 显示全部楼层
优点挺多的

出0入0汤圆

发表于 2012-5-20 18:40:33 | 显示全部楼层
lwv9t18j 发表于 2009-6-2 11:02
在8位机里比较合适,注意一下时间就好了

__LINE__是预定义宏,表示当前代码在当前文件的行数,参看:http://hi.baidu.com/ppdouble_c/b ... a2e397471064b8.html

出0入0汤圆

发表于 2012-5-20 18:41:09 | 显示全部楼层
yoko 发表于 2010-10-30 21:29
在  #define LC_STEP_SET(s,n) s = __LINE__ + n; case __LINE__ + n:  这条语句中, “__LINE__ ”是在 ...

见楼上,点错了,楼上息怒

出0入0汤圆

发表于 2012-5-26 14:15:23 | 显示全部楼层
嗯,不错不错,学习了

出0入0汤圆

发表于 2012-5-26 14:15:53 | 显示全部楼层
下次在单片机上实现看看,可惜现在论坛的附件不能下载啊,太痛苦了日

出0入0汤圆

发表于 2012-9-7 16:47:36 | 显示全部楼层
very good job !!!

出0入0汤圆

发表于 2012-9-7 16:53:03 | 显示全部楼层
学习了,很不错     

出0入0汤圆

发表于 2012-9-25 09:18:12 | 显示全部楼层
mark,,,,,,,,,,,,,,,,,,,

出0入0汤圆

发表于 2014-6-21 20:57:28 | 显示全部楼层
thank you!

出0入0汤圆

发表于 2014-9-30 08:40:56 | 显示全部楼层
不知道在8位机里面试用普遍不,还是学习一下

出0入0汤圆

发表于 2014-12-10 22:43:28 | 显示全部楼层
就单片机来讲,状态机够用了,找不出非要用RTOS的理由来

出0入0汤圆

发表于 2016-1-30 16:46:52 | 显示全部楼层
学习,谢谢!!!

出0入0汤圆

发表于 2016-3-18 10:04:05 | 显示全部楼层
恍然大悟,

出0入0汤圆

发表于 2016-3-19 12:30:14 | 显示全部楼层
最近也在研究这个,学习下,有更多的例程就好了
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 22:14

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表