Charlieplexing Questions

Hi All,
I have acquired some code online regarding Charlieplexing as i need around 70 LEDs for a project I'm working on. At any one time, 30-35 are likely to be lit. Then a switch is thrown and the one LED turns off and another turns on.

When running the program, (16 are connected so far) the LED lights individually and it cycles through, lighting one after the other, then restarts - as the program is supposed to. However, when I type
lightOn( 0 );
It lights the first LED correctly, however, if I type
lightOn( 1 );
It starts being odd with the lights.
If I ask it to turn on lights 0 and 2 it will light 0, 1 and 2, but 1 and 2 are dimmer.
I'm a bit confused with it to be honest, and I'm definitely no expert. Does anyone have any advice?

#define G 45                    // We call pin #46 A
#define H 46                    // "   "   "   #47 B
#define I 47                    // "   "   "   #48 C
#define J 48                    // "   "   "   #49 D
#define K 49
#define L 50
#define M 51
#define N 52
#define O 53

#define PIN_CONFIG 0            // Setup our Boolean config for the 9 pins
#define PIN_STATE 1             // Setup our Boolean state for the 9 pins

#define LED_Num 72              // How many LEDs are we using



int matrix[LED_Num][2][9] = {
  //           PIN_CONFIG                                                        PIN_STATE
  //    G       H       I      J      K      L      M      N      O         G     H    I    J    K    L    M    N    O
  { { OUTPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT }, { HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //1
  { { OUTPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //2
  { { INPUT, OUTPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //3
  { { INPUT, OUTPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW } }, //4
  { { INPUT, INPUT, OUTPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW } }, //5
  { { INPUT, INPUT, OUTPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW } }, //6
  { { INPUT, INPUT, INPUT, OUTPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW } }, //7
  { { INPUT, INPUT, INPUT, OUTPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW } }, //8
  { { INPUT, INPUT, INPUT, INPUT, OUTPUT, OUTPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW } }, //9
  { { INPUT, INPUT, INPUT, INPUT, OUTPUT, OUTPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW } }, //10
  { { INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, OUTPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW } }, //11
  { { INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, OUTPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW } }, //12
  { { INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, OUTPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW } }, //13
  { { INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, OUTPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW } }, //14
  { { INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, OUTPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW } }, //15
  { { INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, OUTPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH } }, //16
  { { OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH } }, //17
  { { OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT }, { HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //18
  { { OUTPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT }, { HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //19
  { { OUTPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW } }, //20
  { { OUTPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT }, { HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //22
  { { OUTPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW } }, //23
  { { OUTPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT }, { HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //24
  { { OUTPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW } }, //25
  { { OUTPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT }, { HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //26
  { { OUTPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW } }, //27
  { { OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT }, { HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //28
  { { OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW } }, //29
  { { OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT }, { HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //30
  { { OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW } }, //31
  { { INPUT, OUTPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //32
  { { INPUT, OUTPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW } }, //33
  { { INPUT, OUTPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //34
  { { INPUT, OUTPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW } }, //35
  { { INPUT, OUTPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT }, { LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //36
  { { INPUT, OUTPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW } }, //37
  { { INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT }, { LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //38
  { { INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW } }, //39
  { { INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT }, { LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //40
  { { INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW } }, //41
  { { INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT }, { LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW } }, //42
  { { INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH } }, //43
  { { INPUT, INPUT, OUTPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW } }, //44
  { { INPUT, INPUT, OUTPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW } }, //45
  { { INPUT, INPUT, OUTPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT }, { LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW } }, //46
  { { INPUT, INPUT, OUTPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW } }, //47
  { { INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT }, { LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW } }, //48
  { { INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW } }, //49
  { { INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT }, { LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW } }, //50
  { { INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW } }, //51
  { { INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT }, { LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW } }, //52
  { { INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH } }, //53
  { { INPUT, INPUT, INPUT, OUTPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW } }, //54
  { { INPUT, INPUT, INPUT, OUTPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW } }, //55
  { { INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT }, { LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW } }, //56
  { { INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW } }, //57
  { { INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT }, { LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW } }, //58
  { { INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW } }, //59
  { { INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT }, { LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW, LOW } }, //60
  { { INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH } }, //61
  { { INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, OUTPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW } }, //62
  { { INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, OUTPUT, INPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW } }, //63
  { { INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, OUTPUT, INPUT }, { LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW } }, //64
  { { INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, OUTPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW } }, //65
  { { INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, OUTPUT }, { LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW, LOW } }, //66
  { { INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, INPUT, OUTPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH } }, //67
  { { INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, OUTPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW } }, //68
  { { INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, OUTPUT, INPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW } }, //69
  { { INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, OUTPUT }, { LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW, LOW } }, //69
  { { INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, INPUT, OUTPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH } }, //70
  { { INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, OUTPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW, LOW } }, //71
  { { INPUT, INPUT, INPUT, INPUT, INPUT, INPUT, OUTPUT, INPUT, OUTPUT }, { LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH } }  //72
};


void

lightOn( int led ) {
  pinMode( G, matrix[led][PIN_CONFIG][0] );
  pinMode( H, matrix[led][PIN_CONFIG][1] );
  pinMode( I, matrix[led][PIN_CONFIG][2] );
  pinMode( J, matrix[led][PIN_CONFIG][3] );
  pinMode( K, matrix[led][PIN_CONFIG][4] );
  pinMode( L, matrix[led][PIN_CONFIG][5] );
  pinMode( M, matrix[led][PIN_CONFIG][6] );
  pinMode( N, matrix[led][PIN_CONFIG][7] );
  pinMode( O, matrix[led][PIN_CONFIG][8] );
  digitalWrite( G, matrix[led][PIN_STATE][0] );
  digitalWrite( H, matrix[led][PIN_STATE][1] );
  digitalWrite( I, matrix[led][PIN_STATE][2] );
  digitalWrite( J, matrix[led][PIN_STATE][3] );
  digitalWrite( K, matrix[led][PIN_STATE][4] );
  digitalWrite( L, matrix[led][PIN_STATE][5] );
  digitalWrite( M, matrix[led][PIN_STATE][6] );
  digitalWrite( N, matrix[led][PIN_STATE][7] );
  digitalWrite( O, matrix[led][PIN_STATE][8] );
}

void setup() {
  Serial.begin(9600);
}


void loop() {
  for ( int l = 0; l < LED_Num; l++ ) {
    lightOn( l );
    delay( 20000 / LED_Num );
  }
}

how are your LEDs wired?

I think you have to set all of the pins to INPUT and LOW before you can safely start setting other pins to OUTPUT.

presumably a convention approach for multiplexing LEDs is

  • configure the row bits as OUTPUT
  • configure the column bits that are disabled to INPUT
  • configure the column bits that are enabled to OUTPUT, LOW
  • set the row bit to HIGH to turn on an LED

don't understand the purpose of the 3-dimensional matrix. it make sense to have a 2-dimensional (row,col) boolean matrix indicating which LEDs are on

and there is a scan routine that is continually disabling column, setting row bits for each column and enabling the column bit for a specific amount of time. presumably each complete scan (all columns) is ~20 msec

and there is something the set/clears LED bits in the 2D boolean matrix

Charlieplexing is NOT a conventional approach to multiplexing LEDs. Each pair of pins has two LEDs between them in opposite directions. You can turn on only one LED at a time but you can control more LEDs with the same number of pins. A convention matrix would require about 17 pins instead of 9 for 70 LEDs. Refreshing fast enough allows the appearance of multiple LEDs on "at the same time".

You are toggling the LEDs at the same rate as the multiplex scan. That is wrong. Slow it down to human readable speed.

so there's a 2nd scan with the roles of the row and col pins reversed

You can do that, but you aren't minimizing drive pins unless the connections are combinatorial.

what does that mean?

The number of LEDs in a Charlieplexed array is 'n!' where n is the number of pins. So 3 pins gives you 3x2x1 == 6 LEDs.

Put that code back in the dumpster where you found it.

Tell us more about your LEDs and how you want to use them. Are they arranged as an array, eg. 7x10? Clearly you are using a Mega, so have plenty of pins. Why charlieplex? Just for fun/learning?

If you want half of them lit at the same time, they will be pretty dim unless you use some transistors to boost the current.

Come on now, don't hold back. :slight_smile:

2 Likes

So... 9 pins could drive 362,880 LEDs? No!

With n pins you can drive n * (n - 1) LEDs.

I have 35 switches, each switch controls - via an i2c - a servo. I need to know whether or not a servo is in state A or state B. So the lights are needed to show me whether each servo is in position A or B, as in one LED or the other LED would be lit.

This code, once I get it working, will get inserted into the main code - but currently it doesn't work. It works in terms of individually lighting up the LEDs in a sequence, as the code above, but as soon as I pop in the line "lightOn( 1 );" which should (as far as I know) just light up the one LED, it doesn't quite work. Well, it works for one LED, but not multiple.

If there is a simpler method of doing it, then I'm happy to change. But I need the MEGA because quite a lot of the pins are taken up with the digital switch inputs, there are a few others taken up with the i2c and the display, but I need to use charlieplexing (or multiplexing if that works) due to the number of pins left over.

...like a dirt cheap LED controller IC? Or breakout board such as HT16k33?

If I were you I would just get an ht16k33 module. They can light up to 128 LEDs and connect to the Arduino using i²c, so no extra Arduino pins required.

still not clear. got a link?

wikipaedia covers it well

https://en.wikipedia.org/wiki/Charlieplexing

Oh dear. How did you implement that? Please say they are multiplexed.

No idea how to do that sadly..... so currently, individually connected. I'm still learning!!!