您的位置:

了解Protothread—嵌入式系统中的轻量级协程技术

随着嵌入式系统的广泛应用,对系统的可靠性和实时性等需求也越来越高。而协程技术能够很好地解决这些问题。Protothread就是一种轻量级协程技术,下面我们将从多个方面对其进行详细的阐述。

一、Protothread概述

Protothread是一种基于C语言的、轻量级的、面向事件驱动的协程技术,它的核心思想是将协程处理尽可能小的处理单元,并使用状态变量指示协程的运行进度,从而向外部事件响应和传达执行进度。

Protothread中的协程都被视为很简单、几乎等价于无状态机的程序代码。这些协程的特征在于它们能够暂停它们的状态机,并在下一次调用时从上次离开的地方继续执行。协程的执行顺序决定了程序代码的执行顺序,这种执行顺序已经预先定义的,从而协程就成为了轻量级进程。

Protothread可以使用非抢占式多任务处理技术来管理协程,这种技术可以将系统中每个任务的执行时间分割成明确的时间片,然后轮流执行多任务。在这个过程中,Protothread可以及时捕获外部事件并响应,从而使得整个系统保持高效和稳定。

Protothread和状态机最大的不同点在于,状态机是由开发人员定义并手动控制执行路径,而Protothread则尝试通过代码的命名和逻辑表达使得代码自动化生成。从而使得工程师不用手动维护状态机更新与之间的关系问题。

二、Protothread的实现

Protothread的实现是通过多个状态和对应的协程来实现的,每个状态对应一个协程,同时协程中还有一个表示执行状态的整型变量,用于在不同状态之间进行状态转换和执行流程控制。

我们可以将这个整型变量称作协程的“状态”变量,当协程执行完一种状态之后,就将其状态变量设置为下一种状态,从而协程就继续沿着设计的程序执行路径进行处理。

typedef struct {
  unsigned short pt_running;
  unsigned short pt_lastline;
  unsigned short pt_linedefined;
} pt_thread;

#define PT_THREAD_NAME(name_args) char name_args; \
  int name_args##_thread(pt_thread *pt, int state)

#define PT_BEGIN(pt) int PT_CONTINUATION_STATE = 0; switch(PT_CONTINUATION_STATE) { case 0:

#define PT_WAIT_UNTIL(pt, condition) PT_CONTINUATION_STATE= __LINE__; case __LINE__: \
  if(!(condition)) return 0

#define PT_END(pt) }

#define PT_YIELD(pt) do {
  PT_CONTINUATION_STATE = __LINE__; \
  return __LINE__; case __LINE__:;} while(0)

上面这段代码展示了典型的PT协程重写,它使用了会产生对嵌套函数调用的switch/case语句,这些switch和case语句用于在协程中实现对PT_STATE的调度处理。

要使用PT协程,只需要包括“PT_THREAD_NAME(name_args)”在函数前面,在你的协程函数中就可以使用在裸库中使用的PT_WAIT_UNTIL, PT_WAIT_WHILE, PT_SPAWN或PT_YIELD等等协程语句。此外,该PT_THREAD_NAME宏展开为一个带有一个单独pt_thread参数的函数。pt_thread数据结构提供了一个状态运行参数,一个最后一行运行标记和一个行定义标记。

三、Protothread的优点和适用场景

Protothread作为一种轻量级协程技术,在许多嵌入式系统中都得到了广泛的应用。

首先,Protothread运行效率高,因为它使用了非抢占式多任务处理技术,能够最大限度地提高系统效率,而且代码清晰简洁、易于维护。同时,使用Protothread的嵌入式系统在性能和实时性方面都能够得到保证。

其次,Protothread的编码方式比传统的状态机更易于实现。其中的协程特征允许在协程间共享变量,并同时使用同一复杂状态机。这种方法更容易管理并测量,因为它减少了设计中的状态转换数量。

最后,当我们面对的系统的复杂性增加时,Protothread能够轻松地扩展。开发人员只需将更多的协程加入到系统中,而不必更改已有的协程。

四、Protothread的示例

static struct pt pt1, pt2;

static PT_THREAD(protothread1(struct pt *pt)) {
  PT_BEGIN(pt);

  for(;;) {
    PT_WAIT_UNTIL(pt, input_a);
    PT_WAIT_UNTIL(pt, input_b);
    output_c = input_a + input_b;
    LCD_Display(output_c);
  }

  PT_END(pt);
}

#define STATE_IDLE 0
#define STATE_INIT 1
#define STATE_SEND 2
#define STATE_DONE 3

u16 seq_no;
u8 buffer[200];

u8 sys_buf[32];
u32 sys_buf_len;

static PT_THREAD(protothread2(struct pt *pt)) {
  PT_BEGIN(pt);

  while(1) {
    PT_WAIT_UNTIL(pt, PT_SEM_WAIT(pt, serial_rx) != 0);
    if((serial_rx_buffer.head != serial_rx_buffer.tail) && 
    ((serial_rx_buffer.head > serial_rx_buffer.tail) || 
     ((serial_rx_buffer.head == serial_rx_buffer.tail) && 
      (serial_rx_buffer.data_cnt != 0)))) {
        process_serial_rx_data();
    }
  }

  PT_END(pt);
}

上面是一些常见的Protothread代码实例,其中protothread1处理输入A和输入B,并将其相加输出到LCD显示屏上。另一个示例是protothread2,它使用PT协程将串行数据接收并交付到接受方。

这些示例展示了Protothread用于嵌入式系统的轻量级协程技术。由于Protothread使用非抢占式多任务处理技术,因此可以有效地提高系统效率,同时代码也更加简洁易懂。

五、小结

Protothread是一种轻量级协程技术,它的核心思想是将协程的处理单元尽可能小,并使用状态变量指示协程的运行进度。它在许多嵌入式系统中得到了广泛应用,具有运行效率高、编码简单等优势。掌握Protothread技术,对于嵌入式系统开发工程师来说是一个非常有意义的技能。