ArduinoLibs
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Groups Pages
Charlieplex.cpp
1 /*
2  * Copyright (C) 2012 Southern Storm Software, Pty Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "Charlieplex.h"
24 #if defined(ARDUINO) && ARDUINO >= 100
25 #include <Arduino.h>
26 #else
27 #include <WProgram.h>
28 #endif
29 #include <stdlib.h>
30 #include <string.h>
31 
121 Charlieplex::Charlieplex(const uint8_t *pins, uint8_t numPins)
122  : _count(((int)numPins) * (numPins - 1))
123  , _lastTime(micros())
124  , _currentIndex(-1)
125  , _pwmPhase(0xC0)
126 {
127  // Determine the best hold time for 50 Hz refresh when all LED's
128  // are lit. Divide it again by 4 (to get 200 Hz) to manage the
129  // simulated PWM in refresh().
130  _holdTime = 20000 / _count / 4;
131 
132  // Allocate the pin arrays and populate them. Doing this now makes
133  // refresh() more efficient later, at the expense of some memory.
134  _pins1 = (uint8_t *)malloc(_count);
135  _pins2 = (uint8_t *)malloc(_count);
136  int n = 0;
137  for (uint8_t pass = 1; pass < numPins; ++pass) {
138  for (uint8_t pin = 0; pin < (numPins - pass); ++pin) {
139  _pins1[n] = _pins2[n + 1] = pins[pin];
140  _pins2[n] = _pins1[n + 1] = pins[pin + pass];
141  n += 2;
142  }
143  }
144 
145  // Allocate space for the LED value array and zero it.
146  _values = (uint8_t *)malloc(_count);
147  memset(_values, 0, _count);
148 
149  // Start with all pins configured as floating inputs (all LED's off).
150  for (uint8_t pin = 0; pin < numPins; ++pin) {
151  digitalWrite(pins[pin], LOW);
152  pinMode(pins[pin], INPUT);
153  }
154 }
155 
160 {
161  free(_pins1);
162  free(_pins2);
163  free(_values);
164 }
165 
278 {
279  unsigned long us = micros();
280  if ((us - _lastTime) >= _holdTime) {
281  _lastTime = us;
282  refresh();
283  }
284 }
285 
297 {
298  // Find the next LED to be lit.
299  int prevIndex = _currentIndex;
300  int limit = _count;
301  while (limit >= 0) {
302  _currentIndex = (_currentIndex + 1) % _count;
303  if (_values[_currentIndex] != 0)
304  break;
305  --limit;
306  }
307  if (limit < 0) {
308  // No LED's are lit. Turn off the previous LED and exit.
309  if (prevIndex != -1) {
310  digitalWrite(_pins1[prevIndex], LOW);
311  digitalWrite(_pins2[prevIndex], LOW);
312  pinMode(_pins1[prevIndex], INPUT);
313  pinMode(_pins2[prevIndex], INPUT);
314  }
315  _currentIndex = -1;
316  return;
317  }
318 
319  // Light the current LED.
320  uint8_t value = _values[_currentIndex];
321  uint8_t pin1 = _pins1[_currentIndex];
322  uint8_t pin2 = _pins2[_currentIndex];
323  _pwmPhase += 0x40;
324  if (prevIndex != _currentIndex) {
325  // Turn off the previous LED.
326  if (prevIndex != -1) {
327  digitalWrite(_pins1[prevIndex], LOW);
328  digitalWrite(_pins2[prevIndex], LOW);
329  pinMode(_pins1[prevIndex], INPUT);
330  pinMode(_pins2[prevIndex], INPUT);
331  }
332 
333  // We simulate PWM using a phase counter because analogWrite()
334  // combined with holdTime() causes too much flickering if more
335  // than one LED is lit. This reduces the PWM resolution to 1 in 4.
336  pinMode(pin1, OUTPUT);
337  pinMode(pin2, OUTPUT);
338  if (value > _pwmPhase)
339  digitalWrite(pin1, HIGH);
340  else
341  digitalWrite(pin1, LOW);
342  } else {
343  // Same LED as previous. Since there is only a single LED
344  // that is lit, we can use analogWrite() to set the PWM state.
345  if (value == 255)
346  digitalWrite(pin1, HIGH);
347  else
348  analogWrite(pin1, value);
349  }
350 }