From 38da3b00c1c7ecc9644d92430510ff680f2aacd2 Mon Sep 17 00:00:00 2001 From: Admar Schoonen Date: Sat, 7 Jan 2012 01:35:54 +0100 Subject: [PATCH] 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) --- .../examples/example-small/example-small.pde | 99 +++ libraries/ProtoThreads/install.txt | 11 + libraries/ProtoThreads/keywords.txt | 32 + libraries/ProtoThreads/lc-addrlabels.inc | 85 ++ libraries/ProtoThreads/lc-switch.inc | 76 ++ libraries/ProtoThreads/lc.inc | 132 +++ libraries/ProtoThreads/pt-sem.inc | 228 +++++ libraries/ProtoThreads/pt.h | 322 +++++++ libraries/ProtoThreads/readme.txt | 51 ++ libraries/ProtoThreads/version.txt | 4 + wordclock/Makefile | 2 +- wordclock/serialconsole.sh | 3 +- wordclock/wordclock.ino | 818 ++++++++++-------- 13 files changed, 1484 insertions(+), 379 deletions(-) create mode 100644 libraries/ProtoThreads/examples/example-small/example-small.pde create mode 100644 libraries/ProtoThreads/install.txt create mode 100644 libraries/ProtoThreads/keywords.txt create mode 100644 libraries/ProtoThreads/lc-addrlabels.inc create mode 100644 libraries/ProtoThreads/lc-switch.inc create mode 100644 libraries/ProtoThreads/lc.inc create mode 100644 libraries/ProtoThreads/pt-sem.inc create mode 100644 libraries/ProtoThreads/pt.h create mode 100644 libraries/ProtoThreads/readme.txt create mode 100644 libraries/ProtoThreads/version.txt diff --git a/libraries/ProtoThreads/examples/example-small/example-small.pde b/libraries/ProtoThreads/examples/example-small/example-small.pde new file mode 100644 index 0000000..98356fd --- /dev/null +++ b/libraries/ProtoThreads/examples/example-small/example-small.pde @@ -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); +} diff --git a/libraries/ProtoThreads/install.txt b/libraries/ProtoThreads/install.txt new file mode 100644 index 0000000..0c2379c --- /dev/null +++ b/libraries/ProtoThreads/install.txt @@ -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 \ No newline at end of file diff --git a/libraries/ProtoThreads/keywords.txt b/libraries/ProtoThreads/keywords.txt new file mode 100644 index 0000000..e030430 --- /dev/null +++ b/libraries/ProtoThreads/keywords.txt @@ -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) +####################################### diff --git a/libraries/ProtoThreads/lc-addrlabels.inc b/libraries/ProtoThreads/lc-addrlabels.inc new file mode 100644 index 0000000..3e6474e --- /dev/null +++ b/libraries/ProtoThreads/lc-addrlabels.inc @@ -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 + * + * $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 + * + * 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__ */ +/** @} */ diff --git a/libraries/ProtoThreads/lc-switch.inc b/libraries/ProtoThreads/lc-switch.inc new file mode 100644 index 0000000..dbdde01 --- /dev/null +++ b/libraries/ProtoThreads/lc-switch.inc @@ -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 + * + * $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 + * + * 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__ */ + +/** @} */ diff --git a/libraries/ProtoThreads/lc.inc b/libraries/ProtoThreads/lc.inc new file mode 100644 index 0000000..4021bec --- /dev/null +++ b/libraries/ProtoThreads/lc.inc @@ -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 + * + * $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 set in a specific function to + * capture the state of the function. After a local continuation has + * been set can be resumed 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 + * + */ + +#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 not 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__ */ + +/** @} */ +/** @} */ diff --git a/libraries/ProtoThreads/pt-sem.inc b/libraries/ProtoThreads/pt-sem.inc new file mode 100644 index 0000000..318512e --- /dev/null +++ b/libraries/ProtoThreads/pt-sem.inc @@ -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 + * + * $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 static. 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 + * + */ + +#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__ */ + +/** @} */ +/** @} */ + diff --git a/libraries/ProtoThreads/pt.h b/libraries/ProtoThreads/pt.h new file mode 100644 index 0000000..d6636d5 --- /dev/null +++ b/libraries/ProtoThreads/pt.h @@ -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 + * + * $Id: pt.h,v 1.6 2006/06/03 11:29:43 adam Exp $ + */ + +/** + * \addtogroup pt + * @{ + */ + +/** + * \file + * Protothreads implementation. + * \author + * Adam Dunkels + * + */ + +#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__ */ + +/** @} */ diff --git a/libraries/ProtoThreads/readme.txt b/libraries/ProtoThreads/readme.txt new file mode 100644 index 0000000..17d0038 --- /dev/null +++ b/libraries/ProtoThreads/readme.txt @@ -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 diff --git a/libraries/ProtoThreads/version.txt b/libraries/ProtoThreads/version.txt new file mode 100644 index 0000000..ef9158d --- /dev/null +++ b/libraries/ProtoThreads/version.txt @@ -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 diff --git a/wordclock/Makefile b/wordclock/Makefile index f22f95c..8c7fa0f 100644 --- a/wordclock/Makefile +++ b/wordclock/Makefile @@ -1,7 +1,7 @@ ARDUINO_DIR = .. TARGET = wordclock -ARDUINO_LIBS = ds1302 +ARDUINO_LIBS = ds1302 ProtoThreads MCU = atmega328 SMCU = m328p diff --git a/wordclock/serialconsole.sh b/wordclock/serialconsole.sh index 5ffacbb..7713436 100755 --- a/wordclock/serialconsole.sh +++ b/wordclock/serialconsole.sh @@ -1,4 +1,5 @@ #!/bin/sh -screen /dev/ttyUSB0 9600 +# screen /dev/ttyUSB0 9600 +screen /dev/ttyUSB0 115200 diff --git a/wordclock/wordclock.ino b/wordclock/wordclock.ino index fe0ae1f..9c9cac9 100644 --- a/wordclock/wordclock.ino +++ b/wordclock/wordclock.ino @@ -4,6 +4,7 @@ #include #include #include +#include // uncomment the following to speed up the timer for testing //#define TESTMODE @@ -49,8 +50,8 @@ void SWversion(void); * * Revision History * - * Date By What - * 20010205 DRJ Initial Creation of Arduino Version + * Date By What + * 20010205 DRJ Initial Creation of Arduino Version * - based on Wordclock.c - from PIC version * 20100428 DRJ Added support for DS1302 RTC Chip - Code verifies * device is present - and if so, uses it for timekeeping @@ -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,25 +101,34 @@ 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; -static unsigned long msTick =0; // the number of Millisecond Ticks since we last +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; -int selftestmode; // 1 = in self test - flash display -int DS1302Present=0; // flag to indicate that the 1302 is there.. 1 = present +int count; +int selftestmode; // 1 = in self test - flash display +int DS1302Present=0; // flag to indicate that the 1302 is there.. 1 = present char Display1=0, Display2=0, Display3=0, Led1=0, Led2=0, Led3=0, Led4=0; -int OldHardware = 0; // 1 = we are running on old hardware -int BTNActive = 1; // the sense of the button inputs (Changes based on hardware type) -int timercount=10; // used for interrupt counting to determine the brightnes of the display - +int OldHardware = 0; // 1 = we are running on old hardware +int BTNActive = 1; // the sense of the button inputs (Changes based on hardware type) +int timercount=10; // used for interrupt counting to determine the brightnes of the display + // hardware constants -int LEDClockPin=5; // Arduino Pin#11 - 4094 Pin 3 clock -int LEDDataPin=3; // Arduino Pin#5 - 4094 pin 2 Data -int LEDStrobePin=4; // Arduino Pin#6 - 4094 pin 1 Strobe +int LEDClockPin=5; // Arduino Pin#11 - 4094 Pin 3 clock +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; @@ -133,10 +136,10 @@ int DS1302CEPin=8; int DS1302CLKPin=9; // Minute LED Pins -int LED1PIN=19; // Arduino analog 5 -int LED2PIN=18; // Arduino analog 4 -int LED3PIN=17; // Arduino analog 3 -int LED4PIN=16; // Arduino analog 2 +int LED1PIN=19; // Arduino analog 5 +int LED2PIN=18; // Arduino analog 4 +int LED3PIN=17; // Arduino analog 3 +int LED4PIN=16; // Arduino analog 2 int current_brightnes=0; @@ -144,7 +147,7 @@ char ambient_light_to_brightness[1024]; // define the language to be used for this project: -#include LANGUAGE // The language pack +#include LANGUAGE // The language pack @@ -155,17 +158,20 @@ 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 */ - Time t = rtc.time(); + /* Get the current time and date from the chip */ + Time t = rtc.time(); - /* Format the time and date and insert into the temporary buffer */ - snprintf(buf, sizeof(buf), "DS1302 time: %02d:%02d:%02d", - t.hr, t.min, t.sec); + /* Format the time and date and insert into the temporary buffer */ + snprintf(buf, sizeof(buf), "DS1302 time: %02d:%02d:%02d", + t.hr, t.min, t.sec); - /* Print the formatted string to serial so we can see the time */ - Serial.println(buf); + /* Print the formatted string to serial so we can see the time */ + Serial.println(buf); } @@ -173,418 +179,476 @@ void print_DS1302time() void setup() { - int n; + int n; - // initialise the hardware - // initialize the appropriate pins as outputs: - pinMode(LEDClockPin, OUTPUT); - pinMode(LEDDataPin, OUTPUT); - pinMode(LEDStrobePin, OUTPUT); - pinMode(FWDButtonPin, INPUT); - pinMode(REVButtonPin, INPUT); + // initialise the hardware + // initialize the appropriate pins as outputs: + pinMode(LEDClockPin, OUTPUT); + pinMode(LEDDataPin, OUTPUT); + pinMode(LEDStrobePin, OUTPUT); + pinMode(FWD_BUTTON_PIN, INPUT); + pinMode(REV_BUTTON_PIN, INPUT); - // setup 1302 - pinMode(DS1302IOPin, OUTPUT); - pinMode(DS1302CEPin, OUTPUT); - pinMode(DS1302CLKPin, OUTPUT); + // setup 1302 + pinMode(DS1302IOPin, OUTPUT); + pinMode(DS1302CEPin, OUTPUT); + pinMode(DS1302CLKPin, OUTPUT); - // Minute LEDS - pinMode(LED1PIN, OUTPUT); - pinMode(LED2PIN, OUTPUT); - pinMode(LED3PIN, OUTPUT); - pinMode(LED4PIN, OUTPUT); + // Minute LEDS + pinMode(LED1PIN, OUTPUT); + pinMode(LED2PIN, OUTPUT); + pinMode(LED3PIN, OUTPUT); + pinMode(LED4PIN, OUTPUT); - analogReference(DEFAULT); - //analogReference(EXTERNAL); - // known 'bug' in AVR ADC: after switching analog reference, first sample - // could be corrupt and should be ignored --> discard this sample now - analogRead(LIGHTSENSOR_INPUTPIN); + //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 + analogRead(LIGHTSENSOR_INPUTPIN); - current_brightnes=MAXBRIGHTNESS; - + current_brightnes=MAXBRIGHTNESS; - Serial.begin(9600); // setup the serial port to 9600 baud - SWversion(); // Display the version number of the software + //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 - Serial.print("Verifying DS1302 "); - // start by verifying that the chip has a valid signature - if (rtc.read_register(0x20) == 0xa5) { - // Signature is there - set the present flag and mmove on - DS1302Present=1; - Serial.println("present - Valid Signature"); - rtc.write_protect(false); - rtc.halt(false); - } - else - { - // Signature isnt there - may be a new chip - - // do a write to see if it will hold the signature - rtc.write_register(0x20,0xa5); - if (rtc.read_register(0x20) == 0xa5) { - // We can store data - assume that it is a new chip that needs initialisation - // Start by turning off write protection and clearing the clock halt flag. - rtc.write_protect(false); - rtc.halt(false); - // Make a new time object to set the date and time - Time t(2010, 04, 28, 10, 19, 00, 01); - // Set the time and date on the chip - rtc.time(t); - // set the DS1302 present flag - DS1302Present=1; - Serial.println("present - new chip initialised."); - } - else Serial.println("absent"); - } + // test whether the DS1302 is there + Serial.print("Verifying DS1302 "); + // start by verifying that the chip has a valid signature + if (rtc.read_register(0x20) == 0xa5) { + // Signature is there - set the present flag and mmove on + DS1302Present=1; + Serial.println("present - Valid Signature"); + rtc.write_protect(false); + rtc.halt(false); + } + else + { + // Signature isnt there - may be a new chip - + // do a write to see if it will hold the signature + rtc.write_register(0x20,0xa5); + if (rtc.read_register(0x20) == 0xa5) { + // We can store data - assume that it is a new chip that needs initialisation + // Start by turning off write protection and clearing the clock halt flag. + rtc.write_protect(false); + rtc.halt(false); + // Make a new time object to set the date and time + Time t(2010, 04, 28, 10, 19, 00, 01); + // Set the time and date on the chip + rtc.time(t); + // set the DS1302 present flag + DS1302Present=1; + Serial.println("present - new chip initialised."); + } + else + Serial.println("absent"); + } - // compute ambient light level to brightness level mapping - for (n = 0; n < sizeof(ambient_light_to_brightness); n++) - { - if (n <= LIGHTSENSOR_BOTTOM) - ambient_light_to_brightness[n] = MINBRIGHTNESS; - else if (n >= LIGHTSENSOR_TOP) - ambient_light_to_brightness[n] = MAXBRIGHTNESS; - else - { - ambient_light_to_brightness[n] = + // compute ambient light level to brightness level mapping + for (n = 0; n < sizeof(ambient_light_to_brightness); n++) + { + if (n <= LIGHTSENSOR_BOTTOM) + ambient_light_to_brightness[n] = MINBRIGHTNESS; + else if (n >= LIGHTSENSOR_TOP) + ambient_light_to_brightness[n] = MAXBRIGHTNESS; + else + { + ambient_light_to_brightness[n] = round( pow((double) LIGHTSENSOR_BASE, ( (double) brightness_per_unit_light * ((double) n - (double) LIGHTSENSOR_BOTTOM) / (double) (1 << LIGHTSENSOR_SCALE))) / pow((double) LIGHTSENSOR_BASE, (double) (MAXBRIGHTNESS - (double) MINBRIGHTNESS)) * ((double) MAXBRIGHTNESS - (double) MINBRIGHTNESS) + (double) MINBRIGHTNESS ); - /* Serial.print(n); - Serial.print(": "); - Serial.println(ambient_light_to_brightness[n], DEC); */ - } - } + /* Serial.print(n); + Serial.print(": "); + Serial.println(ambient_light_to_brightness[n], DEC); */ + } + } - // determine whether we are running on old or new hardware - // old hardware tied the push buttons to ground using 4k7 resistors - // and relied on the buttons to pull them high - // new hardware uses internal pullups, and uses the buttons - // to pull the inputs down. + // determine whether we are running on old or new hardware + // old hardware tied the push buttons to ground using 4k7 resistors + // and relied on the buttons to pull them high + // 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) - { - 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 + OldHardware=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(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 - BTNActive = 0; // True = active for old hardware - } + } + else + { + Serial.println("Detected New Hardware"); + OldHardware=0; // we have new hardware + BTNActive = 0; // True = active for old hardware + } - // Initialise Timer2: Timer Prescaler /64, - TCCR2A = 0; - TCCR2B |= (1<= 60) { - minute=0; - if (++hour == 25) { - hour=1; - } - } - // debug outputs - Serial.println(); - if (DS1302Present==1) print_DS1302time(); - else Serial.print("Arduino Time: " ); - Serial.print(hour); - Serial.print(":"); - Serial.print(minute); - Serial.print(":"); - Serial.print(second); - Serial.print(" "); + // increment the time counters keeping care to rollover as required + second=0; + if (++minute >= 60) { + minute=0; + if (++hour == 25) { + hour=1; + } + } + /* // debug outputs + Serial.println(); + if (DS1302Present==1) print_DS1302time(); + else Serial.print("Arduino Time: " ); + Serial.print(hour); + Serial.print(":"); + Serial.print(minute); + Serial.print(":"); + Serial.print(second); + 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"); + 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 loop(void) +void process_buttons(unsigned char button_status) { - static unsigned long int lightlevel_avg; - unsigned long int lightlevel_sample; - static char n_lightlevel_samples = 0; - static unsigned int counter = 0; + 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; - #ifdef TESTMODE - second=59; - #endif - - //Serial.println("Loop Started"); - - // 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) { - msTick=millis(); - second++; - // Flash the onboard Pin13 Led so we know something is hapening! - digitalWrite(13,HIGH); - delay(50); - digitalWrite(13,LOW); - delay(50); - digitalWrite(13,HIGH); - delay(50); - digitalWrite(13,LOW); - } + // 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); + } - //test to see if we need to increment the time counters - if (second==60) - { - incrementtime(); - displaytime(); - } + displaytime(); + } - if (DS1302Present==1) { - // Get the current time and date from the chip - Time t = rtc.time(); - second=t.sec; - minute=t.min; - hour=t.hr; - } + // key repeat delay code: + millisNow = millis(); - // set the brightnes level based on the current hour - night=7pm - 6.59am - // - #if (USELIGHTSENSOR == 1) - lightlevel_sample = analogRead(LIGHTSENSOR_INPUTPIN); + if (millisNow - buttonPressedMillis < BUTTON_DELAY_LONG) + new_button_delay = BUTTON_DELAY_SHORT; + else + new_button_delay = BUTTON_DELAY_LONG; - // update average - if (n_lightlevel_samples < (1 << LIGHTSENSOR_WEIGHT)) - { - // add (1 << (LIGHTSENSOR_WEIGHT - 1)) to average before division so that - // the average is round()-ed instead of floor()-ed - lightlevel_avg = (n_lightlevel_samples * lightlevel_avg + - lightlevel_sample + (1 << (LIGHTSENSOR_WEIGHT - 1))) / - (n_lightlevel_samples + 1); - n_lightlevel_samples++; - } - else - { - // do not update n_lightlevel_samples to prevent overflow + delay(button_delay); + button_delay = new_button_delay; + buttonPressedMillis = millis(); +} - // add (1 << (LIGHTSENSOR_WEIGHT - 1)) to average before division so that - // the average is round()-ed instead of floor()-ed - lightlevel_avg = ( ((1 << LIGHTSENSOR_WEIGHT) - 1) * lightlevel_avg + - lightlevel_sample + (1 << (LIGHTSENSOR_WEIGHT - 1))) >> LIGHTSENSOR_WEIGHT; - } +static int buttons_thread(struct pt *pt) +{ + PT_BEGIN(pt); - // compute new brightness level - /* - // linear method - if (lightlevel_avg <= LIGHTSENSOR_BOTTOM) - current_brightnes = MINBRIGHTNESS; - else if (lightlevel_avg >= LIGHTSENSOR_TOP) - current_brightnes = MAXBRIGHTNESS; - else - { - current_brightnes = (brightness_per_unit_light * (lightlevel_avg - - LIGHTSENSOR_BOTTOM)) / (1 << LIGHTSENSOR_SCALE) + MINBRIGHTNESS; - } */ + 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); + } + } - current_brightnes = ambient_light_to_brightness[lightlevel_avg]; + PT_END(pt); +} - /*Serial.print("lightsensor: "); - Serial.print(lightlevel_sample); - Serial.print(" / "); - Serial.print(lightlevel_avg); - Serial.print(", brightness: "); - Serial.println(current_brightnes);*/ - #else - if ((hour < DAYLIGHTHOUR) | (hour >= NIGHTLIGHTHOUR)) - current_brightnes=MINBRIGHTNESS; - else - current_brightnes=MAXBRIGHTNESS; - #endif - +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; - // 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"); - } + #ifdef TESTMODE + second=59; + #endif - 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(); - - } + //Serial.println("Loop Started"); + PT_BEGIN(pt); - // 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 + // heart of the timer - keep looking at the millisecond timer on the Arduino + // and increment the seconds counter every 1000 ms - { - 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(); - } + 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; - // test to see if the back button is being held down - // for time setting - if (digitalRead(REVButtonPin)==BTNActive ) - { + second++; + // Flash the onboard Pin13 Led so we know something is hapening! + digitalWrite(13,HIGH); + delay(50); + digitalWrite(13,LOW); + delay(50); + digitalWrite(13,HIGH); + delay(50); + digitalWrite(13,LOW); - 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 + //test to see if we need to increment the time counters + if (second==60) + { + incrementtime(); + displaytime(); + } + if (DS1302Present==1) { + // Get the current time and date from the chip + Time t = rtc.time(); + second=t.sec; + minute=t.min; + hour=t.hr; + } - 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); - } + // set the brightnes level based on the current hour - night=7pm - 6.59am + // + #if (USELIGHTSENSOR == 1) + lightlevel_sample = analogRead(LIGHTSENSOR_INPUTPIN); - displaytime(); - delay(100); - } + // update average + if (n_lightlevel_samples < (1 << LIGHTSENSOR_WEIGHT)) + { + // add (1 << (LIGHTSENSOR_WEIGHT - 1)) to average before division so that + // the average is round()-ed instead of floor()-ed + lightlevel_avg = (n_lightlevel_samples * lightlevel_avg + + lightlevel_sample + (1 << (LIGHTSENSOR_WEIGHT - 1))) / + (n_lightlevel_samples + 1); + n_lightlevel_samples++; + } + else + { + // do not update n_lightlevel_samples to prevent overflow -} + // add (1 << (LIGHTSENSOR_WEIGHT - 1)) to average before division so that + // the average is round()-ed instead of floor()-ed + lightlevel_avg = ( ((1 << LIGHTSENSOR_WEIGHT) - 1) * lightlevel_avg + + lightlevel_sample + (1 << (LIGHTSENSOR_WEIGHT - 1))) >> LIGHTSENSOR_WEIGHT; + } + // compute new brightness level + /* + // linear method + if (lightlevel_avg <= LIGHTSENSOR_BOTTOM) + current_brightnes = MINBRIGHTNESS; + else if (lightlevel_avg >= LIGHTSENSOR_TOP) + current_brightnes = MAXBRIGHTNESS; + else + { + current_brightnes = (brightness_per_unit_light * (lightlevel_avg - + LIGHTSENSOR_BOTTOM)) / (1 << LIGHTSENSOR_SCALE) + MINBRIGHTNESS; + } */ + current_brightnes = ambient_light_to_brightness[lightlevel_avg]; + Serial.print("lightsensor: "); + Serial.print(lightlevel_sample); + Serial.print(" / "); + Serial.print(lightlevel_avg); + Serial.print(", brightness: "); + Serial.println(current_brightnes); + #else + if ((hour < DAYLIGHTHOUR) | (hour >= NIGHTLIGHTHOUR)) + current_brightnes=MINBRIGHTNESS; + else + current_brightnes=MAXBRIGHTNESS; + #endif + + PT_END(pt); + } +} + +void loop() +{ + // call each function continuously + wordclock(&wordclock_pt); + buttons_thread(&buttons_thread_pt); +}