loading
This instructable was inspired by my first AVR microcontroller project that I've been working on for some time now. I wanted to start learning more about the AVR microcontroller and see how much I could do with the minimum amount of hardware... no extra chips, simple components, etc... just my AVR and my code so I could experiment with a simple set of hardware.

I decided to start with one of the smallest AVRs available, the ATTiny85 with only 8 pins. With only 5 output pins available to me (using 6 would make me lose other chip functionality), I decided to experiment with Charlieplexing. Charlieplexing allows you to control many LEDs with very few output pins, by wiring up an LED in both directions to every possible pair-combination of the pins. In my case, 5 pins would allow control of up to 20 LEDs (5 x (5-1)). This would only require the ATTiny, LEDs, 5 resistors, and a power source.

More info on Charlieplexing:  http://en.wikipedia.org/wiki/Charlieplexing

Step 1: Planning Out the Wiring

Conceptually, it's not too difficult to understand how to wire up charlieplexing from some of the simpler diagrams available online (see diagram below courtesy of Wikipedia). However, I wanted to arrange 20 LEDs in a tight grid, and managing all the different connections & polarities seemed like it would get complicated as I scaled up from the 2 or 3 pin examples. Most importantly, to avoid mistakes when assembling, I wanted all my LEDs to be aligned the same direction on the board.

In the next step you'll see how the wiring works out for a larger grid of LEDs.

Step 2: Running Bus-lines to All LEDs in the Grid

After some thinking and sketching, I came up with my concept. In this project we're working with 5 pins. Lets call them A, B, C, D and E.

Let's just focus on pin A for a moment. If pin A is set to a positive voltage, it could control up to 4 different LEDs connected to ground on pins B through E. So I should have one line connecting the 4 positive ends (Anodes) of LEDs to pin A. This same concept extends to all the other pins. If Pin B was positive, it could control 4 others on pins A, C, D, and E. With this in mind, I planned out a positive "bus-line" for each column of 4 LEDs. These are shown in blue in the diagram below.

The negative (anode) connections can be handled on the other side of the board, as bus-lines between each row, feeding to LEDs on either side of the line as needed. These are shown in red in the diagram below.

All that's left is to connect the respective bus-lines to each other so they all connect back to the same 5 pins. These connections are shown in green in the diagram below.

Finally, a resistor is placed between each line and the microcontroller to limit current through the LEDs.

While it's a little complicated to solder by hand, it can be managed. Just go slowly and double check your connections as you go.

Step 3: Charlieplexing in Software - Getting Started

In my project, I used the 4x5 grid to run a Conways Game of Life simulation. However, before we get that complex, let's cover some basics of the software to show how we light LEDs in this charlieplexed setup.

First, I've defined my pins A through E and specified which bit on PORTB they will be referring to. This makes it easier to refer to Line A through E later in the code:

#define LINE_A 0 //Pin 5 (PB0) on ATtiny85
#define LINE_B 1 //Pin 6 (PB1) on ATtiny85
#define LINE_C 2 //Pin 7 (PB2) on ATtiny85
#define LINE_D 3 //Pin 2 (PB3) on ATtiny85
#define LINE_E 4 //Pin 3 (PB4) on ATtiny85


In order to light any one of the 20 LEDs, we need to configure our 5 pins a different way for each LED. To light one LED, we need one pin set to an output with a high voltage, one pin set to an output with a ground voltage, and all the other pins need to be set to inputs to prevent current flow.

To make it simpler, we'll set up some arrays to store all the configurations for DDRB (which sets the input/output modes of each pin) and PORTB (which sets the high/low voltage of each pin).

//DDRB direction config for each LED (1 = output)
const char led_dir[20] = {
  ( 1<<LINE_A | 1<<LINE_E ), //LED 0
  ( 1<<LINE_B | 1<<LINE_E ), //LED 1
  ( 1<<LINE_C | 1<<LINE_E ), //LED 2
  ( 1<<LINE_D | 1<<LINE_E ), //LED 3
  ( 1<<LINE_E | 1<<LINE_D ), //LED 4

  ( 1<<LINE_A | 1<<LINE_D ), //LED 5
  ( 1<<LINE_B | 1<<LINE_D ), //LED 6
  ( 1<<LINE_C | 1<<LINE_D ), //LED 7
  ( 1<<LINE_D | 1<<LINE_C ), //LED 8
  ( 1<<LINE_E | 1<<LINE_C ), //LED 9

  ( 1<<LINE_A | 1<<LINE_C ), //LED 10
  ( 1<<LINE_B | 1<<LINE_C ), //LED 11
  ( 1<<LINE_C | 1<<LINE_B ), //LED 12
  ( 1<<LINE_D | 1<<LINE_B ), //LED 13
  ( 1<<LINE_E | 1<<LINE_B ), //LED 14

  ( 1<<LINE_A | 1<<LINE_B ), //LED 15
  ( 1<<LINE_B | 1<<LINE_A ), //LED 16
  ( 1<<LINE_C | 1<<LINE_A ), //LED 17
  ( 1<<LINE_D | 1<<LINE_A ), //LED 18
  ( 1<<LINE_E | 1<<LINE_A ) //LED 19
};

//PORTB output config for each LED (1 = High, 0 = Low)
const char led_out[20] = {
  ( 1<<LINE_A ), //LED 0
  ( 1<<LINE_B ), //LED 1
  ( 1<<LINE_C ), //LED 2
  ( 1<<LINE_D ), //LED 3
  ( 1<<LINE_E ), //LED 4

  ( 1<<LINE_A ), //LED 5
  ( 1<<LINE_B ), //LED 6
  ( 1<<LINE_C ), //LED 7
  ( 1<<LINE_D ), //LED 8
  ( 1<<LINE_E ), //LED 9

  ( 1<<LINE_A ), //LED 10
  ( 1<<LINE_B ), //LED 11
  ( 1<<LINE_C ), //LED 12
  ( 1<<LINE_D ), //LED 13
  ( 1<<LINE_E ), //LED 14

  ( 1<<LINE_A ), //LED 15
  ( 1<<LINE_B ), //LED 16
  ( 1<<LINE_C ), //LED 17
  ( 1<<LINE_D ), //LED 18
  ( 1<<LINE_E ) //LED 19
};


Finally, we have one simple function to make this work, "light_led"

void light_led(char led_num) { //led_num must be from 0 to 19
 DDRB = led_dir[led_num];
 PORTB = led_out[led_num];
}


void leds_off() {
 DDRB = 0;
 PORTB = 0; 
}


By calling light_led with a number of 0 to 19, we can light the desired LED. From here we can build more complexity into the software to store a 4x5 grid and display it in lights.

Step 4: Charlieplexing in Software - Displaying an Image

Next in our code-building journey we're going to set up a 4x5 grid in memory. By changing the values in this grid, and then running a draw routine, our image will be displayed in LED lights.

Here we'll set up our variable to hold the 20 values (pixels) for our LED image. The default is shown below (all 0's). This would display as a dark image, with all LEDs turned off, but we can change this in software.

char led_grid[20] = {
 000 , 000 , 000 , 000 , 000 ,
 000 , 000 , 000 , 000 , 000 ,
 000 , 000 , 000 , 000 , 000 ,
 000 , 000 , 000 , 000 , 000
};


I'm allowing this variable to store more than just 1's and 0's, but larger numbers too. I want to make my LEDs dimmer at times. So I've decided for this project a value of 100 will be full-brightness, and 0 will be off.

The following function will loop through all 20 positions in the led_grid and if the value of each pixel is above zero, the appropriate LED will be lit. Since you can only light one LED at a time, the image must be re-drawn constantly, and fast enough that you can't notice the strobing/flickering with your eyes. Typically this won't be a problem with the speeds you can run on a microcontroller.

void draw_frame(void){
 char led, bright_val, b;
 for ( led=0; led<=19; led++ ) {
  //software PWM
  bright_val = led_grid[led];
  for( b=0 ; b < bright_val ; b+=4 ) { light_led(led); } //delay while on
  for( b=bright_val ; b<100 ; b+=4 ) { leds_off(); } //delay while off
 }
}

Step 5: Charlieplexing in Software - Adding Complexity

At this point we have a way to create an image in memory and display it on our charlieplexed LED grid. From here you can use your imagination and experiment with your programming code.

For my project, I chose to create a Conways Game of Life simulation. I create a random configuration of LEDs and let them run through each generation until it dies, stays steady, or loops. Once any of these is detected, it resets and starts over. I've included my code in the attached file if you'd like to review, but I would encourage you to experiment and come up with your own animations.

More information on my project can be found on my blog:
http://b2ben.blogspot.com/search/label/TinyChuck5

Thanks for reading!
 
<p>This is great! I made the Fireflies in Jar project with attiny85 but the project has 6 LEDs only. My wife wanted more! :) so I made one with 2 attiny85s. Now I can modify it using only one MCU and 20 LEDs! All I have to do is figure out the programming part of it. Thanks for the great instructable article!</p>
<p>actually you only need one MCU.</p>
<p>I did mine on cotton with an embroidery hoop, quick and dirty with no resistors! Thanks for sharing!</p>
<p>Thanks man, great project :-) </p><p>(unfortunately I only had 19 red leds...)</p>
<p>Awesome! =D Great project!</p>
Awesome Instructable! Code worked like a charm too. Plus it got me to finally start using the Programmers Notepad and WinAvr. Thanks! Here's a quick little video I shot of the results: http://youtu.be/T_uqTmaTCpg
HI Please what resistors are necesary for this desing? We simple red leds, 1,6v<br>Thanks
do the resistors go in beteen the leds??? :( :(<br>
Not exactly, for each LED, the flow of electricity essentially follows this path:<br>(Positive Voltage) &gt; (Resistor) &gt; (LED) &gt; (Resistor) &gt; (Ground)
in the very first picture there are only 5 resistors and in the charlieplexed diargram there are 6 Im a bit confused with that .<br>
All of the diagrams of my circuit have 5 resistors. The Charlieplexing diagram in step two is just an example showing how to charlieplex using 3 resistors and 6 LEDs.
Hi Im currently doing GCSE ELECTRONICS KS4(year10).I really l really love your project and want to make a same on for my GCSE.if u could please tell me what type of microcontroller and the chip will i need to do it.MANY THANKS
This project was built using the ATTiny85 microcontroller chip
That's fantastic. You've simplified the wiring, and the implementation in general, nicely.

About This Instructable

34,691views

102favorites

License:

Bio: Geek, Developer, Maker, Tinkerer, Dad
More by benbrandt22:LED Backlit Sign Made From a Free Flashlight Walnut Box With Aluminum Splines Custom Wood & Aluminum Magnets From a Hard Drive 
Add instructable to: