Wednesday, June 25, 2014

Controlling an RGB LED Matrix with Shift Registers

The goal of this project is to control an 8x8 matrix of RGB LEDs. Each dot in the matrix houses a red, green, and blue LED behind a semi-opaque lens. To produce colors we need to dim the LEDs different amounts. For instance to make sky blue we need a lot of blue, some green, and a little red. Unfortunately LEDs don't dim, they are either on or off, but we can produce a similar effect by blinking them very rapidly, controlling the amount of time they are on and off to create different intensities. To do this we need a fast way to control all 192 LEDs in the matrix. The matrix provides 32 pins on it for controlling the LEDs. Eight of the pins control which row is being controlled, and the other 24 control the RGB LEDs in each column (8 columns with 3 LEDs in each). Controlling 32 IOs is a daunting task for an embedded processor which typically have only a handful of GPIOs (General Purpose IOs). The PIC32 family has the needed IOs but using 32 leaves little else for other purposes. To get around this problem I used a serial-load parallel-out shift register. Read after the break to see how it all works.

The schematic for the RGB Matrix is shown below. To control it we want to turn on all the row selectors (pins 26, 25, 24, 23, 10, 9, 8, and 7) then turn off the first row selector (pin 26) and drive the column pins for the LEDs we want to turn on. We then repeat this for the remaining 7 rows. The row selector is the common cathode for all 24 LEDs in that row. By holding it low we sink the current flowing through the LEDs from the column pins. Essentially an LED will be on when its column pin is held high and its row selector held low.

RGB LED Matrix Schematic
Now that we know how we want to control the RGB matrix we need a way to control its pins from the embedded processor. As I said earlier, I chose to use a shift register. The shift register IC I used is an 8-bit register so I needed to connect 4 of these in a chain to create one large shift register. The specific IC is the SN74HC595 which seemed a good choice because it could be controlled at high speeds (about 10Mhz at 3.3V) and it can sink 70mA of current, which is plenty to turn on the LEDs without having them blind us. To load the shift register we drive the SER input with the values to load while pulsing the SRCLK input to latch in the value. Once all 32 values have been latched in we pulse the RCLK input to place the values into the output drivers which are used to drive the input of the RGB matrix. We chain the 8-bit shift registers together by connecting the output of one registers serial chain (QH*) to the SER input of the next register. Note that QH* is not the same as QH. QH outputs the value in the driver, which is only loaded after RCLK is pulsed. The remaining inputs to the serial registers are nSRCLR which is an active low clear of the shift registers latches, and nOE which is the active low output enable of the drivers. For our purposes nSRCLR should be held high, and nOE held low. Below is my schematic for this 32-bit shift register. Click the image to see a larger version.
The finished serial register was fairly easy to solder up and connect to the RGB matrix. Now we have a method for controlling the RGB matrix, and a shift register that lets us easily drive all 32 pins using only 5 pins on the embedded processor. Next we need code that implements the control scheme we discussed above. I broke the code into two parts, a set of functions to control the shift register, and a set of functions to drive the LED matrix using the shift register functions. The code for the shift register is fairly straightforward as it just follows the method outlined in the register's databook. The code to control the RGB matrix is more complex. The core of the code is found in the UpdateRGBMatrix() function, which handles actually driving the matrix. This function must be called often and at a regular interval. To do this I use a hardware timer and call the function from inside the timer's interrupt service routine. Each time the update function is called it drives one row of the matrix, using a counter to determine which row. The code decides whether or not to drive an LEDs column input using an 8-bit counter (color_counter). The code tests if the value in the counter is less than or equal to the color value we want to display in that pixel. The higher the color value, the longer the counter will be less than the value, and the longer the LED will be lit.
We now have a basic way to control the RGB LED matrix. Moving forward I hope to make some graphics routines for drawing lines and various shapes and I will be sure to share them. Please comment with any questions or suggestions for improvements.

If the LEDs seem to flicker you are not updating the RGB matrix at a fast enough rate. To solve this reduce the amount of time between the calls to UpdateRGBMatrix().

bit_twiddle.h -- Helper functions for bit manipulation in integers.

RBG LED Matrix Datasheet

Related Projects: (Great site with lots of details and optimizations)


  1. I've seen a demo of this LED matrix with 74HC595 shift registers that used a ton of resistors - how are you getting away without having a resistor for every line? Are you actually using 74LS595s as in the schematic? If so, where do you get them cheaply in Australia?

    1. Tim, that's a great question.
      I have seen some of the other designs on the net and they do use resistors to limit the current to the LEDs. In this design I am using the 75HC595N from NXP. You can see there spec out at:
      The shift register outputs are able to sink a small amount of current (20-30mA) which is plenty to light the LEDs at a good level for indoor use. That is my method for limiting the current to the LEDs. If you want the LEDs super-bright because you plan to use them outdoors during the day you would want to have all the row selector outputs drive the gate of bi-polar transistors with a resistor to GND. You would need 8 BJTs and 8 resistors. The red and green LEDs in each pixel are brighter than the blue so if you wanted to be fancy you could also use appropriately sized resistors on all the red and blue column selectors to match the brightness. I do that matching in software using a factor (RED_FACTOR, GREEN_FACTORY, and BLUE_FACTOR) which you can find in rgb_matrix.c
      What is the project you are working on?

  2. Howdy,

    I've written my own driver for tri color LED matrices, using interrupts and allowing the use of higher level libraries that display pixmaps or text (from ladyada).
    It should be more useful to people who need a practical driver to use in projects.

    Would you mind adding a link to it?

    1. One other improvement you'll notice in my code is the use of binary code modulation to emulate 4 bit PWM with only 4 interrupts instead of 16. Do you have a measurement of how many microseconds your routine takes to update the 3 colors for a single line of the matrix?

    2. I like your site! You have a lot of great information for anyone looking to drive these LED displays. Do you have any schematic views of the design? If you need a good, free, schematic entry tool try Eagle (

      I will try and get the number of clock cycles needed for the ISR. That ISR is called 256 times per row to generate the software PWM signal in my design. The clock is 80Mhz and from that we should get the duration.

      Thanks for posting your design!

  3. Hello, i'm fairly new, and studying electronics, so I want to make a project like this. I have a couple questions:
    1.- Where did you connect the shift registers in the arduino?
    2.- Can you choose where to send the signals from the arduino? (I want to make the arduino myself)
    3.- Do i use your library or Marc Merlin's?
    Any help would be greatly appreciated :D

    1. I actually used a PIC32 for this, but it would be fairly easy to implement in Arduino. I would suggest using a library to control the shift register and after that the rest of the code will just need a few edits to handle the name changes in the SR functions.