搜索
bottom↓
回复: 91

推荐protothread多任务,很适合lgt

  [复制链接]

出0入0汤圆

发表于 2012-5-25 18:09:22 | 显示全部楼层 |阅读模式
我用protothread做了几个产品,确实好用,所以吐血推荐在lgt上用这个种语法机制,
可能在几百个字节RAM的情况下,只有靠protothread才能较好的实现多任务了。
因为protothread一个任务只需要2个字节的RAM。

----------下面是转的资料--------------------------------------------------------------
利用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被调用时,直接跳到阻塞位置执行,再次判断运行条件是否满足,并执行后续程序或继续阻塞。

---------------------------------------------------------------------
实际上稍加几个宏,就可以写成这种任务格式,更直观。

PT_TSK_Create(initlcdtask)
unsigned char initlcdtask(struct pt *pt)
{
        static int k;
_SS
        GPIOG->BRR=LCDRST;
        WaitX(100);
        GPIOG->BSRR=LCDRST;
        WaitX(50);
        initial();   
        WaitX(50);
        cls();
        WaitX(50);        
_EE
}

出0入0汤圆

发表于 2012-5-25 19:46:37 | 显示全部楼层
迷茫与无奈!

看不懂!

出0入0汤圆

发表于 2012-5-25 21:57:12 来自手机 | 显示全部楼层
想知道下次程序是怎么找到阻塞点的。难道那个begin最后会编译成一些Goto语句跳转到阻塞点?
来自:amoBBS 阿莫电子论坛 Android客户端

出0入0汤圆

发表于 2012-5-25 22:27:04 | 显示全部楼层
本帖最后由 ljt80158015 于 2012-5-26 09:03 编辑

感觉挺麻烦的!

《时间触发嵌入式设计》  这个我觉得更好!

出0入0汤圆

发表于 2012-5-25 22:38:34 | 显示全部楼层
学过Protothread,但没用到。。。。。

出0入0汤圆

发表于 2012-5-25 23:16:01 | 显示全部楼层
曾经也读过protothread,但一直没实际用过,感觉是依靠任务程序主动判断条件让出CPU,劳烦楼主说说对于没有栈切换,运用时需要注意哪些呢

出0入0汤圆

发表于 2012-5-26 08:41:19 | 显示全部楼层
可以收藏,确实资源占用是我见过的最低的一种OS,不过如果仅仅是需要程序主动让出CPU,那么稳定性就不会太好。并且这样每个线程执行的时间并不固定。

出0入0汤圆

发表于 2012-5-26 09:41:55 | 显示全部楼层
收藏  有时间研究一下  看上去比 《时间触发嵌入式设计》  消耗RAM低很多   不知道实用性怎么样

出0入0汤圆

发表于 2012-5-26 09:53:22 | 显示全部楼层
能不能结合你的项目写个简单能跑的例子啊

出0入0汤圆

发表于 2012-5-26 09:55:56 | 显示全部楼层
wear778899 发表于 2012-5-26 09:41
收藏  有时间研究一下  看上去比 《时间触发嵌入式设计》  消耗RAM低很多   不知道实用性怎么样 ...

《时间触发嵌入式设计》  也没什么内存消耗吧!~

出0入0汤圆

 楼主| 发表于 2012-5-26 12:08:51 | 显示全部楼层
protothread 的核心是:

1)每个任务的本质都是一个基于状态机的函数,多任务并行实际上是不断执行各个基于状态机的函数
...这一点,我想大家都很清楚。

2)对于任务函数:自动实现状态机,无需编程者主动去设计状态机的各个状态,巧妙的应用了c 编译器的特点,行号 __LINE__ 就用作状态,每个代码行都自带了一个隐含状态,用行号作为状态,就无需人为再设计状态了!这是protothread的精华,也是令人震撼的思想。

因此任务函数本质上是状态机,但是又被精妙的自动实现了,无需编程人员以状态机的方式思考,极大的提高了程序代码的自然度。

其实protothread的真正意义,就是这两条, 其他所代码,都是辅助其实现而已。

出0入0汤圆

发表于 2012-5-26 14:09:46 | 显示全部楼层
进来学习支持

出0入0汤圆

发表于 2012-5-26 14:56:17 | 显示全部楼层
不错的思路,顶用

出0入0汤圆

发表于 2012-5-26 22:07:44 | 显示全部楼层
ljt80158015 发表于 2012-5-26 09:55
《时间触发嵌入式设计》  也没什么内存消耗吧!~


任务队列是主要的 额外RAM消耗  (区别在于 使用任务系统  和不使用任务系统的情况下,函数本身的RAM占用是固有的),官方的是一个任务7字节,要求不苛刻情况下可以取消掉delay参数,缩减到6个字节。

上文没仔细看  大致看了一眼说是  单个任务占用 2 字节。

出0入0汤圆

 楼主| 发表于 2012-5-27 10:37:33 | 显示全部楼层
protothread 是经典的状态机,只要看函数开头的switch(pt-﹥lc)  进行散转,就一清二楚了。
因此说protothread是协程,实际很不准确,  准确地说法是:   protothread 就是状态机!

protothread使用行号代替人为设置的状态,如果使用1个Byte记忆状态,那么一个任务函数内代码行数不能超过255行,所以用了1个word来记忆.因此占用了两个字节.一个任务函数内代码行数不超过65535行(这完全够了).

这就是一个protothread任务消耗2个字节的原因: 就是状态变量的消耗. 只要使用状态机机制,谁能省掉这个呢?

protothread具备状态机的所有特性, 只要能使用状态机的地方,就可以使用protothread. 即可以单独使用,也可以结合其他多任务系统机制使用,也可以和时间调度触发联合使用

而主要是好处是: protothread任务函数的代码语法,无需人工设计各个状态, 非常类似于多线程环境下的任务代码,大大降低了代码的复杂度.

出0入0汤圆

发表于 2012-5-27 10:41:43 | 显示全部楼层
http://www.mesnet.com.cn/htm/article_view.asp?id=2581
无法访问,代码在哪下载呢?

出0入0汤圆

发表于 2012-5-27 10:58:36 | 显示全部楼层
protothread学习 .
     ◆ protothread是专为资源有限的系统设计的一种耗费资源特别少并且不使用堆栈的线程模型,相比于嵌入式操作系统,其有如下优点:

      1. 以纯C语言实现,无硬件依靠性; 因此不存在移植的困难。

      2. 极少的资源需求,每个Protothread仅需要2个额外的字节;

      3. 支持阻塞操纵且没有栈的切换。

      ◆它的缺陷在于:

      1. 函数中不具备可重入型,不能使用局部变量;

      2. 按顺序判断各任务条件是否满足,因此无优先级抢占;

      3. 任务中的各条件也是按顺序判断的,因此要求任务中的条件必须是依次出现的。

      ◆ protothread的阻塞机制: 在每个条件判断前,先将当前地址保存到某个变量中,再判断条件是否成立,若条件成立,则往下

          运行;若条件不成立,则返回。

      ◆ protothread基本源码及注释:

         
[c-sharp] view plaincopyprint?
01.#ifndef PC_H  
02.#define PC_H   
03.  
04.typedef unsigned int INT16U;  
05.  
06.struct pt  
07.{  
08.  INT16U lc;   
09.};  
10.
11.
12.#define PT_THREAD_WAITING   0  
13.#define PT_THREAD_EXITED    1   
14.  
15.  
16.//初始化任务变量,只在初始化函数中执行一次就行  
17.#define PT_INIT(pt)     (pt)->lc = 0   
18.  
19.//启动任务处理,放在函数开始处  
20.#define PT_BEGIN(pt)    switch((pt)->lc) { case 0:   
21.  
22.// 等待某个条件成立,若条件不成立则直接退出本函数,下一次进入本函数就直接跳到这个地方判断     
23.// __LINE__ 编译器内置宏,代表当前行号,比如:若当前行号为8,则 s = __LINE__; case __LINE__: 展开为 s = 8; case 8:  
24.#define PT_WAIT_UNTIL(pt,condition)   (pt)->lc = __LINE__;   case __LINE__: /   
25.                                      if(!(condition))  return               
26.  
27.// 结束任务,放在函数的最后  
28.#define PT_END(pt)      }   
29.  
30.  
31.  
32.  
33.  
34.// 等待某个条件不成立        
35.#define PT_WAIT_WHILE(pt,cond)    PT_WAIT_UNTIL((pt),!(cond))   
36.  
37.// 等待某个子任务执行完成  
38.#define PT_WAIT_THREAD(pt,thread)   PT_WAIT_UNTIL((pt),(thread))     
39.  
40.// 新建一个子任务,并等待其执行完退出  
41.#define PT_SPAWN(pt,thread) /   
42.  PT_INIT((pt));            /  
43.  PT_WAIT_THREAD((pt),(thread))  
44.   
45.// 重新启动某任务执行  
46.#define PT_RESTART(pt)  PT_INIT(pt); return   
47.  
48.// 任务后面的部分不执行,直接退出  
49.#define PT_EXIT(pt)     (pt)->lc = PT_THREAD_EXITED;return   
50.
51.
52.
53.#endif  
#ifndef PC_H
#define PC_H

typedef unsigned int INT16U;

struct pt
{
  INT16U lc;  
};


#define PT_THREAD_WAITING   0
#define PT_THREAD_EXITED    1


//初始化任务变量,只在初始化函数中执行一次就行
#define PT_INIT(pt)     (pt)->lc = 0

//启动任务处理,放在函数开始处
#define PT_BEGIN(pt)    switch((pt)->lc) { case 0:

// 等待某个条件成立,若条件不成立则直接退出本函数,下一次进入本函数就直接跳到这个地方判断  
// __LINE__ 编译器内置宏,代表当前行号,比如:若当前行号为8,则 s = __LINE__; case __LINE__: 展开为 s = 8; case 8:
#define PT_WAIT_UNTIL(pt,condition)   (pt)->lc = __LINE__;   case __LINE__: /
                                      if(!(condition))  return            

// 结束任务,放在函数的最后
#define PT_END(pt)      }





// 等待某个条件不成立      
#define PT_WAIT_WHILE(pt,cond)    PT_WAIT_UNTIL((pt),!(cond))

// 等待某个子任务执行完成
#define PT_WAIT_THREAD(pt,thread)   PT_WAIT_UNTIL((pt),(thread))  

// 新建一个子任务,并等待其执行完退出
#define PT_SPAWN(pt,thread) /
  PT_INIT((pt));            /
  PT_WAIT_THREAD((pt),(thread))
  
// 重新启动某任务执行
#define PT_RESTART(pt)  PT_INIT(pt); return

// 任务后面的部分不执行,直接退出
#define PT_EXIT(pt)     (pt)->lc = PT_THREAD_EXITED;return



#endif

     

       ◆ 实例及展开源码:

         应用实例:

         
[c-sharp] view plaincopyprint?
01.static struct pt pt1,pt2;  
02.  
03.static int protothread1_flag,protothread2_flag;  
04.// ========================================   
05.// 线程1   
06.// ========================================   
07.static void protothread1(struct pt *pt)  
08.{  
09.  PT_BEGIN(pt); // 开始时调用   
10.  while(1)  
11.  {  
12.    // 应用代码   
13.    protothread1_flag = 1;  
14.    PT_WAIT_UNTIL(pt,protothread2_flag != 0); // 等待protothread2_flag 标志置位   
15.    protothread2_flag = 0;  
16.  
17.  }   
18.  PT_END(pt);  // 结束时调用   
19.}  
20.// ========================================   
21.// 线程2   
22.// ========================================   
23.static void protothread2(struct pt *pt)  
24.{  
25.  PT_BEGIN(pt);  
26.  while(1)  
27.  {  
28.    // 应用代码   
29.    protothread2_flag = 1;  
30.    PT_WAIT_UNTIL(pt,protothread1_flag != 0); // 等待protothread1_flag 标志置位   
31.  
32.    protothread1_flag = 0;  
33.  }  
34.  PT_END(pt);  
35.}  
36.// ========================================   
37.// 主函数   
38.// ========================================   
39.void main(void)  
40.{  
41.  PT_INIT(&pt1);  // 初始化   
42.  PT_INIT(&pt2);  
43.   
44.  while(1)  
45.  {  
46.    protothread1(&pt1);  
47.    protothread2(&pt2);  
48.  }  
49.}  
static struct pt pt1,pt2;

static int protothread1_flag,protothread2_flag;
// ========================================
// 线程1
// ========================================
static void protothread1(struct pt *pt)
{
  PT_BEGIN(pt); // 开始时调用
  while(1)
  {
    // 应用代码
    protothread1_flag = 1;
    PT_WAIT_UNTIL(pt,protothread2_flag != 0); // 等待protothread2_flag 标志置位
    protothread2_flag = 0;

  }  
  PT_END(pt);  // 结束时调用
}
// ========================================
// 线程2
// ========================================
static void protothread2(struct pt *pt)
{
  PT_BEGIN(pt);
  while(1)
  {
    // 应用代码
    protothread2_flag = 1;
    PT_WAIT_UNTIL(pt,protothread1_flag != 0); // 等待protothread1_flag 标志置位

    protothread1_flag = 0;
  }
  PT_END(pt);
}
// ========================================
// 主函数
// ========================================
void main(void)
{
  PT_INIT(&pt1);  // 初始化
  PT_INIT(&pt2);
  
  while(1)
  {
    protothread1(&pt1);
    protothread2(&pt2);
  }
}

        线程1,2的展开式:

         
[c-sharp] view plaincopyprint?
01.// ========================================   
02.// 线程1   
03.// ========================================   
04.static void protothread1(struct pt *pt)  
05.{  
06.  // PT_BEGIN(pt);展开   
07.  switch(pt->lc)  
08.  {  
09.    case 0:  
10.    ;     
11.      
12.    while(1)  
13.    {  
14.      protothread1_flag = 1;  
15.        
16.      // PT_WAIT_UNTIL(pt,protothread2_flag != 0);展开   
17.      // 条件判断部分,条件不成立,则调度   
18.      pt->lc = 26;   // 假定当前为26行   
19.      case 26:  
20.      if(protothread2_flag == 0)  
21.        return;      // 若protothread2_flag未发生,返回   
22.  
23.      protothread2_flag = 0;  
24.    }  
25.   
26.  // PT_END(pt); 对应switch   
27.  }  
28.}  
29.  
30.// ========================================   
31.// 线程2   
32.// ========================================   
33.static void protothread2(struct pt *pt)  
34.{  
35.  switch(pt->lc)  
36.  {  
37.    case 0:  
38.    ;            
39.    while(1)  
40.    {  
41.      protothread2_flag = 1;  
42.      pt->lc = 44;  
43.      case 44:  
44.      if(protothread1_flag == 0)  
45.        return;  
46.        
47.      myFunc2();  
48.      protothread1_flag = 0;  
49.    }  
50.  }  
51.  
52.}  



看这篇文章就清晰多了!~

出0入0汤圆

发表于 2012-5-27 11:08:16 | 显示全部楼层
◆它的缺陷在于:

      1. 函数中不具备可重入型,不能使用局部变量;



不是很明白,既然不抢占,为何不能使用局部变量?

出0入0汤圆

 楼主| 发表于 2012-5-27 11:40:08 | 显示全部楼层
ljt80158015 发表于 2012-5-27 11:08
◆它的缺陷在于:

      1. 函数中不具备可重入型,不能使用局部变量;

在状态机函数内部,可以临时使用局部变量,但是一旦退出函数后,这些变量值就丢失了。

出0入0汤圆

发表于 2012-5-27 11:47:40 | 显示全部楼层
smset 发表于 2012-5-27 11:40
在状态机函数内部,可以临时使用局部变量,但是一旦退出函数后,这些变量值就丢失了。 ...



protothread1()
protothread2()
中不能使用局部变量。

具体的应用:
myFunc1()
myFunc2()
这些函数中应该能使用局部变量的吧?

出0入0汤圆

发表于 2012-5-28 13:15:10 | 显示全部楼层
protothread 的下载地址
http://dunkels.com/adam/pt/

出0入0汤圆

发表于 2012-5-30 00:10:36 | 显示全部楼层
看得一知半解的,要是提供个简单的程序实验就好了,
平时写程序也是用状态机原理,只是没有规范,
想看看按protothread写法会是怎样的,不知LZ是否可提供完整简单实例(比如控制3个LED分别0.5s、1s、2s闪烁)

出0入0汤圆

发表于 2012-5-30 07:58:29 | 显示全部楼层
楼上说正合我意!另外中断怎么配合呢?
头像被屏蔽

出0入0汤圆

发表于 2012-5-30 13:20:16 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入0汤圆

发表于 2012-5-30 13:23:01 | 显示全部楼层
本帖最后由 ljt80158015 于 2012-5-30 15:47 编辑

它的缺陷在于:

      1. 函数中不具备可重入型,不能使用局部变量;


大体明白了

protothread1()
protothread2()
中不能使用局部变量。

具体的应用:
myFunc1()
myFunc2()
能使用局部变量。



很不错的操作系统!

出0入0汤圆

发表于 2012-5-30 19:56:46 | 显示全部楼层
bbs2009 发表于 2012-5-30 13:20
这有实例:
AVR实验板基于Protothreads下一个歌曲实验
http://www.ouravr.com/thread-963585-1-1.html

感谢

出0入0汤圆

发表于 2012-7-5 16:39:10 | 显示全部楼层
这个相当的好,比RTOS省RAM,比状态机好用,还特别好理解与应用。我有很多案子都是采用这种方式的。

出0入0汤圆

发表于 2012-7-5 16:47:35 | 显示全部楼层
按我的理解,这种系统,在任务函数里,使用堆栈本地变量很麻烦,因为再次进入函数时,是直接switch到上个退出点的,就是说,前面初始化的过程,可能都被跳过了,实际应用还是受到了不少限制

出0入0汤圆

发表于 2012-7-5 17:00:40 | 显示全部楼层
这个,是伪多任务吧,思路非常好

出0入0汤圆

发表于 2012-7-6 15:47:22 来自手机 | 显示全部楼层
protothread1()并不是不能使用局部变量,而是应当小心使用,在相临两个阻塞点之间是可用的,但不能跨阻塞点使用

出0入296汤圆

发表于 2012-7-6 18:13:46 | 显示全部楼层
这个思路很好,值得推广。

出0入0汤圆

发表于 2012-7-7 21:11:24 | 显示全部楼层
这个我也研究过,不错;在小电子产品里用用很好。楼主能提出来让大家讨论,很好!

出0入4汤圆

发表于 2012-7-10 18:48:40 | 显示全部楼层
mark。学习下。

出0入0汤圆

发表于 2012-7-19 21:34:43 | 显示全部楼层
这个要纪念一下!

出0入0汤圆

发表于 2012-7-27 08:51:04 | 显示全部楼层
mark下,留待后用

出75入0汤圆

发表于 2012-7-31 19:05:05 | 显示全部楼层
下来看看代码,借鉴下。

出0入0汤圆

发表于 2012-8-2 22:01:40 | 显示全部楼层
收藏看看。。

出0入0汤圆

发表于 2012-8-2 22:20:49 | 显示全部楼层
Gorgon_Meducer 发表于 2012-7-6 18:13
这个思路很好,值得推广。


连傻孩子大侠都说好的,不好好学习就对不起自己了

出0入0汤圆

发表于 2012-8-2 22:42:51 | 显示全部楼层

还没看,先问个问题,如果多文件编译,存在相同的行号怎么办呢?

出0入0汤圆

 楼主| 发表于 2012-8-8 12:34:31 | 显示全部楼层
ijlc1314 发表于 2012-8-2 22:42
还没看,先问个问题,如果多文件编译,存在相同的行号怎么办呢?

没有问题的,行号只是拿来作为状态值的,每个状态机都用的是自己的状态变量。又不是共同一个状态变量啊。

出0入0汤圆

发表于 2012-8-8 12:46:12 | 显示全部楼层
smset 发表于 2012-8-8 12:34
没有问题的,行号只是拿来作为状态值的,每个状态机都用的是自己的状态变量。又不是共同一个状态变量啊。 ...

嗯,是的,刚开始还没有去看代码,现在看了,知道是怎么回事了

出0入0汤圆

发表于 2012-8-8 13:06:46 | 显示全部楼层
研究研究!

出0入0汤圆

发表于 2012-9-18 02:38:39 来自手机 | 显示全部楼层
能用行号当状态机变量,这想法太厉害了

出0入0汤圆

发表于 2012-9-18 19:48:56 | 显示全部楼层
smset 发表于 2012-5-26 12:08
protothread 的核心是:

1)每个任务的本质都是一个基于状态机的函数,多任务并行实际上是不断执行各个基 ...

不错,正在研究中

出0入0汤圆

发表于 2012-9-18 20:00:13 | 显示全部楼层
不一定要完全用这个模式。。学习下,在自己的代码里用就可以了。。相当 的好用。

出0入0汤圆

发表于 2012-9-18 20:12:27 | 显示全部楼层
状态机!

PT不光适合小项目使用,大项目也适合!

出0入0汤圆

发表于 2012-9-23 14:12:54 | 显示全部楼层
知道是怎么回事了,行号当状态机变量

出0入0汤圆

发表于 2012-9-23 14:25:40 | 显示全部楼层
先学习 一下

出0入0汤圆

发表于 2012-9-25 20:36:43 | 显示全部楼层
怪不得似曾相识的感觉,原来是uip的作者写的

出0入4汤圆

发表于 2012-9-25 23:02:12 | 显示全部楼层
2)对于任务函数:自动实现状态机,无需编程者主动去设计状态机的各个状态,巧妙的应用了c 编译器的特点,行号 __LINE__ 就用作状态,每个代码行都自带了一个隐含状态,用行号作为状态,就无需人为再设计状态了!这是protothread的精华,也是令人震撼的思想。

这句放话让我明白之所以然了,把行号用作状态机,有意思
以前还不知道,C编译器获取行号
谢谢!

出0入0汤圆

发表于 2012-9-26 12:55:17 | 显示全部楼层
LZ能不能放个完整的例子工程出来,那一楼说的那个例子无法下载

出0入0汤圆

发表于 2012-9-26 13:19:34 | 显示全部楼层
重点学习对象!

出0入0汤圆

发表于 2012-10-17 10:02:17 | 显示全部楼层
不错睥思路,但没试过。

出0入0汤圆

发表于 2012-10-18 11:01:32 | 显示全部楼层
very good job !!!

出0入0汤圆

发表于 2012-11-10 10:18:38 | 显示全部楼层
精华之处是状态机用__LINE__来switch

出0入0汤圆

发表于 2012-11-10 11:27:22 | 显示全部楼层
好思路,贴子不能沉了哦!

出0入0汤圆

发表于 2012-11-10 14:07:30 | 显示全部楼层
状态机用__LINE__来switch
mark!

出0入0汤圆

发表于 2012-11-12 10:19:41 | 显示全部楼层
关注中~

出0入0汤圆

发表于 2012-11-12 22:48:33 | 显示全部楼层
看看

出0入0汤圆

发表于 2012-11-12 22:52:40 来自手机 | 显示全部楼层
好东西啊

出0入0汤圆

发表于 2012-11-13 00:07:24 | 显示全部楼层
用了 一下下 却是经典。。。

出0入0汤圆

发表于 2012-11-13 02:54:54 | 显示全部楼层
标记后慢慢学习

出0入0汤圆

发表于 2012-11-13 07:17:03 | 显示全部楼层
ijlc1314 发表于 2012-8-2 22:42
还没看,先问个问题,如果多文件编译,存在相同的行号怎么办呢?

基于Switch的方法行号是一个case后边的值。
所以,只要一个函数的switch内case的行号不重复就没有问题;而C恰好要求一个函数必然最终完全实现在一个C文件中,并且行号天然递增。
除非,对代码有混淆要求,把所有代码弄成一行,C这样可行,否则良好格式化的代码是符合各种约束要求的。没问题。

另外,如果编译器用GCC或者兼容GCC的Lable As Address的语法的话,可以用这个来实现,switch的连续比较会变成类似Goto的跳转,效率高的多。

出0入0汤圆

发表于 2012-11-13 07:59:07 | 显示全部楼层
认真学习,谢谢!!!

出0入0汤圆

发表于 2012-11-13 08:37:25 | 显示全部楼层
研究研究!楼主上传资料吧

出0入0汤圆

发表于 2012-11-13 23:49:58 | 显示全部楼层
http://embeddedgurus.com/state-s ... sus-state-machines/

E文好的可以看看这个,国外博客上关于proto和状态机的对比

出0入0汤圆

发表于 2013-1-11 09:46:55 | 显示全部楼层
有时间看一下

出0入17汤圆

发表于 2013-1-11 19:57:26 | 显示全部楼层
本帖最后由 foxpro2005 于 2013-1-11 20:31 编辑

好贴 ,这个架构不错,值得学习...
我来把原作者的原版上传上来吧...
原连接:http://dunkels.com/adam/pt/download.html

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-1-12 08:21:23 来自手机 | 显示全部楼层
想法值得借鉴啊

出0入0汤圆

发表于 2013-8-1 14:52:29 | 显示全部楼层
收藏,记号

出0入0汤圆

发表于 2013-8-18 13:30:51 | 显示全部楼层
收藏,以待学习

出0入0汤圆

发表于 2013-11-4 09:57:00 | 显示全部楼层
好东西,mark一下

出0入10汤圆

发表于 2013-12-19 15:01:25 | 显示全部楼层
MARK!最近也想用这个试试了

出0入0汤圆

发表于 2014-2-3 20:52:17 来自手机 | 显示全部楼层
学习学习,谢谢

出0入4汤圆

发表于 2014-2-3 23:34:29 | 显示全部楼层
收藏,谢谢!

出0入0汤圆

发表于 2014-2-10 14:46:11 | 显示全部楼层
mark~~~~ 学习下~

出0入0汤圆

发表于 2014-2-12 22:54:34 | 显示全部楼层
protothread任务调度器,  _LINE_作为状态标志

出0入0汤圆

发表于 2014-3-27 19:07:06 | 显示全部楼层
MARK一下啊

出0入0汤圆

发表于 2014-3-30 15:41:51 | 显示全部楼层
学习一下,正好想学习状态机

出0入0汤圆

发表于 2014-5-4 09:39:12 | 显示全部楼层
LZ,你好。有实际应用到项目过吗,稳定性怎么样,是否有实际应用的工程实例可供参考?

出0入0汤圆

发表于 2014-5-4 15:24:18 | 显示全部楼层
LZ,你好!
static int clock_time(void) {   struct timeval tv;   struct timezone tz;      gettimeofday(&tv, &tz);    return tv.tv_sec * 1000 + tv.tv_usec / 1000; }
这个函数中的gettimeofday函数是否在单片机程序中可用?

出0入0汤圆

发表于 2014-5-5 15:27:58 | 显示全部楼层
楼主,PT_SEM.h这个问题能解么,还有其相关的例程

出0入0汤圆

 楼主| 发表于 2014-5-5 16:21:27 | 显示全部楼层
回楼上,建议采用小小调度器,更简洁易用:

http://www.amobbs.com/thread-5508723-1-1.html

出0入0汤圆

发表于 2014-6-10 10:39:11 | 显示全部楼层
楼主又被我抓到了,赶紧完善下调度器!!

出0入0汤圆

发表于 2014-8-9 17:28:27 | 显示全部楼层
mark!

出0入0汤圆

发表于 2014-8-9 18:29:27 | 显示全部楼层
关注中,各有好处。。。

出0入0汤圆

发表于 2014-9-18 22:18:24 | 显示全部楼层
略调啊   这个看起来很高大上 但是实际貌似使用过程中需要考虑的太多了  一不小心就死了

出0入0汤圆

发表于 2015-1-15 19:43:03 | 显示全部楼层
受教了,switch 和循环的用法好巧妙

出0入0汤圆

发表于 2015-2-7 16:34:19 | 显示全部楼层
好东西,学习了

出0入0汤圆

发表于 2015-4-2 19:07:00 | 显示全部楼层
内存确实很省

出50入8汤圆

发表于 2015-7-12 10:55:46 来自手机 | 显示全部楼层
PT就是嵌入式开发者的福音,用过都说好

出0入0汤圆

发表于 2015-9-16 12:33:31 | 显示全部楼层
pt的这个方法不错,我用它来写中断ISR,不过main没有用它的结构。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子论坛 ( 公安交互式论坛备案:44190002001997 粤ICP备09047143号 )

GMT+8, 2022-6-25 09:26

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

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