64 pixel RGB LED Display - Another Arduino Clone
12 Steps
This display is based on an 8x8 RGB LED Matrix. For testing purposes it was connected to a standard Arduino board (Diecimila) using 4 shift registers. After getting it to work I permatized it on a fabbed PCB. The shift registers are 8-bit wide and are easily interfaced with the SPI protocol. Pulse width modulation is used to mix the colors, more on that later. Part of the MCU`s RAM is used as a framebuffer to hold the image. The video RAM is parsed by an interrupt routine in the background, so the user can do other useful things like talking to a PC, read buttons and potentiometers.

Remove these ads by Signing Up

## Step 1: Pulse width modulation for mixing colors

Pulse width modu - WHAT ?

Pulse width modulation essentially is turning the power fed to an electrical device ON and OFF pretty quickly. The usable power results from the mathematical average of the square-wave function taken over the interval of one period. The longer the function stays in the ON position, the more power you get. PWM has the same effect on the brightness of LEDs as a dimmer on AC lights.

The task ahead is to individually control the brightness of 64 RGB LEDS ( = 192 single LEDs ! ) in a cheap and easy way, so one can get the whole spectrum of colors. Preferably there should be no flickering or other disturbing effects. The nonlinear perception of brightness exhibited by the human eye will not be taken into account here ( e.g. the difference between 10% and 20% brightness seems "bigger" than between 90% and 100% ).

Image (1) illustrates the working principle of the PWM algorithm. Say the code is given a value of 7 for the brightness of LED(0,0). Furthermore it knows there is a maximum of N steps in brightness. The code runs N loops for all possible levels of brightness and all necessary loops to service every single LED in all rows. In case the loop counter x in the brightness loop is smaller than 7, the LED is turned on. If it's larger than 7, the LED is turned off. Doing this very quickly for all LEDs, brightness levels and base colors (RGB), each LED can be individually adjusted to show the desired color.

Measurements with an oscilloscope have show that the display refresh code takes about 50% CPU time. The rest can be used to do serial communication with a PC, read buttons, talk to an RFID reader, send I2C data to other modules...
 1-40 of 108 Next »
devilmaycry says: Jul 27, 2011. 12:12 PM
Hi Im currently doing GCSE ELECTRONICS KS4 AND i WANT make a project like your one but the problem is I want to make a Arduino Clone if u guys know any step by step instructable for beginners cos im new to micro controllers.MANY THANKS
Tone341 says: Oct 17, 2012. 6:30 PM
http://www.theparsley.com/arduino/diy/
madworm (author) says: Jul 27, 2011. 4:34 PM
Well, the good news is that there is nothing special about making an arduino clone, no magic involved. A minimal working board is just a few parts (no on-board usb).

I don't know if you're shooting at making your own pcb with smd parts or through hole components. There's also nothing special about that, except that it costs serious money to have pcbs fabbed (more than just a few) and getting a non-functional board is quite upsetting. So you'll want to make sure you have a functional prototype of your schematic working on breadboard or vero/perf/strip-board before you shell out big money.

As you've mentioned GCSE, I take it you're in the UK. For small or one-off prototypes, there's a UK site that offers a nice service for just that ( http://www.badnetwork.co.uk/ )

As far as minimal arduino clones go, the 'boarduino' seems like a good starting point to me ( http://www.ladyada.net/make/boarduino/download.html ). Easy to build with self-sourced parts.

As far as using PCB layout software, this can be a bit tedious at first, probably even quite annoying. Personally I use KiCAD ( http://store.curiousinventor.com/guides/kicad ), which is open source and does not have any constraints like the 'free' version of EAGLE.

Also I'd like to invite you to join the arduino forum on www.arduino.cc - quite a lot of UK folk hang out there as well.
angler says: Oct 18, 2011. 12:15 AM
How is the total shift register (per chip) current kept below 70mA? Is this why you chose 270ohm?
angler says: Oct 18, 2011. 12:33 AM
Oops, just read the 1:8 duty cycle comment below.
acotton1 says: Apr 26, 2011. 8:16 AM

I have one week to make an RGB LED matrix for school. How did you wire your matrix up before you transferred it all to PCB? Due to time and money constraints, I can't have a PCB manufactured. Do you have photos, schematics, or diagrams?
madworm (author) says: Apr 26, 2011. 9:01 AM
One full week... snigger. SCNR.

Before it was transfered to a PCB, I uses a breadboard to test the circuit. Suffice to say that it was no fun at all. So many wires...

If you go to my blog (the link is here somewhere), you will find schematics and photos (flickr) and some code as well. The best entry point is the projects page. Other posts may have outdated content. If you intend to use any of it, make sure to get the latest versions of both from the git repositories, otherwise it may have unpredictable effects.

wmtt says: Mar 30, 2011. 11:00 PM

Good news, I have success in having a working RGB matrix after following your codes and instruction.

May I know how do i modify the code if I want to run it on a stand alone ATMEGA168/328 but NOT from a Arduino?

Thank you again!
madworm (author) says: Mar 31, 2011. 9:23 AM
Well, once you have compiled the code (for an 168 or 328) into the .hex-files, you can just take your favourite ISP programmer and flash the chips. No need to change the source code. On linux systems the .hex files are temporerily created in '/tmp/build.xxxx', on windows I frankly don't know.

Just make sure the FUSE settings of the chips are correct. For an 168 these would be:

a) no bootloader, 16MHz quartz, 16kb usable:

lock: 0x3F
lfuse: 0xFF
hfuse: 0xDD
efuse: 0x01

b) with bootloader, 16MHz quartz, 14kb usable:

lfuse: 0xFF
hfuse: 0xDD
efuse: 0x00
wmtt says: Feb 27, 2011. 10:20 PM
Dear Madworm, you are great! I saw your works elsewhere in the net and I think you are the one to answer my question.

I want to build a 5x5x5 LED matrix using RGB LED, I don't think I will have problem controlling each layer but I have 5 layers to control. I want to

1. mix colour (not just the 7 colours) of the RGB and
2. contol the brightness of each and single LED

so I think PWM (software by 74HC595 or by hardware TLC5940) is my answer.

Do you recommend

1. use the 74HC595 and mutliplex them for each layer (5 layers) or
2. Use TLC5940 and multiplex them for each layers?

I worry that by multiplexing the chips, I do not get enough refresh rate and give rise to LED flickering......

Looking forward to your kind assistance. Any information would be much appreciated!!!
madworm (author) says: Feb 28, 2011. 12:48 AM
I'd use TLC5947 if possible. Very similar to 5940, but simpler to use. Needs no external grayscale clock, which keeps the microcontroller busy all the time. Just send the data and forget about it. With these you'll need a few extra transistors to drive the layers. Maybe something like UDN2981A if you can get that one.

The multiplexing makes it dimmer, but with professional driver chips you can compensate to some degree by adjusting the external current reference resistor. Just make sure that you never stop multiplexing...

But for color mixing anything is better than 595 chips. It may be doable, but at some point you'll wish you hadn't gone that way ;-) A whole lot of time is wasted just for generating the pwm signals.
wmtt says: Mar 6, 2011. 8:42 AM

Thanks for your kind reply. I have already received the 5947 samples from Ti and will give it a try. But exactly do i multiplex it, can you give me some direction or exact way / scehmatic that I can follow.....

One more question, is your 64-pixel RGB matrix true color and not just the 7-color....?

Thank you once again.

WM Tang
madworm (author) says: Mar 6, 2011. 10:43 AM
For starters, you can find an example for a single line of 8 RGB LEDs driven with a 5947 there:

http://docs.macetech.com/doku.php/octobrite

For multiplexing, you need to have a look at the BLANK input. It should be used when a new row is addressed.

A project based on the 5940 is this one:

http://www.thebox.myzen.co.uk/Hardware/Mini_Monome.html

It should convey the principle of what you need to do.

What the 5947 needs is similar. You need to compensate for the 24 vs 16 channels and throw everything out that deals with GSCLK, as this is handled internally.

My doodad can do more than 7 colors ;-) Technically it supports 32768 shades, but not all of them look different to the eye.
dunnos says: Jan 2, 2011. 11:03 AM
When I read the last step I laughed, so hard. Seriously man, piggyback! awesome
wilhelmmaybach says: Dec 2, 2010. 1:52 PM
Hey Madworm, great Instructable, just one question: I understood most of the project, but I dont get the PWM part. I understand you use th 74 595 for selecting or controlling each led and each lead of the LEDs, (1 for red, 1 for green, 1 for blue, 1 for the anode or cathode) but, I dont get how you "connect" the PWM to each RGB, I understood very well what PWM is, but, for example, you have in your PWM a frecuency of 1 Hz (only example), and a PWM resolution of 8 bits, and duty cycle of 25%, ok, you want to connect the output of this PWM to any lead of any led, how do you achieve that?? how do you refresh or shift data into the shift registers to achieve that?? thanks for your response, and please be a little detailed, thanks a lot...:)
madworm (author) says: Dec 3, 2010. 1:28 AM
There is no single PWM source that needs "connecting" to the LEDs. The PWM is created by sending a bit-stream to the shift registers at high speed.

If it helps you understand think of it as many virtual PWM sources that are sequentially sampled, which created the bit-stream. All of this is done in software. The result of this is then sent to the shift registers, which just reproduce it. Each SR pin is a PWM source that way.
VJHAL says: Nov 17, 2010. 11:30 PM
I think I know how to get it to work with 8-bit pixel depth ... or maybe even 16-bit if you can handle a frame buffer that large. Can I get access to the driver code to try out my idea?

Also: Are the support boards for sale or can I get some made the same place you did?
madworm (author) says: Nov 18, 2010. 6:23 AM
You can grab the code and _all_ design files (KiCAD + schematic + gerber files) on my blog ;-) The link should be on my page somewhere here.

Using the gerber files, you can have the boards made for you anywhere. If you just need one board (no parts), this shouldn't be a problem as well ;-) I will find one in my many boxes.
VJHAL says: Nov 18, 2010. 8:33 AM
(Trying again, code got garbled the first time.)

OK I have the code. I am new to Arduino so I am having trouble seeing where the duration of a light pulse is set, but let me describe the idea here in words and pseudocode.

Instead of a countdown loop with "<" comparisons, we look at each bit of the brightness from MSB to LSB. (I will here assume 5 bit depth but the generalization should be obvious.)

In pseudocode (borrowing from the actual code):
#define __brightness_bits 5
#define __brightness_levels (1<< __brightness_bits)
...
for (bit=(__brightness_bits - 1);bit>=0;bit--)
{
for (led = 0; led <= __max_led; led++) {
red &= ~(1< }
green &= ~(1< }
blue &= ~(1< }
}
// Now here comes the key idea:
// Drive the LEDs for time proportional to (1<< bits)
}

The number of different times the LEDs get driven here is proportional to __brightness_bits, not 2 to that power, so in this case 5 (not 32). It scales nicely, linearly instead of exponentially. The remaining problem is simply how to drive the LEDs for a variable amount of time. This might be easy for you, but I need to understand the Arduino interrupt system first, which will take me a while.

Does this make sense?
madworm (author) says: Nov 18, 2010. 9:31 AM
'Simply' is such a nice word ;-)

At first glance I feel you have done this:

a) remove computational needs from the 'decision making' process, better scaling law: O(n)
b) add complexity to the 'driving the LEDs' part

When driving just a single LED (or many in the same way) it would be easy to off-load this task to one of the timers in PWM mode.

For generating the required PWM bit-pattern with shift registers you'd have to run some sort of loop again I think, or use dedicated driver chips with integrated PWM counters (like TI5947, not exactly cheap).

Maybe this is a zero-sum situation, but I can't tell for sure.

Let's see your 'make the LEDs do what I need' code ;-)
VJHAL says: Nov 21, 2010. 1:11 PM
I think this should be pretty easy once I get up the learning curve. It is possible to set an Arduino up so that a timer drives PWM directly without burning CPU cycles; see for example the audio driver at: http://www.arduino.cc/playground/Code/PCMAudio
I think this is what you meant by "off-load this task to one of the timers in PWM mode". So the right SW architecture here is to use one timer in PWM mode just for display on time. Within each frame, you fire off the PWM timer in __brightness_bits groups * __rows rows/group with a different timer setting for each group, but you don't need to sit and loop inside the interrupts and waste a ton of CPU time. Just load the display bits and the timer and then exit: "Fire and forget." :-) You do of course have to know whether it's finished so you can fire off the next one, but that can either be done off the timer interrupt, or, less precisely, just as part of your main loop where you are polling buttons or whatever else you do in the unused CPU cycles. Assuming the minimum pulse width is one clock cycle, the total driving time is still at least __max_brightness clock cycles plus overhead. So we are constrained by
frame_rate * total_driving_time < CPU_clock_speed
so assuming 16 MHz CPU and 1000 cycles overhead per depth-bit per frame (125 cycles per row display) we get theoretical max frame rates of roughly:

4-bits => 3984 Hz (16000000 / (4*1000 + 16))
5-bits => 3179 Hz (16000000 / (5*1000 + 32))
6-bits => 2638 Hz (16000000 / (6*1000 + 64))
7-bits => 2244 Hz (16000000 / (7*1000 + 128))
8-bits => 1937 Hz (16000000 / (8*1000 + 256))
9-bits => 1682 Hz (16000000 / (9*1000 + 512))
10-bits => 1451 Hz (16000000 / (10*1000 + 1024))
11-bits => 1226 Hz (16000000 / (11*1000 + 2048))
12-bits => 994 Hz (16000000 / (12*1000 + 4096))
13-bits => 755 Hz (16000000 / (13*1000 + 8192))
14-bits => 526 Hz (16000000 / (14*1000 + 16384))
15-bits => 334 Hz (16000000 / (15*1000 + 32768))
16-bits => 196 Hz (16000000 / (16*1000 + 65536))
17-bits => 108 Hz (16000000 / (17*1000 + 131072))
18-bits => 57 Hz (16000000 / (18*1000 + 262144))
19-bits => 29 Hz (16000000 / (19*1000 + 524288))
20-bits => 14 Hz (16000000 / (20*1000 + 1048576))

Note that for under 14 bits, the overhead is most of the time, while for 15 and over, the displaying time dominates. But anyway it seems like 16-bit pixel depth should be achievable at completely flicker-free frame rates (if we have room for the double-size frame buffer). Even assuming the overhead doubles for 9-16 bits, we still get 16-bits => 164 Hz. 8 bits should be easy. One could either stick to a fixed frame rate (using the other timer) or just let this run as fast as it will go for maximum brightness (but with possible variations in brightness if the timing varies due to other loads). With this architecture the CPU overhead at 5-bit depth probably drops from 50% to around 1%. Give me a few days to receive the Arduino Uno I just ordered and a week to play around with it and I'll have code for you.
madworm (author) says: Nov 25, 2010. 7:10 AM
Hm, wouldn't there still be an awful lot of switching between the groups? Worst case: each LED has unique values for every color. I think getting a dedicated driver chip (e.g TLC5947 24-ch PWM driver) would be much simpler - if it were available right now. Out of stock everywhere. Only the switching between the rows would remain.
VJHAL says: Nov 19, 2010. 10:50 PM
I need to order an Arduino first I think. As far as you know, will your board work with the Uno?

I don't think it's zero-sum, I think it's a clear win, but the proof would of course be running code. It might be as simple as changing the how-long-to-drive-a-single-row constant to be a variable.

A free PCB would be lovely.
madworm (author) says: Nov 25, 2010. 6:16 AM
It's not a shield, but an Arduino-clone of its own accord. You could abuse your Uno to program it though.
Waren-Neutron says: Nov 19, 2010. 1:42 AM
that' an tv
Jawn says: Aug 16, 2010. 3:53 AM
I should work this into a game of life algorithm.
xense says: Jul 15, 2010. 9:21 PM
gg
8bit says: May 5, 2010. 12:30 PM
Is there any visible flicker?
madworm (author) says: May 5, 2010. 12:47 PM
Visible as "visible for a camera", possible. You can have a look at the videos. Visible to the human eye, no. I'd have a pretty big headache by now if it was flickering.

8bit says: May 5, 2010. 9:06 PM
Thanks. That's good news. I wasn't sure if that method of pwm would be fast enough for the arduino to make a good illusion. I was hoping the slight flicker on the video was due to framerate differences. How much wiggle room do you think is left for addons like polling extra buttons or adding extra functions?
madworm (author) says: May 6, 2010. 1:13 AM
About 50% of the CPU time is used for the PWM, the rest is free. Polling buttons or reading potentiometers works, even serial seems OK 99% of the time - as shown in this instructable.

If you require very precise (sub ms) timing though, you may have to expect a few glitches. For interaction with humans it is still plenty fast.
kevindk92 says: Oct 17, 2009. 3:56 PM
Hello,
what is the function of P1, P4 and P5?

(I love the project!)
madworm (author) says: Oct 18, 2009. 8:01 AM
P4: Used for directly programming the chip (once for the bootloader).

P5: Some pins are shared between the programming port (P4) and the connections to the shift register chips. The jumpers can be used to disconnect them while using P4. You could also use it for connecting other things, but you'd lose control over the matrix of course.
kevindk92 says: Oct 18, 2009. 9:33 AM
thank you!

I've another question:
your last shift register IC5 needs to source over 200mA/port?
in step 2 of this instructable you said:
Each port can source or sink about 25mA of current. The total current per chip sinked or sourced should not exceed 70mA.
why aren't you using a ULN2803 instead?

(sorry for my bad english (Belgium))
madworm (author) says: Oct 18, 2009. 9:58 AM
Maybe 200mA peak, but the 1:8 multiplexing brings that down to about 25mA on average. Each row is only on for about 1ms. It is not ideal, but works. It's only a geek toy and not a professional LED display ;-)

I don't use an additional sink driver, because of chip count. If I had to rebuild the device, I'd replace the sinking 595 chips with tpic6c595 or similar and add p-channel mosfets to source current. I have a prototype with a source driver (udn2981a), but that chip is too slow for PWM.
ReCreate says: Oct 17, 2009. 11:59 AM
I got another animation for you. :D
Its allot better than the previous, Its 30 Frames, Can you do it?
madworm (author) says: Oct 17, 2009. 12:15 PM
I'll give it a shot.
madworm (author) says: Oct 17, 2009. 1:58 PM
ReCreate says: Oct 17, 2009. 12:34 PM
:D Thanks, If you want, i could pack it into a Gif if you want.
madworm (author) says: Oct 18, 2009. 7:47 AM
That would be counterproductive. I need single frames. I could possibly write another script to turn the .gif into single frames, but I don't feel like that right now ;-)
 1-40 of 108 Next »