Introduction: Charlieplexing Made Easy (and What It Even Means?!)
Charlieplexing. Aside from being a fancy word it is a technique for driving multiple LEDs with relatively few pins on a microcontroller. It allows you to drive N*(N-1) LEDs with just N pins without requiring any additional hardware, making it a quick, cheap and easy way to add multiple LEDs to your arduino projects.
Throughout this instructable you will see that I start the majority of steps by directing you to a corresponding simulation. I highly encourage you to play with it before reading contents of the step or looking at images.
To play the simulation, click on "simulate" icon below circuit, wait for it to load and then press "start simulation" above the circuit. Please note that at the time of writing simulation tends to fail to run unless you are logged in due to a bug.
(If you are here just for the "made easy" part of this title, feel free to skip to step 3 or just jump over to step 5 and grab my charlieplexing generator code)
Step 1: Two LEDs
The basic idea behind charlieplexing is best illustrated on example with only two pins and two LEDs. Feel free to play with this SIMULATION - toggle switches and see what happens (switches used in simulation serve to connect middle pin to either + or - ).
If you're done playing with simulation or you just want a less interactive way of learning, read on.
Unlike standard light bulbs, LEDs conduct current only in one direction. That means that LED will emit light when you connect it "correctly" and act as open circuit when connected "wrong". If you connect two LEDs in parallel with different orientations, one of the two will shine no matter the polarity. To select which LED shines, you can modulate which pin is + and which - (image 2). If you set booth to either + or -, current won't have anywhere to flow, turning both LEDs off (image 3).
You have likely noticed that I used only one resistor for two LEDs. That can be done, because electricity "sees" only one LED no matter the polarity. To calculate the resistor's value, use the standard equation or use this ONLINE CALCULATOR. Keep in mind that max current output from Arduino is 40mA (20mA recommended).
Step 2: Three LEDs
Charlieplexing with two pins is useless. It consumes two pins to drive two LEDs, and takes away the option to turn them both on simultaneously.
Fortunately it gets a lot better when you add the third pin. Using three pins for charlieplexing allows us to control 6 LEDs. If you intend to play with SIMULATION for this setup, please do it before reading on.
You have seen that as soon as you flip one switch, two LEDs turn on. No matter what you do, either none two LEDs are lit. That is because there are always two LEDs between + and - terminals (image 2).
Solution to this problem is shown in this SIMULATION. You will see that the only difference is that I added three more switches that disconnect terminals. I once again recommend you go through the simulation before reading on.
Newly added switch's sole purpose is to prevent undesired current flow. Now you can turn on only the LED you want to, but all of a sudden we see ghosting. On arduino, which runs on 5V, ghosting is noticeable when charlieplexing red or green LEDs. That is due to the fact that voltage drop on those two is less than 2.5V and thus some minor current flows through the two LEDs between + and - (image 3). If you were to implement charlieplexing on a microcontroller that runs on 3.3 volts, no ghosting would occur.
Now a word on resistors: Charlieplexing is normally conducted with as many resistors as there are pins for driving LEDs. Value of each resistor is half of the required resistance for driving a single LED the "classic" way.
And to finally address the elephant in the room: I have used three symbols on my schematics: +, - and x. How do they translate to an arduino? + and - are simple. You configure Arduino's pins as an OUTPUT and write HIGH(+) or LOW(-). Disconnected(x) state is a bit less intuitive. If you configure pin as an INPUT, it goes in so called high impedance mode (high-Z), which means that current can't flow in or out of that pin, acting somewhat as a disconnected pin. More on that in the step where I talk about Arduino implementation.
Step 3: N LEDs
Up until now schematics could have been easily drawn without any intersecting wires. For any more than three pins, that is no longer possible.
You could try to figure out how circuit in the schematic above works, or you could try out this SIMULATION. Note that I omitted switches and an Arduino instead.
You will see that LEDs in the schematic (image 1) are arranged in a NxN grid. Greyed out LEDs are omitted because those would be connected to same pin with both cathode and anode. Anodes (+) of all LEDs in first row are connected to pin1 and so are all cathodes in first column. Other diodes follow suit.
If you are wondering what you could possibly do with such arrangement of LEDs, fear not. This is just a schematic, which serves only for easy wiring. PCB layout can be very different. In this SIMULATION I took the exact same project as before and rearranged LEDs to form a circle (I also changed the code so that LEDs copy potentiometer's rotation). One more very common use for charlieplexing is in LED cubes - 6 pins allow you to drive 30 LEDs, enough for a 3x3x3 cube while 9 pins are enough for a 4x4x4 version!
Step 4: The Code
Before showing you how I make the circuits for charlieplexing applications, I would like to talk about the Arduino code for such projects. I will be going through the example with 4 pins, the same code that was used in SIMULATION from previous step. Feel free to play with it and see what happens.
As mentioned before, all pins need to be set to one of three possible states: HIGH (+), LOW (-) and HIGH-Z (unconnected). First two are easy. You configure pins as OUTPUT and use digitalWrite(pin, state) to choose between HIGH and LOW. HIGH-Z (high impedance) is a bit less intuitive, but no more difficult. Whenever a pin is configured as an input, it goes to HIGH-Z mode, which means that the pin can't sink or source any current.
It is best to start the code by defining which Arduino pin corresponds to which charlieplexing pin. By default all pins are configured as digital input pins, which means they are in HIGH-Z mode.
Next comes the function that configures pins to turn the selected LED on - charlieWrite(led). It first sets all pins to HIGH-Z. That is followed by a SWITCH statement that configures correct pins as correct level outputs. When you configure a pin as a digital output, it is set to LOW by default, which is why I only write level value to the pin I want to be set to HIGH.
Last thing to do is to fill SETUP() and LOOP() functions. SETUP() can be left empty if you don't need any additional functionality, while LOOP()'s contents depend on desired mode of operation.
In the first DEMO the code is set up to go through all LEDs with a for statement and turns them on sequentially.
Second DEMO is a little bit more advanced. It goes through all LEDs and fakes effect that any number of them are on simultaneously. LOOP() first measures potentiometer value. Through the FOR loop, that measurement is compared to a threshold that corresponds to each LED. If value is above threshold, LED is turned on, otherwise it is ignored. No matter what happened there, a same delay() is called. This ensures that LED brightness doesn't vary with number of lit LEDs. Note that it would be a lot better to compare "while (micros() - time < 1000){}" would be a better way to go, but I won't torture the simulator. Attached Arduino code is written properly.
Step 5: EAGLE CAD
Making circuits. Finally.
You could always follow te schematic from step 3 and place all LEDs and wires on schematic of your preferred circuit CAD program. That was the way I did it, until I wanted to make a 240 LED display (16 pins). To place 240 LEDs, rename them, wire them... lets just say it would be... time consuming...
Fortunately my go to PCB CAD program, Eagle CAD (free for up to 2 layers and 80cm2 area) saves it's files in an XML format. That means, I can use python to make the file I want almost automagically!
My python code generates a grid of LEDs along with all the needed wiring. You need to put the output .sch file in your project folder and finish up the schematic the way you see fit. You will want to add at least resistors and connection terminals.
From there on it is pretty much straight forward. Go to board editor, place all components and fire up auto router:)
Python script and supporting file have to be in same folder in order to work. You will need python version 3.x to run it. The script generates a "schematic.sch" file, which you can put in your eagle project folder. You can change the type of LEDs you want to use by opening the python script and changing "package" index (see code comments for more info, default is 5mm LED).
I have also included two EAGLE CAD files for a simple charlieplexed clock - same as I used to explain how to program an Arduino for a charlieplexing job. Feel free to take a look at them.
Step 6: Clock
I made a simulation for a 12 LED clock, I made schematic for a 12 LED clock, I made a PCB file for a 12 LED clock and I made the code for driving a 12 LED clock. It would've been a shame if I didn't come full circle and also make a 12 LED clock, now wouldn't it:)
I had a few problems with making a PCB because I desperately wanted to make it with a laser cutter. In the end I had to give up on that dream and go back to trusty ol' photo transfer. If you know of a way to make PCBs using a CO2 laser, please let me know in the comments, because I don't want to give up on that dream just yet.
Other than that, I made PCB to be as easy to manufacture as possible. Etch the copper, drill the holes, tinker with the board's shape, put on a dial and finally solder in the LEDs (if you miss the polarity it will still "work" bot not when you want it to), resistors and jumper wires. Note that I picked white LEDs over red and green ones when choosing the colour to avoid the ghosting effect.
Step 7: Cheat Sheet
- As mentioned in introduction, Charlieplexing allows you to drive up to N*(N-1) LEDs using only N pins on microcontroller. Keep in mind that you can always use fewer.
- Number of LEDs that are virtually lit simultaneously is limited by your microcontroler. You need to refresh all LEDs at least 60 times per second. If your microcontroller doesn't have adequate processing power, you won't be able to achieve that at higher LED counts. 240 LEDs (16 pins) can barely be achieved on Arduino, if you program it on low level.
- Select resistors wisely. You will find a max current rating for your LEDs. If you want them constantly lit (not virtually), choose resistor to limit current to that value. If you are having multiple lit simultaneously, you can choose resistors to limit current to up to 40mA (Arduino's limit). Make sure that average current through a refresh cycle won't exceed LED's rating (example: 12 LEDs with Imax = 2mA | Ioverclock/12 <2mA ==> Ioverclock = 24mA)
- Don't forget that resistors should have half the resistance for driving a single LED because current flows through two resistors.
I would like to thank you for your attention. If you have any questions, comments or suggestions, please don't hesitate to write them in the comments section below. I would also ask you to vote for me in Electronics tips & tricks challenge.
Credits: background pattern for my graphics is from Toptal Subtle Patterns, made by Atle Mo