A reliable communication protocolIn computer communication, there are many cases in which a protocol needs to cope with packet loss. In order to provide a reliable channel, lost packets must be retransmitted. This example shows a simple communication protocol that uses (the absence of) acknowledgment packets to detect a lost packet. If a lost packet is detected, the packet is retransmitted.
The example shows two protothreads, one for the sender and one for the receiver.
#include "pt.h"
PT_THREAD(sender(struct pt *pt))
{
PT_BEGIN(pt);
do {
send_packet();
/* Wait until an ackowledgement has been received, or until the
timer expires. If the timer expires, we should send the packet
again. */
timer_set(&timer, TIMEOUT);
PT_WAIT_UNTIL(pt, acknowledgment_received() ||
timer_expired(&timer));
} while(timer_expired(&timer));
PT_END(pt);
}
PT_THREAD(receiver(struct pt *pt))
{
PT_BEGIN(pt);
/* Wait until a packet has been received, and send an
acknowledgment. */
PT_WAIT_UNTIL(pt, packet_received());
send_acknowledgement();
PT_END(pt);
}
复制代码
Delays - text scrolling on an LCD panelProtothreads can be used for introducing delays inside a function, without using a full threading model. The following example shows a function writing text to a one-line LCD panel. If the text is longer than the size of the panel, the text should be scrolling in from the right.
#include "pt.h"
#include "timer.h"
struct state {
char *text;
char *scrollptr;
struct pt pt;
struct timer timer;
};
PT_THREAD(display_text(struct state *s))
{
PT_BEGIN(&s->pt);
/* If the text is shorter than the display size, show it right
away. */
if(strlen(s->text) <= LCD_SIZE) {
lcd_display_text(s->text);
} else {
/* If the text is longer than the display, we should scroll in the
text from the right with a delay of one second per scroll
step. We do this in a for() loop, where the loop variable is
the pointer to the first character to be displayed. */
for(s->scrollptr = s->text;
strlen(s->scrollptr) > LCD_SIZE;
++s->scrollptr) {
lcd_display_text(s->scrollptr);
/* Wait for one second. */
timer_set(&s->timer, ONE_SECOND);
PT_WAIT_UNTIL(&s->pt, timer_expired(&s->timer));
}
}
PT_END(&s->pt);
}
复制代码
A code lockThis example is a bit more complicated and shows how to implement a simple code lock - the kind of device that is placed next to doors and that you have to push a four digit number into in order to unlock the door.
The code lock waits for key presses from a numeric keyboard and if the correct code is entered, the lock is unlocked. There is a maximum time of one second between each key press, and after the correct code has been entered, no more keys must be pressed for 0.5 seconds before the lock is opened.
The code uses external functions for timers and for checking the keyboard. These functions are included in the full source code for this example, which is included in the downloadable tarball.
/*
* This is the code that has to be entered.
*/
static const char code[4] = {'1', '4', '2', '3'};
/*
* Declaration of the protothread function implementing the code lock
* logic. The protothread function is declared using the PT_THREAD()
* macro. The function is declared with the "static" keyword since it
* is local to this file. The name of the function is codelock_thread
* and it takes one argument, pt, of the type struct pt.
*
*/
static
PT_THREAD(codelock_thread(struct pt *pt))
{
/* This is a local variable that holds the number of keys that have
* been pressed. Note that it is declared with the "static" keyword
* to make sure that the variable is *not* allocated on the stack.
*/
static int keys;
/*
* Declare the beginning of the protothread.
*/
PT_BEGIN(pt);
/*
* We'll let the protothread loop until the protothread is
* expliticly exited with PT_EXIT().
*/
while(1) {
/*
* We'll be reading key presses until we get the right amount of
* correct keys.
*/
for(keys = 0; keys < sizeof(code); ++keys) {
/*
* If we haven't gotten any keypresses, we'll simply wait for one.
*/
if(keys == 0) {
/*
* The PT_WAIT_UNTIL() function will block until the condition
* key_pressed() is true.
*/
PT_WAIT_UNTIL(pt, key_pressed());
} else {
/*
* If the "key" variable was larger than zero, we have already
* gotten at least one correct key press. If so, we'll not
* only wait for the next key, but we'll also set a timer that
* expires in one second. This gives the person pressing the
* keys one second to press the next key in the code.
*/
timer_set(&codelock_timer, 1000);
/*
* The following statement shows how complex blocking
* conditions can be easily expressed with protothreads and
* If we continued from the PT_WAIT_UNTIL() statement without
* the timer expired, we don't open the lock.
*/
if(!timer_expired(&codelock_timer)) {
printf("Key pressed during final wait, code lock locked again.\n");
} else {
/*
* If the timer expired, we'll open the lock and exit from the
* protothread.
*/
printf("Code lock unlocked.\n");
PT_EXIT(pt);
}
}
}
/*
* Finally, we'll mark the end of the protothread.
*/
PT_END(pt);
}
复制代码
The bounded buffer with protothread semaphoresThe following example shows how to implement the bounded buffer problem using the protothreads semaphore library. The example uses three protothreads: one producer() protothread that produces items, one consumer() protothread that consumes items, and one driver_thread() that schedules the producer and consumer protothreads.
Note that there is no need for a mutex to guard the add_to_buffer() and get_from_buffer() functions because of the implicit locking semantics of protothreads - a protothread will never be preempted and will never block except in an explicit PT_WAIT statement.
#include "pt-sem.h"
#define NUM_ITEMS 32
#define BUFSIZE 8
static struct pt_sem full, empty;
static
PT_THREAD(producer(struct pt *pt))
{
static int produced;
PT_BEGIN(pt);
for(produced = 0; produced < NUM_ITEMS; ++produced) {
A radio driver written both with protothreads and events
This example shows an interrupt handler in a device driver for a TR1001 radio chip. The driver receives incoming data in bytes and constructs a frame that is covered by a CRC checksum. The driver is implemented both with protothreads and with an explicit state machine. The state machine has 11 states and is implemented using the C switch() statement. In contrast, the protothreads-based implementation does not have any explicit states.
The flow of control in the state machine-based implementation is quite hard to follow from inspection of the code, whereas the flow of control is evident in the protothreads based implementation.