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
@ -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<<CS22);
TCCR2B &= ~((1<<CS21) | (1<<CS20));
// Use normal mode
TCCR2B &= ~((1<<WGM21) | (1<<WGM20));
// Use internal clock - external clock not used in Arduino
ASSR |= (0<<AS2);
//Timer2 Overflow Interrupt Enable
TIMSK2 |= 1<<TOIE2;
RESET_TIMER2;
sei();
// Initialise Timer2: Timer Prescaler /64,
TCCR2A = 0;
TCCR2B |= (1<<CS22);
TCCR2B &= ~((1<<CS21) | (1<<CS20));
// Use normal mode
TCCR2B &= ~((1<<WGM21) | (1<<WGM20));
// Use internal clock - external clock not used in Arduino
ASSR |= (0<<AS2);
//Timer2 Overflow Interrupt Enable
TIMSK2 |= 1<<TOIE2;
RESET_TIMER2;
sei();
Serial.println("Finished setting up Timer2 Interrupt");
Serial.println("Finished setting up Timer2 Interrupt");
msTick=millis(); // Initialise the msTick counter
msTick=millis(); // Initialise the msTick counter
#if (SKIPSELFTEST != 1)
selftest(); // validate the hardware for the user
#endif
#if (SKIPSELFTEST != 1)
selftest(); // validate the hardware for the user
#endif
selftestmode=0;
selftestmode=0;
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) {
// Get the current time and date from the chip
Time t = rtc.time();
second=t.sec;
minute=t.min;
hour=t.hr;
}
displaytime(); // display the current time
displaytime(); // display the current time
}
// Interrupt handler - Arduino runs at 16 Mhz, so we have 1000 Overflows per second...
// 1/ ((16000000 / 64) / 256) = 1 / 1000
ISR(TIMER2_OVF_vect) {
RESET_TIMER2;
if (timercount-- <= current_brightnes){
// Now we write the actual values to the hardware
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, Display3);
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, Display2);
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, Display1);
digitalWrite(LEDStrobePin,HIGH);
//delay(2);
digitalWrite(LEDStrobePin,LOW);
digitalWrite(LED1PIN,Led1);
digitalWrite(LED2PIN,Led2);
digitalWrite(LED3PIN,Led3);
digitalWrite(LED4PIN,Led4);
}
else
{
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, 0);
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, 0);
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, 0);
digitalWrite(LEDStrobePin,HIGH);
//delay(2);
digitalWrite(LEDStrobePin,LOW);
digitalWrite(LED1PIN,0);
digitalWrite(LED2PIN,0);
digitalWrite(LED3PIN,0);
digitalWrite(LED4PIN,0);
}
if (timercount==0) timercount=N_PWM_STEPS-1;
RESET_TIMER2;
if (timercount-- <= current_brightnes){
// Now we write the actual values to the hardware
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, Display3);
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, Display2);
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, Display1);
digitalWrite(LEDStrobePin,HIGH);
//delay(2);
digitalWrite(LEDStrobePin,LOW);
digitalWrite(LED1PIN,Led1);
digitalWrite(LED2PIN,Led2);
digitalWrite(LED3PIN,Led3);
digitalWrite(LED4PIN,Led4);
}
else
{
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, 0);
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, 0);
shiftOut(LEDDataPin, LEDClockPin, MSBFIRST, 0);
digitalWrite(LEDStrobePin,HIGH);
//delay(2);
digitalWrite(LEDStrobePin,LOW);
digitalWrite(LED1PIN,0);
digitalWrite(LED2PIN,0);
digitalWrite(LED3PIN,0);
digitalWrite(LED4PIN,0);
}
if (timercount==0)
timercount=N_PWM_STEPS-1;
}
void ledsoff(void) {
Display1=0;
Display2=0;
Display3=0;
Led1=0;
Led2=0;
Led3=0;
Led4=0;
Display1=0;
Display2=0;
Display3=0;
Led1=0;
Led2=0;
Led3=0;
Led4=0;
}
void incrementtime(void){
// 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(" ");
// 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);
}