Charlieplexing Example + Code

Hi all,

I've made just a basic 3-pin charlieplex setup and some code to get it running. The picture isn't very clear but you can see that there's only 3 pins running out from the Arduino and 6 LEDs on the breadboard. The reason that there's so many wires is that I tried to get a nice, neat row.

Here's the code:
(Can anyone please help me with the charlieplexlist function I've been trying to write? There's something wrong with me pointers)

/**********************************************************************\
|                      CHARLIEPLEXING FOR NOOBS                        |
|                          By Xepter Keedas                            |
|                                                                      |
|         You are free to use this as you wish but give credit         |
|         where credit is due. I coded all this myself with            |
|         some syntax and coding assistance provided by                |
|         Aaron Burke. Make sure you know what you're doing            |
|         in case you fry your mobo (not my fault or responsibility)   |
|                                                                      |
\**********************************************************************/



const int pin1 = 2; //defining the pins we are using
const int pin2 = 3;
const int pin3 = 4;

int led1 [3] = {pin1, pin2, pin3}; // order goes as follows:
int led2 [3] = {pin2, pin1, pin3}; // high pin, low pin, "input" pin
int led3 [3] = {pin2, pin3, pin1};
int led4 [3] = {pin3, pin2, pin1};
int led5 [3] = {pin1, pin3, pin2};
int led6 [3] = {pin3, pin1, pin2};

//int len(int list[]){
//  return sizeof(list)/sizeof(list[0]);// trying to count the length of the list for the 
//}// for loop. (my friend was doing this, not sure what it was though

int* pin [6] = {
  led1, led2, led3, led4, led5, led6}; //trying to put all the led pointers in a "list" of sorts (sorry, I mainly code in Python). Help pl0x?

void charlieplex(int list[3], int seconds){ //parameters are an array of 3 values (i.e your LED array) and a time delay (in ms)
  int time = seconds;
  digitalWrite(list[1], LOW);
  digitalWrite(list[0], HIGH);
  pinMode(list[2], INPUT);
  delay(time);
  digitalWrite(list[0], LOW);
  pinMode(list[2], OUTPUT);
}  

//void charlieplexlist(int list[], int seconds){               //something wrong with the pointers, not sure what
//  int time = seconds;
//  for (int place = 0; place<=len(list); place++) {            // need help to fix it
//    digitalWrite(list[1], LOW);
//    digitalWrite(list[0], HIGH);
//    pinMode(list[2], INPUT);
//    delay(time);
//    digitalWrite(list[0], LOW);
//    pinMode(list[2], OUTPUT);
//  }
//}
    
  
void setup(){
  pinMode(pin1, OUTPUT);
  pinMode(pin2, OUTPUT);
  pinMode(pin3, OUTPUT);
}


void loop(){
  charlieplex(led1, 1000);//Just decreases the time the LEDs are on for
  charlieplex(led2, 750);//Gives a crappy descending effect
  charlieplex(led3, 500);
  charlieplex(led4, 250);
  charlieplex(led5, 200);
  charlieplex(led6, 175);
  for (int timer = 0; timer <= 2000; timer++) {//seconds delay is whatever you want * ~200
    charlieplex(led1, 1);
    charlieplex(led2, 1);
    charlieplex(led3, 1);
    charlieplex(led4, 1);
    charlieplex(led5, 1);
    charlieplex(led6, 1);
  
  }
//charlieplexlist(pin, 1000);
}

Any comments, improvements, queries, etc I'd be more than happy to respond.

Oh, and Happy Birthday to me! 8)

  • Xepter Keedas

Here's some code I wrote to charlieplex 9 LED's on an ATTiny85:

void setup() {                     //nothing to do in setup
}

byte t = 0;                         //used for looping between LED's in a knight rider kind of fashion
void loop() {
  for (t = 0; t < 10; t++) {
    charliePlex(t);               //switch on LED number T
    delay(75);                     // Wait a bit
  }
  for (t = 9; t >= 0; t--) { // Reverse the run
    charliePlex(t);                // switch on LED number T
    delay(60);                      // delay a bit
  }
  charliePlex(10);               //switch off all LEDs I only had 9, so if you try to switch on any 
                                         //bigger number, they would all be off.
  delay(500);                      //big delay before we do it all over again
}

void charliePlex(byte i) {   // This function switches on one of the LED's. Due to the way I 
                                        // laid out my circuit, the pins are not exactly in a logical order, 
                                        // but if you understand the concept, it's easy to tune it for your circuit.
  for (int t = 0; t < 4; t++) pinMode(t, INPUT); // I used IO ports 0-4. the ATTINY85 only has 5 
                                                                    //(or 6 if you sacrifice reset) GPIO's. This loop will switch 
                                                                    // off all the LED's, because it sets the IO ports to input 
                                                                    // (high impedance) mode.
  switch (i) {                                       //decide which LED we want to switch on
    case 0: charlieSwitch(2, 0);            //The first LED is wired from Pin 2 to Pin 0
     break;
    case 1: charlieSwitch(0, 2);            // the second LED is the reverse of the LED above
     break;
    case 2: charlieSwitch(1, 0);
     break;
    case 3: charlieSwitch(0, 1);
     break;
    case 4: charlieSwitch(1, 2);
     break;
    case 5: charlieSwitch(2, 1);
     break;
    case 6: charlieSwitch(3, 2);
     break;
    case 7: charlieSwitch(2, 3);
     break;
    case 8: charlieSwitch(3, 1);
     break;
   }
}

void charlieSwitch(byte pin1, byte pin2) {            //This function merely sets the ports as output, 
                                                                      // and drives the first one HIGH and the Second one LOW
     pinMode(pin1, OUTPUT); 
     pinMode(pin2, OUTPUT);
     digitalWrite(pin1, HIGH);
     digitalWrite(pin2, LOW);
}

The function "charlieplexlist()" calls the function "len()". Looking at it, with comments removed and line numbers added:

int len(int list[]){
  return sizeof(list)/sizeof(list[0]);
}

The function has one argument, framed as an integer array. It looks like the intent of this function is to find out how many elements are in the array. It's important to note, though, that the function doesn't see the array itself - it only gets the pointer, which is the SRAM address of the first element in the array. We see the argument as "int list[]," which is clearly an array, but the compiler frames this as "int* list," so list is a pointer to integer.

This function knows nothing about the array, except the address of its first element, and that it's supposed to interpret the data it finds there as integer. In particular, it doesn't know how big the array is, in terms of either bytes or elements. So, here's what it will do:

  • Calculate the size of the integer pointer - two bytes on the Arduino.
  • Calculate the size of the first element that the pointer points to, which, because the function interprets that data as integer, will also be two bytes on the Arduino.
  • Divide the size of the pointer by the size of the first element - 2/2 - and get a result of 1.

That'll happen no matter what the argument, "list," points to. On the Arduino, this function will always return a value of 1. That's probably not what you wanted.

I don't know of a way to directly get the size of an array under program control. Whenever I use arrays with variable numbers of elements, I dimension the array to be as big as I expect it to become, count elements as I add them in the program, and pass that count to any code that manipulates the array. Maybe a programming maven will read this and chime in with a solution; however, I doubt that a direct solution exists.

A way around this difficulty is to define a constant equal to the number of elements in the array. The compiler will calculate the size of the array, and its number of elements. The compiler knows everything about the array, and it will do that math accurately. Here's a solution:

const int pinSize = sizeof(pin)/sizeof(pin[0]);

Putting this into your code, I get the expected value of 6.

In "charlieplexlist()," you can replace the reference to "len()" with the constant. If you ultimately want to use "charlieplexlist()" to use an arbitrary array of arbitrary length - maybe you envision your program calculating an LED display sequence based on some input(s) - then you'll need to count the elements as you put them into the array, and pass the element count to "charlieplexlist()" as an input.

You call "charlieplexlist()" like this:

charlieplexlist(pin, 1000);

The first argument, "pin," is an array of arrays of integer. Because arrays are represented simply by their pointers, the type of "pin" is pointer to pointer to integer, or "int**." "charlieplexlist()," though, expects an array of integers, type "int*," which isn't the same thing. In fact, this code doesn't compile under the Arduino IDE 1.0.1, and it fails with a message saying that it can't convert 'int**' to 'int*' to pass this argument to "charlieplexlist()."

My favorite way to deal with all this is to generally rearrange the program. Here's what I'd do:

  • Set the default state of the LED pins to be the high-impedance state - INPUT, LOW - to make your code more general. Right now, the default state is OUTPUT, LOW. When you turn on an LED, you set one pin to HIGH, and one to INPUT - that works great for six LEDs, but if you had a whole lot of them, you'd have to set several lines to INPUT, and then back to OUTPUT when you turn them off, and that means rewriting "charlieplexlist()" whenever you use a different number of LEDs. If the default is INPUT, then to turn on an LED, you set two lines to OUTPUT, and one of those to HIGH, and to turn it off, one line goes to LOW, and two to INPUT, and the same "charlieplexlist()" works for any number of LEDs.
  • Define "pin" as a two dimensional array - 6 by 2, for 6 LEDs, or #LEDs by two for some other number of LEDs. One of the elements is the line that goes HIGH, and one is the line that goes LOW. With the default pin state at INPUT, LOW, there's no need to keep track of pins that need to be in the high-impedance state.
  • Rewrite "charlieplexlist()" to take an array of integers, each of which points to one of the 2-element rows of "pin." Since "pin" is a global variable, its data is available inside "charlieplexlist()." The elements of the array argument to "charlieplexlist()" will then be simply the numbers of the LEDs. A verbal description of a call to "charlieplexlist()" would be something like, "light LED #3, wait a second, turn it off; light LED #4, wait a second, turn it off; blah blah blah ..."

"charlieplexlist()" would look something like this:

void charlieplexlist(int list[], int seconds, int n){
  int time = seconds;
  for (int place = 0; place<n; place++) {
    pinMode(list[0],OUTPUT);
    pinMode(list[1],OUTPUT);
    digitalWrite(list[0], HIGH);
    delay(seconds);
    digitalWrite(list[0], LOW);
    pinMode(list[1], INPUT);
    pinMode(list[0], INPUT);
  }
}

That's untested code. The usual disclaimers apply.