8×8 RGB Matrix — first prototype on perfboard

I’ve finally managed building a prototype on perfboard. So far I’ve just put on the sockets and a minimal arduino. That’s just the ATmega168, quartz + caps, reset switch, green led, input switch, decoupling caps. Tomorrow (after I wake up again, it’s already tomorrow) I’ll put in the 4 74HC595 shift registers and the UDN2981A source driver and wire up the led matrix :-)

For the time being some pictures and 2 short movies as proof that my “matrixuino” actually is alive. OK just one led and pushbutton, but the rest will be ready soon.


I’ll also post a schematic of the prototype, but it’ll be drawn by hand. Just quicker right now.

Update 1: 21.09.2008:

I managed to reverse one chip, luckily it just fried the 595 and no wires seem to have taken damage. The FTDI cable and my computer still works :-)

Update 2: 21.09.2008:

DONE with soldering :-) Documentation comes later, no time right now… me headache!


If you notice, the leds don’t turn off 100% in the vicinity of leds that are on 100%. This happens since I added the UDN2981A (current source, row driver) to get more current pumped into the leds. Using just 4x 74HC595 (shift registers) doesn’t exhibit this problem.

I tried to look at the SPI data with my old oscilloscope (TEKTRONIX 314), but its analog bandwidth is just 10MHz and SPI clock is 8MHz. Triggering on a single channel is hard, as the data always changes. When using external triggering it works kind of, but the fast signals only leave a dim trace on the screen. Suboptimal. It can run with 12V from battery, but it’s way toooo old. I NEED A NEW DSO !

Update 3: 25.09.2008:

Seems the high SPI clock causes the trouble with the UDN2981A chip, apparently it’s kinda slow. When not using it, the following pins in it’s socket should be bridged: 1,18 – 2,17 – 3,16 …. 8,11.

If you think about building this, please use the NEW schematic of the updated project!

Update 4: 27.09.2008:

If you happen to have an arduino-board and the chips, the code will work without modifications, just make sure you get the pin numbers right! The numbers in the code use the arduino_numbering, which is _NOT_ the same as pin numbers on the chip. In case you build it according to my schematic and want to use the button I’ve added, you’ll have to add something like this to the code:

Here’s an example that will run on the thing if you build it according to the schematic and uses the button and the led. What it should do is display a white led and move it across the matrix as soon as you push the button. Also the extra led (not the matrix) will light up when the button is pressed.

matrix_test.pde matrix_test.hex (ATMEGA 168)

This entry was posted in Arduino., Electronics. and tagged , , , , . Bookmark the permalink.

66 Responses to 8×8 RGB Matrix — first prototype on perfboard

  1. andy says:

    Hi, good blog, when you have designed a pcb layout can i steal it just the file that is? Great link for the rgb matrix too better than sparkfun :P

  2. robert says:

    Hehe, when I find the time to mess with eagle’s part library. Last time I just wasn’t in the mood for scanning every single database entry for a match with the parts I already have. I also doubt it will work as a single layer board, at least not without a lot of extra wires.

  3. andy says:

    ok no hurry havent got all the bits yet, ordered 25 shift registers for £6 in uk so thats ok i can have 5 displays :P nah il start with one i think.

  4. stigi says:

    anybody tried scaling this to 16×8?
    Came up with the similar layout, just using an ULN2803A for driving the row anodes. your code looks much nicer though. glad i found it, since now i don’t have to struggle with pwm. thanks!!

    will use it for building a 16×8 rgb monome clone ( see monome.org).
    you may check my blog for progress: http://seidbereit.de/topics/monome/

  5. robert says:

    Thnx for the kind words :-)

    I think 16×8 is possible, but (at least when using shift registers) I guess you’d have to cut down the PWM to fewer cycles (less colors to choose from). I think the peeps at sparkfun.com also use direct port manipulation to drive the rows and just use the shifters for the columns to save some cpu time. I also think they don’t do any PWM int the basic firmware at all (just 7 or 8 colors possible). The SPI speed is not the problem, but the time the interrupt-code takes to calculate the PWM cycles. Setting the SPI clock to 4MHz or 8MHz makes no difference with my code at all.

  6. andy says:


    think i have made a proto type pcb for this no UDN2981A yet, not lots of room, goin to make it and see if it works about 15 jumper wires nt to bad thou

    Il let you know how i get on

  7. robert says:


    Maybe you won’t need that chip anyway. At least I’m sort of annoyed by the blurring it creates.

    Sorry I didn’t come up with a layout, way too busy at work right now.

  8. andy says:

    got the matrix x2 today wow they are thin! but ledsee has gone??

    okay il get my perf board version running today i think.

    talk soon

  9. Helmut says:

    Anbei ein Bild einer 8×8 Matrix der etwas anderen Art.:-)


  10. Helmut says:

    aehhh, 8×18 :-)

  11. Pingback: Simple linux load monitor using the 8×8 RGB matrix - My 2µF

  12. andy says:

    hi got it working

    any chance you code help with a code to scroll text?


  13. robert says:

    I’ll look at it.
    For the time being, on evilmadscientist.com there’s something similar with scrolling text inside a pumpkin.

  14. dave says:

    on the matrix the pins go 1-16 on the bottom how do they go on the top?

    17- 32 or
    32- 17


  15. andy says:


    any luck with scroling text yet im usless at coding

  16. Pingback: Creating schematics and PCB layouts with open source tools - My 2µF

  17. robert says:

    Nope, I’m busy with ‘earning money’ at the time. No chance yet to code the scrolling text.

  18. andy says:

    ok no worries ive had another idea,

    would you think it is possible to for example get a 8×8 rgb matrix and put it in a line of 1 pixel high and 64 long? awkward pcb though

  19. Chris says:

    Hi, nice work.

    How does the ‘scanning’ work in your project? In a normal matrix with no PWM you might load data into the 595s and then scan through the rows.

    In your example you’re achieving different PWM per pixel, so are you scanning rows and colums in order to vary the PWM as you move to the next pixel?

  20. robert says:

    That’s right, I do the very same thing just way faster. To achieve individual PWM per pixel, each full refresh of the matrix (scanning the rows) draws 1/N of the full PWM cycle for all pins. When loading the shift register the algorithm determines if a LED should be on or off depending on the desired brightness and the current refresh cycle of the matrix. After N refreshes of the matrix the PWM cycle is complete.

  21. andy says:

    any luck with scrolling code?


  22. andy says:

    Hi, any luck with scrolling code ?

  23. andy says:

    hi could you email the schematic in its original eagal form??

    thanks andy

  24. Aaron says:

    Nice work!
    How many matrices could you connect to a single arduino? I am tempted to go your route, but i need 6 matrices in a single system. I was planning on buying the sparkfun version, but yours looks like a better solution.

    also, what’s the average current draw without the drivers?

  25. robert says:

    If you want to do PWM for extended color mixing or per LED brightness control, it’s 1 matrix per arduino. there’s not enough ram to store more than one matrix. it’s also a speed issue to get the data shifted out fast enough to get it flicker free. if you reduce the colors to what the sparkfun module provides by just statically mixing the LEDs, you can maybe run 2 matrices if you devise a more efficient way of storing the state in RAM. one pixel will just need 3bits, but this doesn’t fit into bytes easily without wasting memory. I haven’t measured the current yet, but it’s less than 500mA. It doesn’t blow the arduino’s fuse.

  26. Frollard says:

    A thought:
    “more efficient use of RAM…3 bits…doesnt fit”

    instead of putting the 3 bits next to each other in memory, use 3 bytes to hold 8 led’s values…

    I love the project, and I’m definitely gonna build one; been needing a new project for my arduino!

  27. robert says:

    Ah yes, this is possible of course. The only problem with it is that some (R,G,B) triplets are split over 2 bytes. This is possible, but complicates the logic of storing/reading the data a bit.

  28. andy says:


    can you email the pcb as have had no luck with doing mine :(

    thanks :)

  29. robert says:

    I’ve added a new post with some KICAD files. Use at your own risk, unchecked, was never built.

  30. Francis says:

    Hey… can you clarify for what part of the code I would have to change to use a common-cathod matrix? I it up and runing but obviously some things are working backwards.

  31. robert says:


    Hmmm, let’s see…

    First you should make sure that this is in the right order:


    If your matrix has common cathode rows and common anode columns (the exact inverse of mine) then you have to change this in the above piece of code:

    spi_transfer( ~(B00000001<<row) );

    This just sends the inverse of the previous byte as now 1=off, 0=on.

    Then replace:

    byte row = B00000000; // row: current source. on when (1)


    byte row = B11111111; // row: current sink. on when (0)


    red = B11111111; // off
    green = B11111111; // off
    blue = B11111111; // off


    red = B00000000; // off
    green = B00000000; // off
    blue = B00000000; // off

    and finally:

    red &= ~(1<<led);
    green &= ~(1<<led);
    blue &= ~(1<<led);


    red |= (1<<led);
    green |= (1<<led);
    blue |= (1<<led);

    This should be all.

  32. Francis says:

    Thanks! It works great. One odd thing though… the bottom row of LEDS is always about twice as bright as the others. I double checked resistor values and swapped the row selector chip but the problem persists. Did you ever run into that problem?

    • robert says:

      No, that sounds odd.

      As it persists after swapping the chips, it sounds more like a code issue. But I haven’t seen anything like that with any of my boards. Have you measured the LED’s forward voltage of the bottom row ? I think it should be unlikely, but maybe they are different. BTW, did you use 74HC595 or 74HCT595 ? The HC ones are much better for this job.

  33. Francis says:

    I’m closing in on the problem… it seems that in only occurs with functions that use hue, not the rgb functions.

  34. Francis says:

    maybe there is something in the set_led_hue function that needs to be adjusted for a common cathod matrix?

  35. robert says:


    I sent you an email.

  36. H..sawkee says:

    kindly forward me the full circuit diagram

  37. robert says:

    Why not download it ?

    I posted the files on my blog.

  38. jan says:

    hi Robert,
    i have the same problem as Francis, but i’m using a common anode matrix. the demo runs pretty much fine, except for what seems to be an offset error. for example the smiley, it has a bar of LEDs in the top row. also the colors are of in some demos, and the top row is always 2x brighter than then rest of the row, the bottom row is about 1.5x times brighter than the others.
    I’m using a BL-M23A881RGB Matrix with 4 74HC595Ns – haven’t integrated the UDN2981A yet.
    it would be great if you have any tips what could be wrong…


  39. robert says:

    First, forget about the UDN2981A, this chip is way too slow. It will create ghost images on the matrix. Seeedstudio use the Mitsubishi M54564P/FP which is faster, but I haven’t found a place to get it from yet.

    OK. Good that you’ve posted the part number of your matrix. I’ve compared its pinout with the one I use (see the Tech / Datasheets page for a PDF file). Long story short, the pinout is different!

    The code expects all pins for red, green and blue to be sorted consecutively and arranged in a certain way. Have a look at “Assembly Overview” on my Projects / Shop page.

    So you have two options:

    – rewire the LED matrix
    – change the interrupt code

    I’d do the first. Just make sure all red,green and blue cathodes are sorted from 1…8 and end up on the right shift registers. The same applies to the anodes.

  40. jan says:

    Thanks for the tips, i’ll leave the UDN2981A out. the good news: i suck at soldering. i got rid off the missing lines and the top row has the right brightness now.
    the bad news: the bottom row is still brighter than the others. could this be another bad solder joint, and it’s getting twice the voltage? i’ve checked everything, they look good now … and there seems to be blue missing every now and then, e.g. in the random part and the color wave, but it works in the smiley?! here’s a video what it looks like right now: http://qik.com/video/1665950

    i’ve figured out the pinout by manually lighting up every LED, so i think it’s fine the way i have it – or could it be the reason for the 2 problems?


  41. robert says:

    That looks really strange.

    Wrong pin order could cause wrong colors, but the increased brightness in just one row… strange.

    Have you tested what happens if you set individual leds to the base colors ?
    I’ve uploaded a modified test program on the Project page. Disable demo() and enable the function demo_2() in the loop() function. This should test the individual leds:

    Led(0,0): red, then green, then blue, then white
    Led(0,1): …

    Oh, and don’t forget to adapt this to your order of shift registers (in case you use my old schematics):


    All new code is made for the updated schematics used for the boards I ordered at a FAB house.

  42. jan says:

    Alright, today in daylight the situation looks different: it wasn’t one row being the bright, it was one row being just right and the others being too dim.
    I’ve tested your other code, this is what it looks like (slowed it down a bit): http://qik.com/video/1668085 – Red, Green, Blue, Dim Orange. White doesn’t seem to work?!
    When i change the spi_transfer order in your matrix_code example, all rows look as bright as the last row, but what’s going on on the matrix is completely different from what it should be…
    btw, i’ve wired the setup on a breadboard according to this: http://www.arduino.cc/en/Tutorial/ShiftOut
    The board looks like this now: http://is.gd/zNT3
    Oh, and i thought it was the right thing to do, so i didn’t put 24 resistors in front of all the column cathodes, but 8 in front of the row anodes – that’s not the problem, right? ;)

  43. robert says:

    AHA, that explains a lot !

    Now I understand why the last row was always too bright. If you wire it like the example in the playground, you wire the “OE” (output enable) line to GND, but my code needs that line to be wired to Arduino’s pin 9!

    What happens is this:

    While the display is redrawn (inside the ISR() function) everything is OK, but when this function is done (so the Arduino has time to do something else in the loop() function) my code _disables_ the display. Why ? Outside the ISR(), the pulse width modulation to mix the colors doesn’t happen anymore, so all LEDs that were on before STAY ON (static)–> brightness is way too high.

    Now to the resistors.

    If you just use 8 resistors for the anodes, the LEDs must share them. If you have a look at the last video, you can see that the first LED in a row seems to work, all colors get turned on. But as soon as more LEDs should turn on, only _red_ works.

    The reason for this is:

    The forward voltage for the red LED is much lower than for green and blue. By using Ohm’s law and 3 LEDs being in parallel you know that all get the same voltage. If green and blue maybe need about 3V to turn on, but red works with 1.8V it sucks away all the current, so the green and blue LEDs are “starved”.

    You _must_ use 24 resistors, so all the LEDs can work independently. To get a good reproduction for white light, I’d start with 3 potentiometers for just 1 LED and determine the resistors so it looks good. 24x 270Ohm worked for my Matrix, you may need different ones.

  44. jan says:

    dang, Thanks!
    if it wasn’t for the soldering, i’d buy a board from you =)

  45. robert says:


    Afraid of SMD ?

  46. jan says:

    Taadaaa: http://qik.com/video/1670657
    Ripped everything off the PCB and started soldering some connectors, not a nice, but smaller footprint with all the resistors in between…

    Me, afraid of SMD? HELL YES! =)

    I just got started with soldering, and most of my joints still look like crap, but i’d really love to get it all on one PCB… maybe one day!

  47. robert says:

    PCB no problem !

    I used KICAD (open source, runs on windoze too) to make the board. Just download KICAD ( http://kicad.sourceforge.net ) and the source files from my blog and modify the pin-mapping, reroute the board and send the files to http://www.seeedstudio.com/wiki/ and wait a bit.

    There you can get 5 PCBs for >> 30$ << if you publish the source files at their wiki. ( Even after adding 19% MwSt. this is really cheap ). The service you need is called "propaganda". I'm just testing what you have to do to import the source files on windoze.

  48. jan says:

    Hmm, sounds like a plan, $30 is really inexpensive! In the mean time i’ve moved everything from the breadboard to “thin air”, soldered a couple of hundred joints, everything uses connectors and IC sockets now … maybe i should have just soldered the components together directly, but now it’s modular – and can be reused on a PCB =)

    Here’s what the mess looks like now: http://twitpic.com/58uel

    Thanks again for your support!

  49. Pingback: Led | Dirk's Blog

  50. Francis says:

    Thanks for the help Robert, I got this working as you know a while ago but never got around to linking back. Anyway, here’s the finished product:


    So much fun and would never have gotten it working without your help. It sits proudly on my desk in my office today (right now it’s running a binary clock display which confounds all).

    Rock on.

  51. Pingback: Prototype PCB Soldering Demo 1

  52. Chris says:

    Hi Robert…excellent information btw. I did have one quick question…I am doing something similar, but I’m worried about sending too much current through the 595s. Since you light an entire row at one time, if all of the red LEDs are on in any given row (about 11.8mA each according to your V3 schematic if your Vcc is +5V), won’t that use 94.4mA, which exceeds the 75ma limit for the 595? Are your chips getting hot or even dying? Or am I completely missing something obvious here? Thanks in advance! :)

  53. robert says:

    The chips are not getting hot. I doubt they’d get hot even if you tried pulling 100mA out of them, if it worked at all. Their internal resistance is too high.

    I’ve just now measured the peak current for the red LEDs with my scope and it is about 5.5mA (1.5V voltage drop on the 270Ω resistors). That stays way below the maximum. Still you already see slight variation in brightness depending on how many LEDs are on per row. Pulling about 50mA (peak, not effective) out of a single pin is about the maximum it tolerates. For more “bang” one really needs good and fast source drivers (kind of hard to get with low turn on/off times and small V(CE)-sat.) and proper sink drivers (much easier to find and get, e.g. MBI5168, STP08CP05 – both are current regulated – or TPIC6C595 – unregulated).

  54. Chris says:

    Ahh cool. Yeah, I had been looking at all of the chips you listed below (especially the MBI5168 that you were discussing on your main page), but here in the US, I can’t find suppliers for any of those in the DIP package except the TPIC6C595 (I don’t have surface mount soldering experience or equipment.) I ordered some UDN2981s to play with, but as you discovered, it looks like they are going to be a bit too slow to do good high speed switching. Perhaps I’ll grab some TPICs as well and play with them. I have it working just fine, but the brightness isn’t what I’d like. I also ordered some resistor networks to make the board cleaner (8 isolated resistors in a single 16-pin DIP.)

  55. robert says:

    Kingelectronics.com sell the MBI5168 in a DIP16 package! And no more soldering of millions of resistors ;-)

  56. Chris says:

    Awesome…I just ordered 15 of them! For some reason, I thought they were in the UK, but after reading your post more closely, I realize they are here in the US. Can’t wait to see how they work…thanks again! Great find on those Macroblock chips!

  57. robert says:

    Glad you’ve found that out ;-)

    I’d be happy to hear from you again, once you’ve had some time to deal with the chips. I’ll order mine soon, but would like to hear if there are any traps to avoid.

  58. Chris says:

    Just a heads up, I got an email from King Electronics after placing my order that they are only able to ship less than half of my order because they are now out of stock. You might want to “pre-order” yours to make sure they have stock.

  59. robert says:

    Good God! I was so hoping 2010 would be a better year for me, and now this! After just about 3½ hours into the new year. This is a bad omen. When I wake up again, I will run a full backup on my computer.

    BTW. Happy New Year.

  60. josh says:

    hello Robert! thanks for sharing all your great work – i’ve used your ideas to help me build my own Matrix. i built the actual LED matrix by hand, soldering all 64 RGB LEDS into a grid. it was a chore, but works pretty well and gave me a large 16″ x 16″ x 64 RGB ‘pixel’ display.

    your code runs great and i have tweaked it a little to get different effects. what i want to do now is feed it data over serial/usb to display graphics in realtime. i was hoping this could work since the matrix would simply act as a dumb, unbuffered display, so RAM would not be an issue. it simply receives a ‘frame’ (a list of rgb values for each LED/pixel), and sets all LEDs to those levels,hopefully using your PWM code to get full color mixing. does this sound possible?

    i’ve managed to send a a single BYTE over serial to set all 8 LEDs in a row to ‘off’ or ‘on’. and i could probably set this up as an array or list that would set each row. but again, this only sends 1 BIT to each LED: ON or OFF. is there a way that i could send a string or series of bytes to Arduino, that your code could interpret and set each color of each LED to an 8bit value (approximated using PWM)?

    the first thing that comes to mind is something like DMX512 – the serial control protocol for lighting systems. it sends 512 bytes, each corresponding to a ‘channel’ or the brightness of a single light. for an RGB light fixture, 3 Channels or 3 bytes would set the brightness of red, green, and blue lights. the beginning of the 512 byte message is marked with a ‘header’ and repeats itself over and over at 250kbaud until a change in the message is made. the message is updated then sent repeatedly. there is a timing component, but i dont think that would be crucial in my case.

    so my question is: does your code offer an elegant way of accepting data in this or a similar structure? can i pass a byte to get a corresponding brightness using the PWM part of your code? or maybe your code isn’t required? although from what i can tell your code is fast and efficient – and the use of SPI leaves some room for other operations to go on at the same time. maybe i would need this for receiving and storing the serial data? i know some code, but yours is more complex then im used to – so i can’t tell just by looking at it if there’s a way to do this.

    any ideas?

  61. robert says:

    I don’t know if there is enough CPU time and RAM available to receive a DMX frame of 512 bytes length and do all of the PWM. I’ve already written something like what you seem to be planning some time ago. It is not fully optimized, as it always sends pixel coordinates and color values, which takes 43 bytes per row. If you can live with always sending a full row when updating, this could go down to 26 bytes and speed things up. It also needs some more intelligence to allow automatic re-synchronization if data gets dropped. The post is called “Showing live video on my 64-pixel display”.

  62. josh says:

    thanks for the quick reply!

    yes i think 512 bytes might be too much too – i was just trying to use DMX as an example of the serial message i could send. if i just send 3 color bytes per LED and let the order in which i send dictate the WHICH LED (i.e. 1st byte sets LED 1 Red; 2nd byte sets LED 1 Green; 4th byte sets LED 2, Red; etc.) that would only take 24 bytes per row – or 192 bytes for a whole ‘frame’ – that’s my theory anyway. perhaps your code needs extra data?

    i saw your ‘live video’ post – that looks cool! though, i’ve got a setup that will take either custom 8 pixel animations or text or whatever and send it out to this stream of bytes.

    so my question is: if i have a list of these color bytes, are there hooks in your code i could feed it into?

    or to put it another way, does your code have a way to set the brightness (through PWM) of each color of a specific LED?

    sorry if i’m missing something obvious. as i said, i know a little basic code, but yours is quite advanced for me – but it seemed better to adapt yours than start over from scratch. maybe not though.. = )

    thanks for your time!

  63. Christian says:

    Hi, very nice blog!
    I’m currently building a 8×8 matrix display like yours. I’m wondering about your design: Do you really drive a rgb led completely without ULN2981 (which I also have to use, because my led module has common anodes)?
    What happens, if you turn on all led’s of a row? 8 columns, each with 3 leds (r,g,b) = 24 leds, 24 * 20mA = 480mA. The 74HC595-output can only drive 20mA – no peaks allowed (corresponding to datasheet). I tried a similar setup and roasted the first two 595er’s :(


    • robert says:

      The duty cycle is 1/8 for each row. The average current per row (all LEDs on) is just about 30mA. I’m probably abusing the 595s, but so far no fatalities.

      Of course this only holds true while the multiplexing runs. Should the code freeze, things would probably get nasty.

Comments are closed.