Protothreads

A novel way to implement multithreaded programs in small microcontrollers

From Adam Dunkels' website: "Protothreads are extremely lightweight stackless threads designed for severely memory constrained systems, such as small embedded systems or wireless sensor network nodes. Protothreads provide linear code execution for event-driven systems implemented in C. Protothreads can be used with or without an underlying operating system to provide blocking event-handlers. Protothreads provide sequential flow of control without complex state machines or full multi-threading."

Recently I had to implement a design where the microcontroller had to keep track of several tasks at once, but each task mostly consisted of waiting for some I/O event before it would go to the next step.

My usual approach would have been to implement this as one big while loop calling several handlers or tasks, and each handler or task being a state machine. Now while state machines work well, they can become a bit complicated to read and keep track of. (Although that depends on who you're asking, as this discussion of Protothreads vs State machines shows.)

Personally I find that protothreads are a very nice way to hide away the compexities of a state machines (eg a switch statement), and that it allows me to write the code in a very easy to understand top-down manner.

I will not go into the details on how protothreads work or give examples here, as Adam Dunkels' website gives more than enough examples and insight on how protothreads work.

When I finally understood how the protothreads work, it was like a whole new world opened up for me. I learnt things about C that I did not really know was possible. (And to be honest, I think there are a lot of purists out there who would argue that it really should not even be allowed in C).

Insight into protothreads reveal that you have to be very careful in incorporating switch statements in a protothread. It can be done, but one must take care that your protothread does not span accross the protothread macros. I found it best to use if-elseif-elseif .... else constructs.

For example, the following will not work:

PT_THREAD(mythread(struct pt* p, int a))
{
PT_BEGIN(P);

switch(a)
{
case 0:
PT_YIELD();
case 1:
do_something();
PT_YIELD();
}
PT_END(p);
}

For me to get it working properly, I did the following:

PT_THREAD(mythread(struct pt* p, int a))
{
PT_BEGIN(P);

if (a==0)
{
PT_YIELD();
}
else if (a==1)
{
do_something();
PT_YIELD();
}
PT_END(p);
}

Another pitfall is that your local variables will not really work. I solved this by declaring all my local variables as static variables, so that every variable keeps its value as the thread exists and gets executed later again. Not quite sure it everybody would agree, but it worked for me. Instead of using local variables, one could also pass a "context" to every thread, which would effectively replace the local variables:

struct mythread_context 
{
int a;
int b;
int c;
int d;
};

PT_THREAD(mythread(struct pt* p, struct mythread_context* c))
{
PT_BEGIN(P);

if (c->a==0)
{
c->b++;
PT_YIELD();
}
else if (a==1)
{
c->d--;
do_something();
PT_YIELD();
}
PT_END(p);
}

In the next few weeks I will be expanding a bit on some of the things I did around protothreads. Hope you find protothreads as useful as I did.

armand Monday 07 April 2014 - 4:16 pm | | Programming

No comments

(optional field)
(optional field)
Remember personal info?
Small print: All html tags except <b> and <i> will be removed from your comment. You can make links by just typing the url or mail-address.