Protothreads is a very
light-weight, stackless, threading library written entirely as C macros
by Adam Dunkels. As such, it is trivial to move to PIC32.
Adam Dunkels' documentation
is very good
and easy to understand.
There is support for a thread to wait for an event, spawn a thread,
and use semaphores. The Protothreads system is a cooperative multithread
system. As such, there is no thread preemption. All thread switching is at explicit wait or yield
statement. There is no scheduler. You can write your own, or just use a
simple round-robin scheme, calling each thread in succession in main
.
Because there is no preemption, handling shared variables is easier
because you always know exactly when a thread switch will occur. Because
there is no separate stack for each thread, the memory footprint is
quite small, but using automatic (stack) local variables must be avoided. You can use static
local variables. Protothreads uses a switch-statement type construct to handle thread switching, so it is not possible to embed a thread-wait statement in a switch stanza.
You must read sections 1.3-1.6 of the reference manual to see all of the implementation details.
I hacked some of Dunkels' examples. To run the examples you need to download software from Dunkels' site or use a local copy. Most examples also require a UART connection to a terminal, as explained in a project further down the page.
-- The first example
has two threads executing at a rate based on a hardware timer ISR,
which generates a millisecond time counter. Each thread yields for a
waiting time and when executing prints the thread number and time.
Thread 1 executes once per second. Thread 2 executes every 4 seconds.
Main just sets up the timer ISR and UART, then inintialzes the threads
and schedules them.
-- The second example
has three threads.
Threads 1 and 2 wait on semaphores, each of which is signaled by the
other thread. The two threades therefore alternate. Thread 3 just
executes every few seconds. I defined an new macro to make it easier for
a thread to wait for a specific time. PT_YIELD_TIME(wait_time)
takes the wait time parameter and uses a local variable and the
millisceond timer variable to yield the processor to another thread for wait_time
milliseconds. The second example also has a small routine to compute
approximate microseconds since reset and return it as a 64-bit long long int
.
#define PT_YIELD_TIME(delay_time) \ do { static int time_thread; \ PT_YIELD_UNTIL(pt, milliSec >= time_thread); \ time_thread = milliSec + delay_time ;} while(0);-- The third example has three threads. Threads 1 and 2 wait on semaphores, each of which is signaled by the other thread. The two threads therefore alternate. Thread 3 takes input from a serial terminal. The actual input routine is a thread which is spawned by thread 3. Thread 3 then waits for the input thread to terminate which it does when the human presses <enter>. The input thread yields the processor while it is waiting for the slow human to type each character, so other threads do not stall. The key statment is below which causes protothreads to wait/yield on a hardware flag. The flag is defined as part of
plib.h
. PT_YIELD_UNTIL(pt, UARTReceivedDataIsAvailable(UART2));
Note that the spawn command
PT_SPAWN(pt, &pt_input, GetSerialBuffer(&pt_input) );
initializes the input thread and schedules it. The three parameters are the current thread structure, a pointer to the spawned thread, and the actual thread function. If more than one thread is using serial input, then the spawn command should be surrounded by semaphore wait/signal commands because
GetSerialBuffer
is not reentrant.-- The fourth example investigates non-blocking UART transmit. In a printf, there is a waitloop for each character. We can replace that with thread yield on a per character basis. Doing this speeds up processing a factor of 2 or so. But how fast is the swtich between two threads?... Read more »