Charlieplexing LEDs with an AVR ATmega328

This is the first in a two-part series. This first article made me realize there's a lot of ground to cover and we're already in the tl;dr territory.UPDATE: The MAKE Magazine Blog asked me to make this into a Project so you can also read the adapted version over there: http://blog.makezine.com/2013/05/24/controlling-leds-with-charlieplexing/

How many times has this happened to you? You have a little LED project with an AVR ATmega328 microcontroller (or Arduino) at its core and you need to light up a boatload.... A dingyload of LEDs. Maybe it doesn't happen a lot to you. It's happened on three recent projects for me. My latest two LED projects are a timekeeping piece that illuminates 21 characters from behind and a simple LED chaser thing.

As usual I wanted to keep the component count down on these projects. I also tend to prefer not to use a ton of ICs with busses between them and whatnot, if I can help it. So much darn soldering and stuff. Meh. Luckily, back in 1995, so the Wikipedia story goes, a super-smart dood named Charlie Allen at Maxim Integrated devised a super-ingenius way to control a large number of LEDs using a not-so-large number of microcontroller pins. The method is called, "Charlieplexing" and it seems a but daunting, at first, but it's not that bad once you figger it out.

The core of this concept is based on the fact that diodes only allow current to flow in one direction. The "D" in "LED" stands for "diode." Ergo, LEDs are diodes. LEDs only light up when connected a particular direction. Put two diodes in parallel BUT in the opposite-facing direction and you can turn on one or the other simply by switching the polarity of the ends of the circuit. Fun! Diagram:

Two LEDs in Parallel
Two LEDs in Parallel

Let's assume I have a 3V little coin cell running this thing and limiting resistors and CCRs are not our thing, today. Why not? Anyhoo... Looking at the diagram, you see I'm using GPIO pins from an Atmel ATmega168 AVR microcontroller (it was readily available in the library in Fritzing, the program I used to throw together the drawring diagram schematic thing above). If I configger A0 and A1 as outputs and then set A1 HIGH and A0 LOW, I can turn on LED1. If I flip the states of the two pins and make A1 LOW and A0 HIGH, LED2 will light. Here's a animated GIF of how it works with direct connections from the LEDs to 3V lithium coin cell on a breadboard:

Parallel opposite polarity LEDs flipping state
Parallel opposite polarity LEDs flipping state

Watch carefully and you'll see that I switched the positive and negative to make one or the other LED light. Easy as pie. Imagine each wire from the battery is a GPIO pin from a microctonroller. All I'm doing up there is making one HIGH and one LOW and then flipping them back and forth.

Now, to expand this is to Charlieplex. Let's start with a schematic:

Charlieplexing Schematic: 3 Pins, 6 LEDs
Charlieplexing Schematic: 3 Pins, 6 LEDs

If you start working out the formula, you'll see that the pins-to-LEDs formula looks like this: LEDs = Pins * (Pins - 1). In the case of this latest schematic, we're gonna have 3 pins * 2 pins = 6 LEDs. The table for turning on each LED looks like this:

Logic Table for 3 Pins, 6 LED
Logic Table for 3 Pins, 6 LED

And here is that circuit on a breadboard (pins simulated with +3V and GND, of course):

Breadboard: Charlieplexing 6 LED Sequence with 3 Pins
Breadboard: Charlieplexing 6 LED Sequence with 3 Pins

Here's where the trickery happens: When there are more than two pins involved, you must employ the tri-state nature of microcontroller pins. What's this mean? Hell if I know the details. In layman's terms, it means you put the pins you're NOT using at the moment into their input (or, "high-Z") state so that they are at high impedance. In other words, pins in high-Z (or input mode) do not contribute to or mess up the actions of the other pins they might be connected to. On the breadboard above, "Z" shows as not-connected ("NC"), but on a microcontroller, it would in fact be connected and we have to use the input pin mode for pins not busy doing anything for the currently lit LED. For example, while turning on and off LED 5 and LED 6, A1 is hanging out in the middle with the potential to suck power or add power onto the A2 or A0 lines. Not good. By setting the A1 pin as in INPUT, it will not mess with the voltages on A2 or A0 and that is what allows us to tie all these pins together through LEDs without causing weird combinations of LEDs to turn on.

Let's add one more pin for funzies. This will give us the ability to drive 12 LEDs because 4 pins * (4 pins - 1 pin) = 12. Hooray for math! Schematic:

Schematic: Charlieplexing 12 LEDs with 4 GPIO Pins
Schematic: Charlieplexing 12 LEDs with 4 GPIO Pins

So, yeah, this stuff gets cornfuzing fast. But, what you see up there is that each pin should connect to each other pin in two directions (two LEDs facing opposite ways) to maximize the number of LEDs. I'll show you a quality control chart I use in a second to help me make sure everything is there and connected correctly.

Here's an example of a 5-pin Charlieplexed LED array for a project I'm working on and for which I will write an article later:

20 Charlieplexed LEDs on a PCB
20 Charlieplexed LEDs on a PCB

As a warning, routing on a single sided PCB for a Charlieplexing scheme is not trivial, but not too bad as long as your LED count isn't too high. In the photo above, the the right half of the PCB is the 20 Charlieplexed LEDs (technically 21 LEDs because I needed just one extra LED and it got its own pins). Notice there are what look like three resistors mixed in there. I'm using 0Ω (zero Ohm) resistors as jumpers over traces to avoid going to a double-sided board. Mostly because it's easier to solder three SMD resistors to a board than it is to align two sides of a double-sided PCB. The traces running from the ATmega328 on the left side of the board to the LEDs are connected kinda like the schematic above: I labeled them "A" to "E" from top to bottom in the photograph. The bottom two traces are for the LED in the lower-right corner. The components on the left of the board make up a homemade Arduino-compatible circuit. The pads on the very left of the board eventually got a male pin header used to program and power this prototype board.

There are caveats to Charlieplexing. I suggest you read the Wikipedia article on the topic. I won't rewrite the wheel here. The article there does a fine job of explaining the bummers associated with the concept. Overall, though, if you're lighting a single LED at a time, this method works great.

In the next article in this two-part series, I'll post the code and show that thing above working. It's neat and fun.