ArduinoLibs
Charlieplex.cpp
00001 /*
00002  * Copyright (C) 2012 Southern Storm Software, Pty Ltd.
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a
00005  * copy of this software and associated documentation files (the "Software"),
00006  * to deal in the Software without restriction, including without limitation
00007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00008  * and/or sell copies of the Software, and to permit persons to whom the
00009  * Software is furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included
00012  * in all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
00015  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00017  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00018  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00019  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00020  * DEALINGS IN THE SOFTWARE.
00021  */
00022 
00023 #include "Charlieplex.h"
00024 #if defined(ARDUINO) && ARDUINO >= 100
00025 #include <Arduino.h>
00026 #else
00027 #include <WProgram.h>
00028 #endif
00029 #include <stdlib.h>
00030 #include <string.h>
00031 
00121 Charlieplex::Charlieplex(const uint8_t *pins, uint8_t numPins)
00122     : _count(((int)numPins) * (numPins - 1))
00123     , _lastTime(micros())
00124     , _currentIndex(-1)
00125     , _pwmPhase(0xC0)
00126 {
00127     // Determine the best hold time for 50 Hz refresh when all LED's
00128     // are lit.  Divide it again by 4 (to get 200 Hz) to manage the
00129     // simulated PWM in refresh().
00130     _holdTime = 20000 / _count / 4;
00131 
00132     // Allocate the pin arrays and populate them.  Doing this now makes
00133     // refresh() more efficient later, at the expense of some memory.
00134     _pins1 = (uint8_t *)malloc(_count);
00135     _pins2 = (uint8_t *)malloc(_count);
00136     int n = 0;
00137     for (uint8_t pass = 1; pass < numPins; ++pass) {
00138         for (uint8_t pin = 0; pin < (numPins - pass); ++pin) {
00139             _pins1[n] = _pins2[n + 1] = pins[pin];
00140             _pins2[n] = _pins1[n + 1] = pins[pin + pass];
00141             n += 2;
00142         }
00143     }
00144 
00145     // Allocate space for the LED value array and zero it.
00146     _values = (uint8_t *)malloc(_count);
00147     memset(_values, 0, _count);
00148 
00149     // Start with all pins configured as floating inputs (all LED's off).
00150     for (uint8_t pin = 0; pin < numPins; ++pin) {
00151         digitalWrite(pins[pin], LOW);
00152         pinMode(pins[pin], INPUT);
00153     }
00154 }
00155 
00159 Charlieplex::~Charlieplex()
00160 {
00161     free(_pins1);
00162     free(_pins2);
00163     free(_values);
00164 }
00165 
00277 void Charlieplex::loop()
00278 {
00279     unsigned long us = micros();
00280     if ((us - _lastTime) >= _holdTime) {
00281         _lastTime = us;
00282         refresh();
00283     }
00284 }
00285 
00296 void Charlieplex::refresh()
00297 {
00298     // Find the next LED to be lit.
00299     int prevIndex = _currentIndex;
00300     int limit = _count;
00301     while (limit >= 0) {
00302         _currentIndex = (_currentIndex + 1) % _count;
00303         if (_values[_currentIndex] != 0)
00304             break;
00305         --limit;
00306     }
00307     if (limit < 0) {
00308         // No LED's are lit.  Turn off the previous LED and exit.
00309         if (prevIndex != -1) {
00310             digitalWrite(_pins1[prevIndex], LOW);
00311             digitalWrite(_pins2[prevIndex], LOW);
00312             pinMode(_pins1[prevIndex], INPUT);
00313             pinMode(_pins2[prevIndex], INPUT);
00314         }
00315         _currentIndex = -1;
00316         return;
00317     }
00318 
00319     // Light the current LED.
00320     uint8_t value = _values[_currentIndex];
00321     uint8_t pin1 = _pins1[_currentIndex];
00322     uint8_t pin2 = _pins2[_currentIndex];
00323     _pwmPhase += 0x40;
00324     if (prevIndex != _currentIndex) {
00325         // Turn off the previous LED.
00326         if (prevIndex != -1) {
00327             digitalWrite(_pins1[prevIndex], LOW);
00328             digitalWrite(_pins2[prevIndex], LOW);
00329             pinMode(_pins1[prevIndex], INPUT);
00330             pinMode(_pins2[prevIndex], INPUT);
00331         }
00332 
00333         // We simulate PWM using a phase counter because analogWrite()
00334         // combined with holdTime() causes too much flickering if more
00335         // than one LED is lit.  This reduces the PWM resolution to 1 in 4.
00336         pinMode(pin1, OUTPUT);
00337         pinMode(pin2, OUTPUT);
00338         if (value > _pwmPhase)
00339             digitalWrite(pin1, HIGH);
00340         else
00341             digitalWrite(pin1, LOW);
00342     } else {
00343         // Same LED as previous.  Since there is only a single LED
00344         // that is lit, we can use analogWrite() to set the PWM state.
00345         if (value == 255)
00346             digitalWrite(pin1, HIGH);
00347         else
00348             analogWrite(pin1, value);
00349     }
00350 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator