License: Attribution Non-commercial Share Alike (by-nc-sa) Intro: 64 pixel RGB LED Display - Another Arduino CloneThis 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.

Image Notes 1. FTDI USB/TTL serial adapter

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 I2 C data to other modules...

step 2: Talking to shift registers and LEDs

A shift register is a device that allows for loading data serially and a parallel output. The opposite operation is also possible with the appropriate chip. There's a good tutorial on shift registers on the arduino website. The LEDs are driven by 8-bit shift registers of the type 74HC595. Each port can source or sink about 25mA of current. The total current per chip sinked or sourced should not exceed 70mA. These chips are extremely cheap, so don't pay more than about 40cents per piece. As LEDs have an exponential current/voltage characteristic, there need to be current limiting resistors. Using Ohm's law: R = ( V - Vf ) / I R = limiting resistor, V = 5V, Vf = LED's forward voltage, I = desired current Red LEDs have a forward voltage of about 1.8V, blue and green range from 2.5V to 3.5V. Use a simple multimeter to determine that. For proper color reproduction one should take a few things into account: spectral sensitivity of the human eye (red/blue: bad, green: good), efficiency of the LED at a certain wavelength and current. In practice one simply takes 3 potentiometers and adjusts them until the LED shows proper white light. Of course the maximum LED current must not be exceeded. What's also important here is that the shift register driving the rows must supply current to 3x8 LEDs, so better not push the current up too high. I was successful with limiting resistors of 270Ohm for all LEDs, but that depends on the make of the LED matrix of course. The shift registers are interfaced with SPI serial. SPI = Serial Peripheral Interface ( Image (1) ). Opposed to the serial ports on PCs (asynchronous, no clock signal), SPI needs a clock line (SRCLK). Then there's a signal line telling the device when the data is valid (chip select / latch / RCLK). Finally there are two data lines, one is called MOSI (master out slave in), the other one is called MISO (master in slave out). SPI is used to interface integrated circuits, just like I2 C. This project needs MOSI, SRCLK and RCLK. Additionally the enable line (G) is used as well. An SPI cycle is started by pulling the RCLK line to LOW ( Image (2) ). The MCU sends its data on the MOSI line. The logical state of it is sampled by the shift register at the rising edge of the SRCLK line. The cycle is terminated by pulling the RCLK line back to HIGH. Now the data is available at the outputs.

step 3: SchematicImage (1) shows how the shift registers are wired. They are daisy-chained, so data can be shifted into this chain and also through it. Therefore adding more shift registers is easy. Image (2) shows the rest of the schematic with the MCU, connectors, quartz... The attached PDF file contains the whole works, best for printing.

File Downloadsv303_schematic.pdf ((765x539) 189 KB) [NOTE: When saving, if you see .tmp as the file ext, rename it to 'v303_schematic.pdf']

step 4: C++ Source code

In C/C++ usually one has to prototype functions before coding them. #include <stdio.h> int main(void); void do_something(void); int main(void) { do_something(); } void do_something(void) { /* comment */ } The Arduino IDE does not require this step, as functions prototypes are generated automatically. Therefore function prototypes won't show up in the code shown here. Image (1): setup() function Image (2): spi_transfer() function using hardware SPI of the ATmega168 chip (runs faster) Image (3): framebuffer code using a timer1 overflow interrupt. Pieces of code that have a slightly cryptic look for beginners e.g. while(!(SPSR & (1<<SPIF))) {} use the MCU's registers directly. This example in words: "while the the SPIF-bit in register SPSR is not set do nothing". I just want to emphasize that for standard projects it is really not necessary to deal with these things so closely related to hardware. Beginners should not be frightened by this.

File DownloadsV3_x_boards_test_v3.pde.zip (3 KB) [NOTE: When saving, if you see .tmp as the file ext, rename it to 'V3_x_boards_test_v3.pde.zip']

step 5: Finished Gadget

After having solved all problems and getting the code running, I just had to created a PCB layout and send it off to a fab house. It look so much cleaner :-) Image (1): fully populated controller board Image (2): front side of the bare PCB Image (2): back side There are connectors breaking out PORTC and PORTD of the ATmega168/328 chip and 5V/GND. These ports contain the serial RX,TX lines, the I2 C lines, digital I/O lines and 7 ADC lines. This is intended for stacking shields on the backside of the board. The spacing is suitable for using perfboard (0.1in). The bootloader can be flashed using the ICSP header (works with adafruit's USBtinyISP). As soon as that is done, just use a standard FTDI USB/TTL serial adapter or similar. I've also added an auto-reset-disable jumper. I've also cooked up a little Perl script (see my blog), that enables auto-reset with FTDI cables which usually doesn't work out of the box (RTS vs. DTR line). This works on Linux, maybe on MAC. Printed circuit boards and a few DIY KITs are available on my blog. SMD soldering required! See the PDF files for building instructions and sources for LED matrices.

step 6: Application: CPU load monitor for Linux using Perl

This is a very basic load monitor with a history plot. It's based on a Perl script that gathers the system's "load average" every 1s using iostat. Data is stored in an array which is shifted upon each update. New data is added at the top of the list, the oldest entry gets pushed out.

More detailed information and downloads ( code... ) are available on my blog.

File Downloadsload_monitor.zip (3 KB) [NOTE: When saving, if you see .tmp as the file ext, rename it to 'load_monitor.zip']

step 7: Application: talking to other modules using IC

This is just a proof of principle and by far not the simplest solution for this job. Using I2 C allows for directly addressing up to 127 "slave" boards. Here the board on the right side in the video is the "master" (which initiates all transfers), the left board is the slave (waiting for data). I2 C needs 2 signal lines and the usual power lines (+, -, SDA, SCL). As it is a bus, all devices are connected to it in parallel.

step 9: Displaying images / animations on the matrix - quick hack

So it's only got 8x8 pixel and a few colors available. First use something like Gimp to scale down you favorite image to exactly 8x8 pixels and save it as ".ppm" raw format (not ASCII). PPM is easy to read and process in a Perl script. Using ImageMagick and the command line tool "convert" won't work properly. Upload the new arduino code, then use the Perl script to upload to the controller.

The flicker is just a mismatch of LED refresh and my camera's frame rate. After updating the code a bit, it runs quite zippy. All images are transfered live over serial as you see them. Longer animations could be stored in an external EEPROM like it is done in various spoke-pov boards.

File Downloadsshow_ppm_v2.zip (4 KB) [NOTE: When saving, if you see .tmp as the file ext, rename it to 'show_ppm_v2.zip']

show_ppm_v3.zip (6 KB) [NOTE: When saving, if you see .tmp as the file ext, rename it to 'show_ppm_v3.zip']

step 10: Interactive control of stored animations

Why let the microcontroller have all the fun ? The Arduino cult is all about physical computing and interaction, so just add a potentiometer and take control ! Using one of the 8 analog to digital converter inputs makes that very simple.

File Downloadspot+anim.pde (10 KB) [NOTE: When saving, if you see .tmp as the file ext, rename it to 'pot+anim.pde']

step 11: Showing live video

Using a Perl script and a few modules makes it quite easy to show quasi live video on X11 systems. It was coded on linux and may work on MACs as well. It works like this: - get the mouse cursor position - capture a box of NxN pixel centered at the cursor - scale the image to 8x8 pixel - send it to the LED board - repeat

File Downloadsshow_ppm_v4.zip (8 KB) [NOTE: When saving, if you see .tmp as the file ext, rename it to 'show_ppm_v4.zip']

RGBike POV Open project by Hazard

kevindk92 says:Hello, what is the function of P1, P4 and P5? (I love the project!)

madworm says:P1: Connects to an USB/Serial adapter for code upload or control. P4: Used for directly programming the chip (once for the bootloader).

Oct 18, 2009. 8:01 AM REPLY

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.

Oct 18, 2009. 9:33 AM REPLY

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 says:

Oct 18, 2009. 9:58 AM REPLY 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:I got another animation for you. :D Its allot better than the previous, Its 30 Frames, Can you do it?

Oct 17, 2009. 11:59 AM REPLY

frames.zip5 KB

madworm says:I'll give it a shot.

Oct 17, 2009. 12:15 PM REPLY

madworm says:http://www.youtube.com/watch?v=_XnL-cuM5ag<br />

Oct 17, 2009. 1:58 PM REPLY

ReCreate says::D Thanks, If you want, i could pack it into a Gif if you want.

Oct 17, 2009. 12:34 PM REPLY

madworm says:

Oct 18, 2009. 7:47 AM REPLY 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 ;-)

ReCreate says:Oh ok...

Oct 18, 2009. 4:01 PM REPLY

sushi says:Awesome project!!! I have a question however:

Jul 30, 2009. 10:15 AM REPLY

Why did you connect the shift register to both the anode and cathode? can the shift register connect to both ground and vcc? could you elaborate on that please?

madworm says:Aha! The type of shift register I use for this project (74HC595) can in deed source and sink current (so called 3-state drivers).

Jul 30, 2009. 10:56 AM REPLY

To make the display work, only one line is active at any given time (like on old cathode ray TV sets). The s.r. driving the rows is connected to the anodes of the matrix and takes care of that. The remaining 3 s.r. activate the columns of the display for red, green and blue. The intersecting LEDs light up briefly. By persistence of vision the whole matrix seems to light up. Getting different colors is done by tweaking the on/off timing for RGB.

sushi says:so in this shift register, when you have a pin high it connects to vcc and when you have it low it connects to gnd?

Jul 30, 2009. 1:04 PM REPLY

Jul 30, 2009. 1:47 PM REPLY

The 3rd state (high resistance, all pins quasi not connected to anything) shows up if you disable the output drivers by setting the right pin to HIGH.

sushi says:You said the right pin to high...what the right pin? thanks for the prompt responses!

Jul 30, 2009. 7:09 PM REPLY

madworm says:

Jul 31, 2009. 6:41 AM REPLY For this chip it is pin 13. Depending on the data sheet it is called 'output enable' (OE) or sometimes (G). Set to 5V the outputs are disconnected, set to GND they reflect the states of the uploaded data. http://www.arduino.cc/en/Tutorial/ShiftOut

sushi says:Ahhh niceee Sweet! thanks for all your help! you were very quick about it Again, awesome instructable...deserves more than five stars

Jul 31, 2009. 12:52 PM REPLY

thermoelectric says:Great Instructable, 5 stars. How much did the RGB LED matrix cost? I probably missed the price...

Jun 24, 2009. 10:10 PM REPLY

Jun 25, 2009. 1:44 AM REPLY

Jun 25, 2009. 5:42 AM REPLY Oh, Also, can you use a PIC microcontroller to run this? I just have a couple of PIC16F88's lying around, and thought this might be a good use for them...

thermoelectric says:Nevermind, I don't have any SMD ones anyway.

Jun 25, 2009. 5:45 AM REPLY

thermoelectric says:Wow, that would sure get pricey if trying to make a big display out of them.

Jun 25, 2009. 5:39 AM REPLY

madworm says:

Jun 25, 2009. 10:33 AM REPLY That is all too true, now imagine how much such big displays may cost. Even if they only pay say 1/10th for a mass purchase. If I win in the lottery, then maybe I'll go and build a big display. A good excuse to play with FPGA chips for sure. Running this with a PIC should work, but you'd have to make a new PCB for it of course. But the principle of operation would stay the same, just with different code and so on.

thermoelectric says:Good luck with the lottery! I would really like to see a big display. Thanks for replying. I might have to wait for a while to build it though, I don't exactly have a job yet :-)

Jun 25, 2009. 2:00 PM REPLY

KLASH69 says:

Jun 1, 2009. 7:13 PM REPLY Very good Instructable. This got me started with 74HC595's and I have a firm grasp on them now because of this instructable. Thank you very much. After figuring out how easy it is to use bit shift registers, I attempted to run 8 RGB LED's (w/ PWM of course) while having a PIC Microcontroller (PIC16F877A) control my 3x 74HC595 8-Bit Shift Registers. I based it off your instructable and decided to use SPI as you did, simply because of its speed. I am running at 20 MHZ with an 8-Bit software PWM and cannot get it to be flicker free. I could easily lower the PWM to 4 or 5-Bit and that helps, but that takes away some color. I was wondering if I could see your source code and I'll try to convert it over to the compiler I am using (CCS C). Thanks for your time and great instructable!

Jun 2, 2009. 1:45 AM REPLY

the c++ source code is in step 4 ( V3_x_boards_test_v3.pde.zip ). It lacks the function prototypes ( that's an arduino thing), but other than that it's valid code. Regarding the bit resolution of PWM, I also can't get more than 5bit out of it. Maybe it could be done by an assembler guru, but I sure can't.

KLASH69 says:

Jun 2, 2009. 6:44 PM REPLY Sorry I must have overlooked your code because I did go through all the zip files haha. Well I have settled on 5-bit PWM, which I can't complain about because it is software PWM. Thanks for the help. :)

Rob K says:Nice display you have there. What program did you use to draw the schematic?

May 27, 2009. 8:13 PM REPLY

May 28, 2009. 2:56 AM REPLY

silverbyte says:great 8x8 rgb controller. Im wondering if I can use your controller in conjunction with monome www.monome.org (also checkout some youtube videos on it)

May 21, 2009. 4:58 PM REPLY

I built one with RGB LED's but haven't figured out how to use all RGB LED's on it. Im wondering if it would be possible to hackup your controller in conjunction with the monome I have to be able to control full RGB on my monome. Think its feasable? I would have to figure out how to send data to it, and the serial ports already being used by Arduino + Monome to PC

madworm says:--> I2C using the wire library --> NewSoftSerial9 library But as monome seems to be driven by an ATmega32, you'd have to port them.

May 22, 2009. 1:59 AM REPLY

madworm says:

May 20, 2009. 12:56 PM REPLY Here's another video. The data is now stored in the chip's memory, so it's a lot faster. I had to slow it down quite a bit, otherwise you'd only see a static image.

xtremd says:Couldn't you also use a TLC5940 for this led matrix? just daisy chain them?

May 20, 2009. 10:51 AM REPLY

madworm says:

May 20, 2009. 11:19 AM REPLY Sure, but that chip is expensive. Also I think compared to the 5947 it's inferior. Always having to supply the gray scale clock. It would support much better color resolution, but at the time I started this it seemed too complicated to deal with. And I'd still have to do some sort of multiplexing, unless you're talking about driving each LED with a dedicated channel ( = 12x 5940 ).

alex-sharetskiy says:so what did the module and all the ICs cost? (total cost)

May 18, 2009. 9:35 PM REPLY

madworm says:That's without the LED matrix!

May 19, 2009. 11:34 AM REPLY

The LED matrix costs about 13$, depending where you get a compatible version. There's info on that in on of the PDF files I've attached.

alex-sharetskiy says:that's $33+

May 19, 2009. 5:47 PM REPLY

would it be possible to get multiple displays connected, say you get 500 of them and connect them so that they form a giant screen?

madworm says:

May 19, 2009. 9:11 PM REPLY You can form bigger displays if you want, either using normal serial or I2C. I2C allows to directly address each module, but is limited to 127 devices. Using normal serial would force a daisy-chained setup (data is shifted through every module). If you're thinking about displaying video like on the hacked peggy2 modules from evilmadscientist.com, this may be possible but requires some serious speed optimization in the code. That's more than just a weekend project. It's up to You to make it work :-)

alex-sharetskiy says:okay..... how fast can one module change images?

May 19, 2009. 9:41 PM REPLY

madworm says:That depends on the code :-)

May 19, 2009. 10:43 PM REPLY

If you transmit data pixel by pixel it'll be slow like shown in the video of step 9. That's using a speed of 19200 and a lot of overhead. Start byte + stop byte + coordinates for each LED. That is transmitting 7 bytes per package for 3 bytes of payload. If you send the full image in one go this ratio will improve, but you always would have to send the whole image. Of course you could write more intelligent code that gets told by the PC what's coming at it and make it more efficient.

madworm says:

May 19, 2009. 11:31 AM REPLY I got parts for the controller for a batch size of 25. Otherwise the cost for the PCBs (per piece) would have killed me. That way cost per controller is about 20$. The PCB is 6.80$ per piece, the rest is for the chips, headers...

T3h_Muffinator says:Nice! How'd you find interfacing using I2C? In my experience it's a bit of a russian roulette, especially with fabbed boards. I'm working on a bunch of projects that I wanna get fabbed - Where'd you get your boards mfg'd?

May 19, 2009. 1:09 PM REPLY

madworm says:

May 19, 2009. 1:31 PM REPLY Hm. I haven't done enough of I2C to have encountered any problems so far. Also the arduino libraries only use 100kHz clock, maybe that's more robust with longer wires. The boards with white solder mask shown on some of the images were made by Seeedstudio.com (open source deal, 5 for 30$,). The boards with black solder mask were made by www.goldphoenixpcb.biz (took just one week until delivered by FedEx, 169$ for 25 boards). Gold Phoenix has cheaper deals, but I wanted to have black solder mask, silk screen on both sides and also no lead.