ArduinoLibs
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends 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 }
~Charlieplex()
Destroys this charlieplexed array.
Charlieplex(const uint8_t *pins, uint8_t numPins)
Constructs a new charliexplexing array where the output pins are specified by the numPins entries in ...
void loop()
Runs the multiplexing loop, to display the LED states on the charlieplexed array. ...
void refresh()
Refreshes the charlieplexed array by advancing to the next LED that needs to be lit.