Introduction: 64 Pixel RGB LED Display - Another Arduino Clone
More information about "Arduino": www.arduino.cc
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...
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 I2C. 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: Schematic
Image (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.
Attachments
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.
Attachments
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 I2C 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
More detailed information and downloads ( code... ) are available on my blog.
Attachments
Step 7: Application: Talking to Other Modules Using I²C
Using I2C 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). I2C 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 8: Application: "Game Cube" :-)
Just a freak thought.
This one also fits into to wooden enclosure shown on the intro page. It's got 5 buttons on its backside which might be used for playing a simple game.
THE END ?
Step 9: Displaying Images / Animations on the Matrix - Quick Hack
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.
Step 10: Interactive Control of Stored Animations
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.
Attachments
Step 11: Showing Live Video
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
Attachments
Step 12: More Light Almost for Free

Participated in the
Get the LED Out! Contest
140 Comments
3 years ago on Introduction
Good tutorial...but your schematic is such low res I can't read the labels. Can you post a high res one?
5 years ago
Dear Madworm,
How can i use your board to display alphabet letters?
6 years ago
Dear Madworm,
This is great tutorial, and this is what i look for for all my time before. but i have a question, would you please to add some detail about the scaning (how to give 7 value for N maximum). your project is used 8x8 RGB led instead of i want to try with 8X1 RGB led.
Thanks in advance
Reply 6 years ago
Dear,
I'm rather busy right now.
If you still want to go the way of using shift registers, I strongly suggest having a look at the Arduino "ShiftPWM" library! It takes away all of the hard work for you.
https://github.com/elcojacobs/ShiftPWM
Best,
Robert
Reply 5 years ago
Dear Robert,
i'm sorry for my late reply.
thanks a lot for your suggestion.
Regard,
Gusti
6 years ago
Its a great poject, Can u help me find which pins of the arduino pro mini and uno have to be used here to have proper spi communication, i couldnt find it easy to get around ur code, and didnt understand which all pins are to be used. Plsss kindly help me with ur 5 mins.
Reply 6 years ago
Latest code:
https://github.com/madworm/V3_x_boards_test?files=1
In the first few lines you'll find the pin numbers. "Digital" pins 9 - 13 on the UNO and compatible ones. Same numbers on the other board. They should be printed on the PCB.
6 years ago
Reply 6 years ago
Better look at "shiftPWM" or use adafruit neopixel / WS2812B.
8 years ago on Introduction
Hi Madworm, great tutorial you've got here. So I've been working on implementing your circuit, but I have only arduino mega, and apparently it doesn't work there. I presume, it's something in functions that are working with SPI and timer register, that must be different for Mega.
Would you be able to point out what exactly has to be changed? I relatively new to arduino, and coding myself, so yeah.
I've tried simulation with arduino uno, and it worked fine. Mega doesn't work in simulation either..
Reply 8 years ago on Introduction
I need to look at the ATmega1280 datasheet first. I assume you've got this board (http://arduino.cc/en/Main/arduinoBoardMega).
That will take some time. It is not radically different, but I need to find the right bits & pieces.
Reply 6 years ago
Excuse me, have you found the difference between both boards?
I'm trying to build this, but so far the functions seem unresponsive.
Any help would be appreciated.
Reply 6 years ago
I haven't looked at the differences closely. Most likely difference in timer interrupts (naming & which one is free to use) / register naming.
Reply 8 years ago on Introduction
First make sure you've connected the right pins. According to the datasheet [atmega1280] that chip only has ONE hardware SPI device. See where the pins MOSI, MISO, SCK and CS end up on the Arduino Mega.
Some error messages would be helpful, if there are any.
Reply 8 years ago on Introduction
Yeah, I've connected data pin to 51st of arduino mega, clock to 52nd latch to 53rd. (according to second table here - http://www.arduino.cc/en/Reference/SPI)
In proteus simulation I'm getting "Writing to UDR3 while transmission is not enabled"
As for real circuit, I was testing just with one (or four) led(s), not entire matrix, and if I remember correctly, LED(s) just didn't do anything, and shift registers maintained constant levels on their outputs.
Reply 8 years ago on Introduction
That is odd. UDR indicates involvement of an UART.
Try this: http://pastebin.com/PcjfytxE
That should "blink" all 4 shift registers on/off every second.
If that works, we know SPI is good.
6 years ago
It is possible to connect several modules that make for a longer text?
How would the schematic? Thank you
Reply 6 years ago
Possible yes, advisable no.
You'd be better off with one much more powerful microcontroller, than having to deal with syncing up many very busy one.
I'd look into WS2812B LEDs or similar, so driving the LEDs themselves is much simpler & doesn't occupy most of the cpu time.
7 years ago
Hey madworm,That's a great instructable right there. But could you please help me in scrolling text on the matrix? I think that's the only feature missing in the instructable..
8 years ago on Introduction
Hi Madworm, I'm currently playing with this setup and do wonder one thing, Was there a specific reason to put the resistors on the cathode side? As they are all the same value technically they can be on the anode side as well, reducing the amount to only 8 instead of 24. I know it isn't the cost but it does makes the board a bit cleaner. Or am I overseeing something here?