Multithreading + key repeat delay

* split main loop in two separate threads, using ProtoThreads 1.4 (see
  http://arduinocollective.com/2009/12/05/arduino-threading/ and
  http://www.sics.se/~adam/pt/)
* implemented simple key repeat delay: first delay is 1 second, after that it is
  only 200 ms
* changed indenting (since multithreading was such a big change in it self, it
  was relatively painless to do it now)
This commit is contained in:
Admar Schoonen 2012-01-07 01:35:54 +01:00
parent 773de62bd7
commit 38da3b00c1
13 changed files with 1484 additions and 379 deletions

View File

@ -0,0 +1,99 @@
/**
* This is a very small example that shows how to use
* protothreads. The program consists of two protothreads that wait
* for each other to toggle a variable.
*/
/* We must always include pt.h in our protothreads code. */
#include "pt.h"
/* Two flags that the two protothread functions use. */
static int protothread1_flag, protothread2_flag;
/* protothread state variables, one for each ptorothread. */
static struct pt pt1, pt2;
/**
* The first protothread function. A protothread function must always
* return an integer, but must never explicitly return - returning is
* performed inside the protothread statements.
*
* The protothread function is driven by the main loop further down in
* the code.
*/
static int protothread1(struct pt *pt)
{
/* A protothread function must begin with PT_BEGIN() which takes a
pointer to a struct pt. */
PT_BEGIN(pt);
/* We loop forever here. */
while(1) {
/* Wait until the other protothread has set its flag. */
PT_WAIT_UNTIL(pt, protothread2_flag != 0);
Serial.println("Protothread 1 running");
/* We then reset the other protothread's flag, and set our own
flag so that the other protothread can run. */
protothread2_flag = 0;
protothread1_flag = 1;
/* And we loop. */
}
/* All protothread functions must end with PT_END() which takes a
pointer to a struct pt. */
PT_END(pt);
}
/**
* The second protothread function.
* This one is almost the same as the first one.
*/
static int protothread2(struct pt *pt)
{
PT_BEGIN(pt);
while(1) {
/* Let the other protothread run. */
protothread2_flag = 1;
/* Wait until the other protothread has set its flag. */
PT_WAIT_UNTIL(pt, protothread1_flag != 0);
Serial.println("Protothread 2 running");
/* We then reset the other protothread's flag. */
protothread1_flag = 0;
/* And we loop. */
}
PT_END(pt);
}
/**
* setup() is where the protothreads are initialized.
* The state variables pt1 and pt2 holdthe state of the two protothreads.
*/
void setup()
{
/* Initialize the protothread state variables with PT_INIT(). */
PT_INIT(&pt1);
PT_INIT(&pt2);
Serial.begin(9600);
}
/**
* Finally, we have the main loop.
* This is where the protothreads are scheduled.
*/
void loop()
{
/*
* We schedule the two protothreads by repeatedly calling their
* protothread functions and passing a pointer to the protothread
* state variables as arguments.
*/
protothread1(&pt1);
protothread2(&pt2);
}

View File

@ -0,0 +1,11 @@
Installation of the Arduino implementation of Protothreads
----------------------------------------------------------
To install into the Arduino environment, please extract
this package into %arduino%/hardware/libraries/
Protothreads is now available via menu
Sketch -> Import Library -> Protothreads
Example(s) is available via menu
File -> Sketchbook -> Examples -> Library-Protothreads

View File

@ -0,0 +1,32 @@
#######################################
# Syntax Coloring Map For Ultrasound
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
PT_WAITING KEYWORD1
PT_EXITED KEYWORD1
PT_ENDED KEYWORD1
PT_YIELDED KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
PT_INIT KEYWORD2
PT_THREAD KEYWORD2
PT_BEGIN KEYWORD2
PT_END KEYWORD2
PT_WAIT_UNTIL KEYWORD2
PT_WAIT_WHILE KEYWORD2
PT_WAIT_THREAD KEYWORD2
PT_SPAWN KEYWORD2
PT_EXIT KEYWORD2
PT_RESTART KEYWORD2
PT_SCHEDULE KEYWORD2
PT_YIELD KEYWORD2
PT_YIELD_UNTIL KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2004-2005, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
* Author: Adam Dunkels <adam@sics.se>
*
* $Id: lc-addrlabels.h,v 1.4 2006/06/03 11:29:43 adam Exp $
*/
/**
* \addtogroup lc
* @{
*/
/**
* \file
* Implementation of local continuations based on the "Labels as
* values" feature of gcc
* \author
* Adam Dunkels <adam@sics.se>
*
* This implementation of local continuations is based on a special
* feature of the GCC C compiler called "labels as values". This
* feature allows assigning pointers with the address of the code
* corresponding to a particular C label.
*
* For more information, see the GCC documentation:
* http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
*
*/
#ifndef __LC_ADDRLABELS_H__
#define __LC_ADDRLABELS_H__
/** \hideinitializer */
typedef void * lc_t;
#define LC_INIT(s) s = NULL
#define LC_RESUME(s) \
do { \
if(s != NULL) { \
goto *s; \
} \
} while(0)
#define LC_CONCAT2(s1, s2) s1##s2
#define LC_CONCAT(s1, s2) LC_CONCAT2(s1, s2)
#define LC_SET(s) \
do { \
LC_CONCAT(LC_LABEL, __LINE__): \
(s) = &&LC_CONCAT(LC_LABEL, __LINE__); \
} while(0)
#define LC_END(s)
#endif /* __LC_ADDRLABELS_H__ */
/** @} */

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2004-2005, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
* Author: Adam Dunkels <adam@sics.se>
*
* $Id: lc-switch.h,v 1.4 2006/06/03 11:29:43 adam Exp $
*/
/**
* \addtogroup lc
* @{
*/
/**
* \file
* Implementation of local continuations based on switch() statment
* \author Adam Dunkels <adam@sics.se>
*
* This implementation of local continuations uses the C switch()
* statement to resume execution of a function somewhere inside the
* function's body. The implementation is based on the fact that
* switch() statements are able to jump directly into the bodies of
* control structures such as if() or while() statmenets.
*
* This implementation borrows heavily from Simon Tatham's coroutines
* implementation in C:
* http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
*/
#ifndef __LC_SWITCH_H__
#define __LC_SWITCH_H__
/* WARNING! lc implementation using switch() does not work if an
LC_SET() is done within another switch() statement! */
/** \hideinitializer */
typedef unsigned short lc_t;
#define LC_INIT(s) s = 0;
#define LC_RESUME(s) switch(s) { case 0:
#define LC_SET(s) s = __LINE__; case __LINE__:
#define LC_END(s) }
#endif /* __LC_SWITCH_H__ */
/** @} */

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2004-2005, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the protothreads library.
*
* Author: Adam Dunkels <adam@sics.se>
*
* $Id: lc.h,v 1.2 2005/02/24 10:36:59 adam Exp $
*/
/**
* \addtogroup pt
* @{
*/
/**
* \defgroup lc Local continuations
* @{
*
* Local continuations form the basis for implementing protothreads. A
* local continuation can be <i>set</i> in a specific function to
* capture the state of the function. After a local continuation has
* been set can be <i>resumed</i> in order to restore the state of the
* function at the point where the local continuation was set.
*
*
*/
/**
* \file lc.h
* Local continuations
* \author
* Adam Dunkels <adam@sics.se>
*
*/
#ifdef DOXYGEN
/**
* Initialize a local continuation.
*
* This operation initializes the local continuation, thereby
* unsetting any previously set continuation state.
*
* \hideinitializer
*/
#define LC_INIT(lc)
/**
* Set a local continuation.
*
* The set operation saves the state of the function at the point
* where the operation is executed. As far as the set operation is
* concerned, the state of the function does <b>not</b> include the
* call-stack or local (automatic) variables, but only the program
* counter and such CPU registers that needs to be saved.
*
* \hideinitializer
*/
#define LC_SET(lc)
/**
* Resume a local continuation.
*
* The resume operation resumes a previously set local continuation, thus
* restoring the state in which the function was when the local
* continuation was set. If the local continuation has not been
* previously set, the resume operation does nothing.
*
* \hideinitializer
*/
#define LC_RESUME(lc)
/**
* Mark the end of local continuation usage.
*
* The end operation signifies that local continuations should not be
* used any more in the function. This operation is not needed for
* most implementations of local continuation, but is required by a
* few implementations.
*
* \hideinitializer
*/
#define LC_END(lc)
/**
* \var typedef lc_t;
*
* The local continuation type.
*
* \hideinitializer
*/
#endif /* DOXYGEN */
#ifndef __LC_H__
#define __LC_H__
#ifdef LC_INCLUDE
#include LC_INCLUDE
#else
#include "lc-switch.inc"
#endif /* LC_INCLUDE */
#endif /* __LC_H__ */
/** @} */
/** @} */

View File

@ -0,0 +1,228 @@
/*
* Copyright (c) 2004, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the protothreads library.
*
* Author: Adam Dunkels <adam@sics.se>
*
* $Id: pt-sem.h,v 1.2 2005/02/24 10:36:59 adam Exp $
*/
/**
* \addtogroup pt
* @{
*/
/**
* \defgroup ptsem Protothread semaphores
* @{
*
* This module implements counting semaphores on top of
* protothreads. Semaphores are a synchronization primitive that
* provide two operations: "wait" and "signal". The "wait" operation
* checks the semaphore counter and blocks the thread if the counter
* is zero. The "signal" operation increases the semaphore counter but
* does not block. If another thread has blocked waiting for the
* semaphore that is signalled, the blocked thread will become
* runnable again.
*
* Semaphores can be used to implement other, more structured,
* synchronization primitives such as monitors and message
* queues/bounded buffers (see below).
*
* The following example shows how the producer-consumer problem, also
* known as the bounded buffer problem, can be solved using
* protothreads and semaphores. Notes on the program follow after the
* example.
*
\code
#include "pt-sem.inc"
#define NUM_ITEMS 32
#define BUFSIZE 8
static struct pt_sem mutex, full, empty;
PT_THREAD(producer(struct pt *pt))
{
static int produced;
PT_BEGIN(pt);
for(produced = 0; produced < NUM_ITEMS; ++produced) {
PT_SEM_WAIT(pt, &full);
PT_SEM_WAIT(pt, &mutex);
add_to_buffer(produce_item());
PT_SEM_SIGNAL(pt, &mutex);
PT_SEM_SIGNAL(pt, &empty);
}
PT_END(pt);
}
PT_THREAD(consumer(struct pt *pt))
{
static int consumed;
PT_BEGIN(pt);
for(consumed = 0; consumed < NUM_ITEMS; ++consumed) {
PT_SEM_WAIT(pt, &empty);
PT_SEM_WAIT(pt, &mutex);
consume_item(get_from_buffer());
PT_SEM_SIGNAL(pt, &mutex);
PT_SEM_SIGNAL(pt, &full);
}
PT_END(pt);
}
PT_THREAD(driver_thread(struct pt *pt))
{
static struct pt pt_producer, pt_consumer;
PT_BEGIN(pt);
PT_SEM_INIT(&empty, 0);
PT_SEM_INIT(&full, BUFSIZE);
PT_SEM_INIT(&mutex, 1);
PT_INIT(&pt_producer);
PT_INIT(&pt_consumer);
PT_WAIT_THREAD(pt, producer(&pt_producer) &
consumer(&pt_consumer));
PT_END(pt);
}
\endcode
*
* The program uses three protothreads: one protothread that
* implements the consumer, one thread that implements the producer,
* and one protothread that drives the two other protothreads. The
* program uses three semaphores: "full", "empty" and "mutex". The
* "mutex" semaphore is used to provide mutual exclusion for the
* buffer, the "empty" semaphore is used to block the consumer is the
* buffer is empty, and the "full" semaphore is used to block the
* producer is the buffer is full.
*
* The "driver_thread" holds two protothread state variables,
* "pt_producer" and "pt_consumer". It is important to note that both
* these variables are declared as <i>static</i>. If the static
* keyword is not used, both variables are stored on the stack. Since
* protothreads do not store the stack, these variables may be
* overwritten during a protothread wait operation. Similarly, both
* the "consumer" and "producer" protothreads declare their local
* variables as static, to avoid them being stored on the stack.
*
*
*/
/**
* \file
* Couting semaphores implemented on protothreads
* \author
* Adam Dunkels <adam@sics.se>
*
*/
#ifndef __PT_SEM_H__
#define __PT_SEM_H__
#include "pt.h"
struct pt_sem {
unsigned int count;
};
/**
* Initialize a semaphore
*
* This macro initializes a semaphore with a value for the
* counter. Internally, the semaphores use an "unsigned int" to
* represent the counter, and therefore the "count" argument should be
* within range of an unsigned int.
*
* \param s (struct pt_sem *) A pointer to the pt_sem struct
* representing the semaphore
*
* \param c (unsigned int) The initial count of the semaphore.
* \hideinitializer
*/
#define PT_SEM_INIT(s, c) (s)->count = c
/**
* Wait for a semaphore
*
* This macro carries out the "wait" operation on the semaphore. The
* wait operation causes the protothread to block while the counter is
* zero. When the counter reaches a value larger than zero, the
* protothread will continue.
*
* \param pt (struct pt *) A pointer to the protothread (struct pt) in
* which the operation is executed.
*
* \param s (struct pt_sem *) A pointer to the pt_sem struct
* representing the semaphore
*
* \hideinitializer
*/
#define PT_SEM_WAIT(pt, s) \
do { \
PT_WAIT_UNTIL(pt, (s)->count > 0); \
--(s)->count; \
} while(0)
/**
* Signal a semaphore
*
* This macro carries out the "signal" operation on the semaphore. The
* signal operation increments the counter inside the semaphore, which
* eventually will cause waiting protothreads to continue executing.
*
* \param pt (struct pt *) A pointer to the protothread (struct pt) in
* which the operation is executed.
*
* \param s (struct pt_sem *) A pointer to the pt_sem struct
* representing the semaphore
*
* \hideinitializer
*/
#define PT_SEM_SIGNAL(pt, s) ++(s)->count
#endif /* __PT_SEM_H__ */
/** @} */
/** @} */

322
libraries/ProtoThreads/pt.h Normal file
View File

@ -0,0 +1,322 @@
/*
* Copyright (c) 2004-2006, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Author: Adam Dunkels <adam@sics.se>
*
* $Id: pt.h,v 1.6 2006/06/03 11:29:43 adam Exp $
*/
/**
* \addtogroup pt
* @{
*/
/**
* \file
* Protothreads implementation.
* \author
* Adam Dunkels <adam@sics.se>
*
*/
#ifndef __PT_H__
#define __PT_H__
#include "lc.inc"
struct pt {
lc_t lc;
};
#define PT_WAITING 0
#define PT_EXITED 1
#define PT_ENDED 2
#define PT_YIELDED 3
/**
* \name Initialization
* @{
*/
/**
* Initialize a protothread.
*
* Initializes a protothread. Initialization must be done prior to
* starting to execute the protothread.
*
* \param pt A pointer to the protothread control structure.
*
* \sa PT_SPAWN()
*
* \hideinitializer
*/
#define PT_INIT(pt) LC_INIT((pt)->lc)
/** @} */
/**
* \name Declaration and definition
* @{
*/
/**
* Declaration of a protothread function.
*
* This macro is used to declare a protothread function. Protothreads
* function should be declared with this macro, but can also be
* declared as regular C functions that return an integer value.
*
* \param name_args The name and arguments of the C function
* implementing the protothread.
*
* \hideinitializer
*/
#define PT_THREAD(name_args) char name_args
/**
* Declare the start of a protothread inside the C function
* implementing the protothread.
*
* This macro is used to declare the starting point of a
* protothread. It should be placed at the start of the function in
* which the protothread runs. All C statements above the PT_BEGIN()
* invokation will be executed each time the protothread is scheduled.
*
* \param pt A pointer to the protothread control structure.
*
* \hideinitializer
*/
#define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc)
/**
* Declare the end of a protothread.
*
* This macro is used for declaring that a protothread ends. It must
* always be used together with a matching PT_BEGIN() macro.
*
* \param pt A pointer to the protothread control structure.
*
* \hideinitializer
*/
#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \
PT_INIT(pt); return PT_ENDED; }
/** @} */
/**
* \name Blocked wait
* @{
*/
/**
* Block and wait until condition is true.
*
* This macro blocks the protothread until the specified condition is
* true.
*
* \param pt A pointer to the protothread control structure.
* \param condition The condition.
*
* \hideinitializer
*/
#define PT_WAIT_UNTIL(pt, condition) \
do { \
LC_SET((pt)->lc); \
if(!(condition)) { \
return PT_WAITING; \
} \
} while(0)
/**
* Block and wait while condition is true.
*
* This function blocks and waits while condition is true. See
* PT_WAIT_UNTIL().
*
* \param pt A pointer to the protothread control structure.
* \param cond The condition.
*
* \hideinitializer
*/
#define PT_WAIT_WHILE(pt, cond) PT_WAIT_UNTIL((pt), !(cond))
/** @} */
/**
* \name Hierarchical protothreads
* @{
*/
/**
* Block and wait until a child protothread completes.
*
* This macro schedules a child protothread. The current protothread
* will block until the child protothread completes.
*
* \note The child protothread must be manually initialized with the
* PT_INIT() function before this function is used.
*
* \param pt A pointer to the protothread control structure.
* \param thread The child protothread with arguments
*
* \sa PT_SPAWN()
*
* \hideinitializer
*/
#define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE((pt), PT_SCHEDULE(thread))
/**
* Spawn a child protothread and wait until it exits.
*
* This macro spawns a child protothread and waits until it exits. The
* macro can only be used within a protothread.
*
* \param pt A pointer to the protothread control structure.
* \param child A pointer to the child protothread's control structure.
* \param thread The child protothread with arguments
*
* \hideinitializer
*/
#define PT_SPAWN(pt, child, thread) \
do { \
PT_INIT((child)); \
PT_WAIT_THREAD((pt), (thread)); \
} while(0)
/** @} */
/**
* \name Exiting and restarting
* @{
*/
/**
* Restart the protothread.
*
* This macro will block and cause the running protothread to restart
* its execution at the place of the PT_BEGIN() call.
*
* \param pt A pointer to the protothread control structure.
*
* \hideinitializer
*/
#define PT_RESTART(pt) \
do { \
PT_INIT(pt); \
return PT_WAITING; \
} while(0)
/**
* Exit the protothread.
*
* This macro causes the protothread to exit. If the protothread was
* spawned by another protothread, the parent protothread will become
* unblocked and can continue to run.
*
* \param pt A pointer to the protothread control structure.
*
* \hideinitializer
*/
#define PT_EXIT(pt) \
do { \
PT_INIT(pt); \
return PT_EXITED; \
} while(0)
/** @} */
/**
* \name Calling a protothread
* @{
*/
/**
* Schedule a protothread.
*
* This function shedules a protothread. The return value of the
* function is non-zero if the protothread is running or zero if the
* protothread has exited.
*
* \param f The call to the C function implementing the protothread to
* be scheduled
*
* \hideinitializer
*/
#define PT_SCHEDULE(f) ((f) == PT_WAITING)
/** @} */
/**
* \name Yielding from a protothread
* @{
*/
/**
* Yield from the current protothread.
*
* This function will yield the protothread, thereby allowing other
* processing to take place in the system.
*
* \param pt A pointer to the protothread control structure.
*
* \hideinitializer
*/
#define PT_YIELD(pt) \
do { \
PT_YIELD_FLAG = 0; \
LC_SET((pt)->lc); \
if(PT_YIELD_FLAG == 0) { \
return PT_YIELDED; \
} \
} while(0)
/**
* \brief Yield from the protothread until a condition occurs.
* \param pt A pointer to the protothread control structure.
* \param cond The condition.
*
* This function will yield the protothread, until the
* specified condition evaluates to true.
*
*
* \hideinitializer
*/
#define PT_YIELD_UNTIL(pt, cond) \
do { \
PT_YIELD_FLAG = 0; \
LC_SET((pt)->lc); \
if((PT_YIELD_FLAG == 0) || !(cond)) { \
return PT_YIELDED; \
} \
} while(0)
/** @} */
#endif /* __PT_H__ */
/** @} */

View File

@ -0,0 +1,51 @@
Arduino implementation of Protothreads version 1.4.01
-----------------------------------------------------
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.
Main features:
* Very small RAM overhead - only two bytes per protothread and no extra stacks
* Highly portable - the protothreads library is 100% pure C and no architecture
specific assembly code
* Can be used with or without an OS
* Provides blocking wait without full multi-threading or stack-switching
* Freely available under a BSD-like open source license
Related links:
Protothreads:
http://www.sics.se/~adam/pt/index.html
Download of original Protothreads on which this version is based on:
http://www.sics.se/~adam/pt/download.html
Publications:
http://www.sics.se/~adam/pt/publications.html
Documentation:
http://www.sics.se/~adam/pt/pt-1.4-refman/
Protothreads has been developed by Adam Dunkels
http://www.sics.se/~adam/
The original Protothreads implementation by Adam Dunkels as been adopted
to the Arduino platform and is maintained by Benjamin Soelberg.
The Arduino "implementation" of Protothreads is based on version 1.4 of
the original Protothreads.
email benjamin.soelberg@gmail.com
blog http://www.kukkuk.dk
www http://www.javadesign.dk

View File

@ -0,0 +1,4 @@
Date Version Description
-------------------------------------------------------------------------------
2009.01.07 1.4.01 Initial version of the Arduino implementation of
Protothreads. Based on original Protothreads version 1.4

View File

@ -1,7 +1,7 @@
ARDUINO_DIR = ..
TARGET = wordclock
ARDUINO_LIBS = ds1302
ARDUINO_LIBS = ds1302 ProtoThreads
MCU = atmega328
SMCU = m328p

View File

@ -1,4 +1,5 @@
#!/bin/sh
screen /dev/ttyUSB0 9600
# screen /dev/ttyUSB0 9600
screen /dev/ttyUSB0 115200

View File

@ -4,6 +4,7 @@
#include <avr/interrupt.h>
#include <avr/io.h>
#include <math.h>
#include <pt.h>
// uncomment the following to speed up the timer for testing
//#define TESTMODE
@ -80,22 +81,15 @@ void SWversion(void);
// base of exponential mapping (must be > 1)
#define LIGHTSENSOR_BASE 1.40
// DAY Brightness setting 0 = off 20 = full
#define N_PWM_STEPS 11
// DAY Brightness setting 0 = off, N_PWM_STEPS - 1 = full
#define MAXBRIGHTNESS 10
// start MAXBRIGHTNESS at DAYLIGHTHOUR (7 am)
#define DAYLIGHTHOUR 7
// NIGHT Brightness setting 0 = off 20 = full
// NIGHT Brightness setting 0 = off, N_PWM_STEPS - 1 = full
#define MINBRIGHTNESS 2
// start MINBRIGHTNESS at NIGHTLIGHTHOUR (7 pm)
#define NIGHTLIGHTHOUR 19
#define N_PWM_STEPS 11
//#define LED1 Led1=1
//#define LED2 Led2=1
//#define LED3 Led3=1
//#define LED4 Led4=1
#define INIT_TIMER_COUNT 6
#define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNT
@ -107,7 +101,7 @@ void SWversion(void);
unsigned int brightness_per_unit_light = ((1 << LIGHTSENSOR_SCALE) * (MAXBRIGHTNESS -
MINBRIGHTNESS)) / (LIGHTSENSOR_TOP - LIGHTSENSOR_BOTTOM);
int hour=12, minute=0, second=00;
int hour=12, minute=0, second=0;
static unsigned long msTick =0; // the number of Millisecond Ticks since we last
// incremented the second counter
int count;
@ -124,8 +118,17 @@ int LEDDataPin=3; // Arduino Pin#5 - 4094 pin 2 Data
int LEDStrobePin=4; // Arduino Pin#6 - 4094 pin 1 Strobe
// buttons
int FWDButtonPin=6;
int REVButtonPin=7;
#define FWD_BUTTON_PIN 6
#define REV_BUTTON_PIN 7
#define BUTTON_PORT PIND
#define FWD_BUTTON_MASK (1 << 6)
#define REV_BUTTON_MASK (1 << 7)
// delay in ms before repeating key
#define BUTTON_DELAY_LONG 1000
// delay in ms before repeating key a second time
#define BUTTON_DELAY_SHORT 200
// 1302 RTC Constants
int DS1302IOPin=10;
@ -155,6 +158,9 @@ char buf[50]; // time output string for debugging
// create an object that talks to the RTC
DS1302 rtc(DS1302CEPin, DS1302IOPin, DS1302CLKPin);
// ProtoThread structures (one for each thread)
static struct pt buttons_thread_pt, wordclock_pt;
void print_DS1302time()
{
/* Get the current time and date from the chip */
@ -180,8 +186,8 @@ void setup()
pinMode(LEDClockPin, OUTPUT);
pinMode(LEDDataPin, OUTPUT);
pinMode(LEDStrobePin, OUTPUT);
pinMode(FWDButtonPin, INPUT);
pinMode(REVButtonPin, INPUT);
pinMode(FWD_BUTTON_PIN, INPUT);
pinMode(REV_BUTTON_PIN, INPUT);
// setup 1302
pinMode(DS1302IOPin, OUTPUT);
@ -194,7 +200,8 @@ void setup()
pinMode(LED3PIN, OUTPUT);
pinMode(LED4PIN, OUTPUT);
analogReference(DEFAULT);
//analogReference(DEFAULT);
analogReference(INTERNAL);
//analogReference(EXTERNAL);
// known 'bug' in AVR ADC: after switching analog reference, first sample
// could be corrupt and should be ignored --> discard this sample now
@ -202,8 +209,8 @@ void setup()
current_brightnes=MAXBRIGHTNESS;
Serial.begin(9600); // setup the serial port to 9600 baud
//Serial.begin(9600); // setup the serial port to 9600 baud
Serial.begin(115200); // setup the serial port to 115200 baud
SWversion(); // Display the version number of the software
// test whether the DS1302 is there
@ -234,7 +241,8 @@ void setup()
DS1302Present=1;
Serial.println("present - new chip initialised.");
}
else Serial.println("absent");
else
Serial.println("absent");
}
// compute ambient light level to brightness level mapping
@ -265,23 +273,23 @@ void setup()
// new hardware uses internal pullups, and uses the buttons
// to pull the inputs down.
digitalWrite(FWDButtonPin,HIGH); // Turn on weak pullups
digitalWrite(REVButtonPin,HIGH); // Turn on weak pullups
digitalWrite(FWD_BUTTON_PIN,HIGH); // Turn on weak pullups
digitalWrite(REV_BUTTON_PIN,HIGH); // Turn on weak pullups
OldHardware=0;
if ( digitalRead(FWDButtonPin)==0 && digitalRead(REVButtonPin)==0)
if ( digitalRead(FWD_BUTTON_PIN)==0 && digitalRead(REV_BUTTON_PIN)==0)
{
Serial.println("Detected Old Hardware");
OldHardware=1; // we have old hardware
BTNActive = 1; // True = active for old hardware
digitalWrite(FWDButtonPin,LOW); // Turn off weak pullups
digitalWrite(REVButtonPin,LOW); // Turn off weak pullups
digitalWrite(FWD_BUTTON_PIN,LOW); // Turn off weak pullups
digitalWrite(REV_BUTTON_PIN,LOW); // Turn off weak pullups
}
else
{
Serial.println("Detected New Hardware");
OldHardware=0; // we have old hardware
OldHardware=0; // we have new hardware
BTNActive = 0; // True = active for old hardware
}
@ -354,10 +362,8 @@ ISR(TIMER2_OVF_vect) {
digitalWrite(LED4PIN,0);
}
if (timercount==0) timercount=N_PWM_STEPS-1;
if (timercount==0)
timercount=N_PWM_STEPS-1;
}
@ -372,12 +378,6 @@ void ledsoff(void) {
Led4=0;
}
void incrementtime(void){
// increment the time counters keeping care to rollover as required
second=0;
@ -387,7 +387,7 @@ void incrementtime(void){
hour=1;
}
}
// debug outputs
/* // debug outputs
Serial.println();
if (DS1302Present==1) print_DS1302time();
else Serial.print("Arduino Time: " );
@ -396,26 +396,147 @@ void incrementtime(void){
Serial.print(minute);
Serial.print(":");
Serial.print(second);
Serial.print(" ");
Serial.print(" ");*/
}
void SWversion(void) {
delay(2000);
Serial.println();
Serial.println("Wordclock -Arduino v3.0a - reduced brightness version");
Serial.print(LANGUAGE); Serial.println(" header file used");
Serial.println("(c)2009, 2010, 2011 Doug Jackson");
}
void process_buttons(unsigned char button_status)
{
char buttonPressed = 0, fwdButtonPressed = 0, revButtonPressed = 0, buttonStatus = 0;
static unsigned long buttonPressedMillis = 0;
static unsigned long button_delay = BUTTON_DELAY_LONG;
unsigned long millisNow = 0, new_button_delay;
void loop(void)
// test to see if both buttons are being held down
// if so - start a self test till both buttons are held
// down again.
//if ( digitalRead(FWD_BUTTON_PIN)==BTNActive && digitalRead(REV_BUTTON_PIN)==BTNActive)
if (BTNActive)
{
if ((button_status & FWD_BUTTON_MASK) > 0)
fwdButtonPressed = 1;
if ((button_status & REV_BUTTON_MASK) > 0)
revButtonPressed = 1;
}
else
{
if ((button_status & FWD_BUTTON_MASK) == 0)
fwdButtonPressed = 1;
if ((button_status & REV_BUTTON_MASK) == 0)
revButtonPressed = 1;
}
if (fwdButtonPressed && revButtonPressed)
{
selftestmode != selftestmode;
}
if (selftestmode) {
Serial.println("Selftest Mode TRUE");
for(int i=0; i<100; i++)
{
Display1=255; Display2=255; Display3=255; delay(101-i);
ledsoff();delay(101-i);
if (digitalRead(FWD_BUTTON_PIN)==1)
selftestmode=!selftestmode;
}
displaytime();
}
if (fwdButtonPressed)
{
// the forward button is down
// and it has been more than one second since we
// last looked
Serial.println("Forward Button Down");
incrementtime();
second++; // Increment the second counter to ensure that the name
// flash doesnt happen when setting time
if (DS1302Present==1) {
// Make a new time object to set the date and time
Time t(2010, 04, 28, hour, minute, second, 01);
// Set the time and date on the chip
rtc.time(t);
}
displaytime();
}
//if (digitalRead(REV_BUTTON_PIN)==BTNActive )
if (revButtonPressed)
{
Serial.println("Backwards Button Down");
minute--;
minute--;
second=0; // decrement the minute counter
if (minute<0) {
minute=58;
if (--hour <0) hour=23;
}
incrementtime();
second++; // Increment the second counter to ensure that the name
// flash doesnt happen when setting time
if (DS1302Present==1) {
// Make a new time object to set the date and time
Time t(2010, 04, 28, hour, minute, second, 01);
// Set the time and date on the chip
rtc.time(t);
}
displaytime();
}
// key repeat delay code:
millisNow = millis();
if (millisNow - buttonPressedMillis < BUTTON_DELAY_LONG)
new_button_delay = BUTTON_DELAY_SHORT;
else
new_button_delay = BUTTON_DELAY_LONG;
delay(button_delay);
button_delay = new_button_delay;
buttonPressedMillis = millis();
}
static int buttons_thread(struct pt *pt)
{
PT_BEGIN(pt);
while(1)
{
if (BTNActive)
{
PT_WAIT_UNTIL(pt, ((BUTTON_PORT & FWD_BUTTON_MASK) > 0) ||
((BUTTON_PORT & REV_BUTTON_MASK) > 0));
process_buttons(BUTTON_PORT);
}
else
{
PT_WAIT_UNTIL(pt, ((BUTTON_PORT & FWD_BUTTON_MASK) == 0) ||
((BUTTON_PORT & REV_BUTTON_MASK) == 0));
process_buttons(BUTTON_PORT);
}
}
PT_END(pt);
}
static int wordclock(struct pt *pt)
{
static unsigned long int lightlevel_avg;
unsigned long int lightlevel_sample;
static char n_lightlevel_samples = 0;
static unsigned int counter = 0;
static char millisWillOverflow = 0;
#ifdef TESTMODE
second=59;
@ -423,10 +544,25 @@ void loop(void)
//Serial.println("Loop Started");
PT_BEGIN(pt);
// heart of the timer - keep looking at the millisecond timer on the Arduino
// and increment the seconds counter every 1000 ms
if ( millis() - msTick >999) {
while(1)
{
PT_WAIT_UNTIL(pt, millisWillOverflow ? (millis() - msTick > 999) :
((millis() < 4294967295 - 998) && (millis() - msTick > 999)));
msTick=millis();
if (msTick > 4294967295 - 998)
{
// millis() will overflow --> adjust msTick
msTick = 999 - (4294967295 - msTick);
millisWillOverflow = 1;
}
else
millisWillOverflow = 0;
second++;
// Flash the onboard Pin13 Led so we know something is hapening!
digitalWrite(13,HIGH);
@ -436,9 +572,6 @@ void loop(void)
digitalWrite(13,HIGH);
delay(50);
digitalWrite(13,LOW);
}
//test to see if we need to increment the time counters
if (second==60)
@ -495,12 +628,12 @@ void loop(void)
current_brightnes = ambient_light_to_brightness[lightlevel_avg];
/*Serial.print("lightsensor: ");
Serial.print("lightsensor: ");
Serial.print(lightlevel_sample);
Serial.print(" / ");
Serial.print(lightlevel_avg);
Serial.print(", brightness: ");
Serial.println(current_brightnes);*/
Serial.println(current_brightnes);
#else
if ((hour < DAYLIGHTHOUR) | (hour >= NIGHTLIGHTHOUR))
current_brightnes=MINBRIGHTNESS;
@ -508,83 +641,14 @@ void loop(void)
current_brightnes=MAXBRIGHTNESS;
#endif
// test to see if both buttons are being held down
// if so - start a self test till both buttons are held
// down again.
if ( digitalRead(FWDButtonPin)==BTNActive && digitalRead(REVButtonPin)==BTNActive)
{
selftestmode = !selftestmode;
if (selftestmode) Serial.println("Selftest Mode TRUE");
else Serial.println("Selftest mode FALSE");
PT_END(pt);
}
if (selftestmode) {
for(int i=0; i<100; i++)
{
Display1=255; Display2=255; Display3=255; delay(101-i);
ledsoff();delay(101-i);
if (digitalRead(FWDButtonPin)==1) selftestmode=!selftestmode;
}
displaytime();
}
// test to see if a forward button is being held down
// for time setting
if (digitalRead(FWDButtonPin) ==BTNActive )
// the forward button is down
// and it has been more than one second since we
// last looked
{
Serial.println("Forward Button Down");
incrementtime();
second++; // Increment the second counter to ensure that the name
// flash doesnt happen when setting time
if (DS1302Present==1) {
// Make a new time object to set the date and time
Time t(2010, 04, 28, hour, minute, second, 01);
// Set the time and date on the chip
rtc.time(t);
}
delay(100);
displaytime();
}
// test to see if the back button is being held down
// for time setting
if (digitalRead(REVButtonPin)==BTNActive )
{
Serial.println("Backwards Button Down");
minute--;
minute--;
second=0; // decrement the minute counter
if (minute<0) {
minute=58;
if (--hour <0) hour=23;
}
incrementtime();
second++; // Increment the second counter to ensure that the name
// flash doesnt happen when setting time
if (DS1302Present==1) {
// Make a new time object to set the date and time
Time t(2010, 04, 28, hour, minute, second, 01);
// Set the time and date on the chip
rtc.time(t);
}
displaytime();
delay(100);
}
}
void loop()
{
// call each function continuously
wordclock(&wordclock_pt);
buttons_thread(&buttons_thread_pt);
}