GoalsThis instructable presents the concepts involved with shift registers and high side drivers. By illustrating these concepts with an 8x8 LED matrix I hope to provide you with the tools needed to adapt and expand to the size and layout your project calls for.
Experience and Skills
I would rate this project to be of medium difficulty:
- If you already have experience programming microcontrollers and working with LEDs this project should be fairly easy for you to complete and to scale to larger arrays of lights.
- If you are just starting out with microcontrollers and have flashed an LED or two you should be able to complete this project with some help from our friend google.
- If you have little or no experience with microcontrollers or programming this is probably beyond what you should be getting yourself into. Try out a few other beginner projects and come back when you've got some more experience writing programs for microcontrollers.
Disclaimer and Credit
First, I am not an electrical engineer. If you see something that is wrong, or not a best practice, please let me know and I'll make the correction.
Do this at your own risk! You should know what you're doing or you can cause damage to your computer, your microcontroller, and even yourself.
I have learned a lot from the internet, particularly from the forums at: http://www.avrfreaks.net
I am using a font set that came with the ks0108 universal C library. Check that out here:
Step 1: Parts
General PartsTo make an 8x8 grid of LEDs and control them you will need:
- 64 LEDs of your choice
- 8 Resistors for the LEDs
- 1 Shift register for the columns
- 1 Driver array for the rows
- 8 Resistors for switching the driver array
- 1 microcontroller
- 1 clock source for microcontroller
- 1 prototyping board
- 1 power supply
- Hook-up wire
Specific Parts Used HereFor this instructable I used the following:
- 64 green LEDs (Mouser part #604-WP7113GD)
- 8 220ohm 1/4 watt resistors for the LEDs (Mouser part #660-CFS1/4CT52R221J)
- 1 HEF4794 LED driver with shift register (Mouser part #771-HEF4794BPN)
- 1 mic2981 High-Voltage High-Current Source Driver Array (Digikey part #576-1158-ND)
- 8 3.3kohm 1/4 watt resistors for switching the driver array (Radio Shack part #271-1328)
- 1 Atmel ATmega8 microcontroller (Mouser part #556-ATMEGA8-16PU)
- 1 12MHz crystal for the microcontroller clock source (Mouser part #815-AB-12-B2)
- 1 2200-hole prototyping board (Radio Shack part #276-147)
- Converted ATX power supply: See This Instructable
- Solid core 22-awg hook-up wire (Radio Shack part #278-1221)
- Solderless breadboard (Radio Shack part #276-169 (no longer available, try: 276-002)
- AVR Dragon (Mouser part #556-ATAVRDRAGON)
- Dragon Rider 500 by Ecros Technologies: See This Instructable
Notes Regarding Parts
Row and Column Drivers: Probably the most difficult part of this project is picking the row and column drivers. First off, I do not think a standard 74HC595 shift register is a good idea here because they cannot handle the kind of current we want to send through the LEDs. This is why I chose the HEF4794 driver as it can easily sink the current present when all 8 leds are in one row are switched on.
The shift register is present on the low side (the ground pin of the leds). We will need a row driver that can source enough current to string multiple columns together. The mic2981 can supply up to 500mA. The only other part I have found that performs this task is the UDN2981 (digikey part #620-1120-ND) which is the same part by a different manufacturer. Please send me a message if you know of other high-side drivers that would work well in this application.
LED Matrix: This matrix is 8x8 because the row and column drivers each have 8 pins. A larger LED array may be built by stringing multiple matrices together and will be discussed in the "modular concepts" step. If you want a large array, order all of the needed parts at one time.
There are 8x8, 5x7 and 5x8 LED matrices available in one convenient package. These should be easy to substitute for a diy matrix. Ebay is a good source for these. Mouser has some 5x7 units available such as part #604-TA12-11GWA. I used cheap green LEDs because I'm just playing around and have fun. Spending more on high-brightness, high-efficiency LEDs can allow you to produce a much more spectacular looking display... this is good enough for me though!
Control Hardware: The matrix is controlled by an Atmel AVR microcontroller. You will need a programmer for this. Because I am prototyping I am using the Dragon Rider 500 for which I have written both assembly and usage instructables. This is an easy tool for prototyping and I highly recommend it.
Step 2: The matrix
The LEDS need to be aligned so they face the same direction at the same angle. I found the easiest option for me was to put the body of the LED flush to the board and hold it there with a small piece of plexiglass and a clamp. I put a few LEDs in place a couple of inches away from the row I was working on to make sure the plexiglass was parallel with the prototyping board.
Rows and Columns
We need to have a common connection for each row as well as each column. Because of our row and column driver choice we need to have the anode (positive lead of the LED) connected by row and the cathode (negative lead of the LED) connected by column.
For this prototype I am using solid core (single conductor) hook-up wire. This will be very easy to interface with a solderless breadboard. Feel free to use a different connector type to suit your project.
Building the Matrix1. Place the first column of LEDS in the prototyping board.
2. Double check that your polarity for each LED is correct, this will be very difficult to fix if you realize it later.
3. Solder both leads of the LED to the board. Check to make sure they are aligned correctly (not at weird angles) and clip off the cathode leads. Make sure you do not clip the anode lead, we will need that later so just leave it pointing up.
4. Remove the insulation from a piece of solid core wire. Solder this piece of wire to each cathode right at board level.
- I tacked this at each end then went back and added a bit of solder at each junction.
- This wire should run past your last LED to make for an easy interface when we add control wires.
6. To create a row bus, bend several of the anode leads at a 90 degree angle so they touch the other anode leads in the same row.
- There are detailed pictures of this below.
- Take care not to let these come in contact with the column buses, creating a short circuit.
- Leave the last anode sticking past the final LED. This will be used to connect the row driver control wires.
9. Attach control wires.
- I used red solid core wire for the rows and black for the columns.
- Connect one wire for each column and one for each row. This can easily be done at the end of each bus.
ImportantThis LED matrix does not have any current limiting resistors. If you test this without resistors you will probably burn out your LEDs and all this work will be for nothing.
Step 3: The control hardware
In order to save on pins I am using a shift register to control the columns. This way I can control an almost unlimited number of columns with just four microcontroller pins. It is possible to use only three if the Enable Output pin is tied directly to voltage. I have selected the HEF4794 LED driver with shift register. This is a better option than a standard 74HC595 as it can easily sink the current present when all 8 LEDs are on at one time.
On the high side (current source for the rows) I am using an mic2981. The schematic shows a UDN2981, I believe these two are interchangeable. This driver can source up to 500mA of current. Because we are only driving 1 row at a time this gives a lot of opportunity for expansion, up to 33 columns for this chip (more on that in the "modular concepts" step).
Building the Control Hardware
For this instructable I have just breadboarded this circuit. For a more permanent solution you will want to either etch your own circuit board or use prototyping board.
1. Row Driver
- Place the mic2981 (or UDN2981) in the breadboard
- Connect Pin 9 to Voltage (This is confusing in the schematic)
- Connect Pin 10 to Ground (This is confusing in the schematic)
- insert 3k3 resistors connecting to pins 1-8
- Connect from Port D of the ATmega8 (PD0-PD8) to the 8 resistors
- Connect the 8 row control wires of the LED matrix to pins 11-18 (note that I have connected the lowest row of LEDs to Pin 18 and the Highest row to Pin 11).
- Place the hef4794 in the breadboard
- Connect Pin 16 to voltage
- Connect Pin 8 to ground
- Connect 220 ohm resistors to Pins 4-7 and 11-14.
- Connect the 8 column control wires from the LED matrix to the 8 resistors you just connected.
- Connect Pin1 (Latch) to PC0 of the ATmega8
- Connect Pin2 (Data) to PC1 of the ATmega8
- Connect Pin3 (Clock) to PC2 of the ATmega8
- Connect Pin15 (Enable Output) to PC3 of the ATmega8
- Connect a 12MHz crystal and load capacitors as shown in the schematic
- Connect the programming header as shown in the schematic
- It is best to filter the voltage supplied to the ATmega8. Use a 0.1uf capacitor between Pin 7 & 8 of the ATmega8
- The reset pin should not be left floating as it can cause random resets. Use a resistor to connect it to voltage, anything about 1k should be good. I've used a 10k resistor in the schematic.
Step 4: Software
The TrickYes, like everything, there's a trick. The trick is that there are never more than 8 LEDs illuminated at one time.
For this to work well, a bit of crafty programming is needed. The concept I have chosen is to use a timer interrupt. Here's how the display interrupt works in plain english:
- Timer counts up to a certain point, when reached the interrupt service routine is run.
- This routine decides which row is the next one to be displayed.
- The information for the next row is looked up from a buffer and shifted into the column driver (this information is not "latched" so it is not yet displayed).
- The row driver is shut off, no LEDs are currently lit.
- The column driver is "latched" make in the information we shifted in two steps ago the current information to display.
- The row driver then provides current to the new row we are displaying.
- The interrupt service routine ends and program returns to normal flow until the next interrupt.
The full LED display is mapped out in an array. In between interrupts the array can be changed (be mindful of atomicity) and will appear on the display during the next interrupt.
The specifics of writing code for the AVR microcontroller and of how to write code to talk to the shift registers is beyond the scope of this instructable. I have included the source code (written in C and compiled with AVR-GCC) as well as the hex file to program directly. I have commented all of the code so you should be able to use this to clear up any questions about how to get data into the shift register and how the row refresh is working.
Please note that I am using a font file that came with the ks0108 universal C library. That library can be found here: http://en.radzio.dxp.pl/ks0108/
Shift Registers: How ToI've decided to add a bit about how to program with shift registers. I hope this clears things up for those who haven't worked with them before.
What they do
Shift Registers take a signal from one wire and output that information to many different pins. In this case, there is one data wire that takes in the data and 8 pins that are controlled depending on what data has been received. To make things better, there is an outpin for each shift register that can be connected to the input pin of another shift register. This is called cascading and makes the expansion potential an almost unlimited prospect.
The Control Pins
Shift registers have 4 control pins:
- Latch - This pin tells the shift register when it is time to switch to newly entered data
- Data - The 1's and 0's telling the shift register what pins to activate are received on this pin.
- Clock - This is a pulse sent from the microcontroller that tells the shift register to take a data reading and move to the next step in the communication process
- Enable Output - This is an on/off switch, High=On, Low=Off
Here's a crash course in the operation of the above control pins:
Step 1: Set Latch, Data, and Clock low
- Setting the Latch low tells the shift register we are about to write to it.
Step 3: Set Clock pin high, telling the Shift Register to read in the current Data pin value
- All other values currently in the Shift Register will move over by 1 place, making room for the current logic value of the Data pin.
- The clock pin must be set low before changing to the next Data value. Toggling this pin between high and low is what creates the "clock pulse" the shift register needs to know when to move to the next step in the process.
- This tells the shift register to take all of the data that has been shifted in and use it to activate the output pins. This means that you will not see data as it is shifting in; no change in the output pins will occur until the Latch is set high.
- There will be no pin output until the Enable Output is set to high, no matter what is happening with the other three control pins.
- This pin can always be left high if you wish
There are two pins you can use for cascading, Os and Os1. Os is for fast rising clocks and Os1 is for slow rising clocks. Hook this pin to the data pin of the next shift register and the overflow from this chip will be entered into the next.
End of update
Addressing the displayIn the example program I have created an array of 8 bytes called row_buffer. Each byte corresponds to one row of the 8x8 display, row 0 being the bottom and row 7 being the top. The least significant bit of each row is on the right, the most significant bit on the left. Changing the display is as easy as writing a new value to that data array, the interrupt service routine takes care of refreshing the display.
ProgrammingProgramming will not be discussed in detail here. I would warn you not to use a DAPA programming cable as I believe you will be unable to program the chip once it is running at 12MHz. All other standard programmers should work (STK500, MKII, Dragon, Parallel/Serial programmers, etc.).
Make sure to program the fuses to use the 12MHz crystal
In ActionOnce you program the chip the display should scroll a "Hello World!". Here is a video of the LED matrix in actions. The video quality is pretty low as I made this with my digital camera's video feature and not a proper video or webcam.
Step 5: Modular concepts
MathI am driving the LEDs at about 15mA (5V-1.8vDrop/220ohms=14.5mA). This means I can drive up to 33 columns with the mic2981 driver (500mA/15mA=33.3). Divided by 8 we can see that this allows us to string together 4 shift registers.
Also consider that you do not need to have all 32 columns stretch from left to right. You could instead create a 16x16 array that is wired the same way you would an 8x32 array. This would be addressed by shifting in 4 bytes.... the first two would shift all the way to the leds for the 9th row, the second two bytes would shift into the first row. Both rows would be sourced by one pin on the row driver.
Cascading Shift RegistersThe shift registers used are cascading shift register. This means that when you shift in data, the overflow appears on the Os pin. The becomes very useful as a set of shift registers can be connected to each other, Os pin to Data pin, adding 8 columns with each new chip.
All of the shift registers will connect to the same Latch, Clock, and Enable Output pins on the microcontroller. The "cascading" effect is created when the Os of the first shift register is connected to the Data pin of the second. The programming will need to be altered to reflect the increased number of columns. Both the buffer that stores the information and the function that shifts information in for each column need to be updated to reflect the actual number of columns.
A schematic of this is given below as an example.
Multiple Row DriversThe row driver (mic2981) can source enough current to drive 32 columns. What if you want more than 32 columns? It should be possible to use multiple row drivers without using more microcontroller pins.
We need the row drivers to source enough current to light the LEDs. If you are using more columns than it is possible to light at one time, addition row drivers can supply the needed current. The same input pins from the microcontroller are used so there is no need to alter the scanning of the rows. In other words, each driver controls the rows for an 8x32 block. Even though 64 columns may have the same PHYSICAL row placement, we divide the row buses in two, using one driver for the 8 rows of the first 32 columns, and a second driver for the 8 rows of the second 32 columns and so forth.
A schematic of this is given below as an example.
1. Do not use multiple row drivers with the same number of columns. Doing so would mean that each shift register pin would be driving more than one LED at a time.
2. You must have a set of 8 resistors (3k3) for each row driver, one set for multiple row drivers will not work as it will not provide the necessary current to switch the gates.
For ExampleI decided to expand on the matrix I built earlier. I have added 7 more rows for a total of 15 as that's all I can fit on this protoboard.
I also just found out about a contest that Instructables is doing called "Let it Glow". Here is a video of my take on that. Once again, the digital camera I used to take the video doesn't do it justice. This looks great to the human eye, especially where all the LEDs flash, but doesn't look nearly as good in the video. Enjoy:
Source code for this larger display is included below.
Step 6: Conclusion
I have left the Two Wire Interface (I2C) pins unused in this design. There are several interesting prospects that can use these two pins. Addition of an I2C EEPROM will allow for storage of much larger messages. There is also the prospect of designing programming to turn the mega8 into an I2C compatible display driver. This would open up the possibility of having a USB enable device to display data on your LED array by passing it over the I2C bus.
There are many pins left over that could be used for buttons or an IR receiver. This would allow for messages to be programmed in via a menu system.
For this instructable I only implemented a couple of display functions. One just writes characters to the display, the other scrolls characters onto the display. The important thing to remember is that what you see in the lights is represented in a data array. If you come up with cleaver ways to change the data array, the lights will change in the same way.
Some tantalizing opportunities include creating a graphing meter out of the columns. This could be used as a signal analyzer with a stereo. Scrolling can be implemented from the top down or bottom up, even left to right. Good luck, have fun!
Step 7: Follow Up
Circuit Board Features
- Shift registers are on separate boards that can be daisy chained together to increase the size of the display.
- Controller board has it's own power regulator so this can be run by any power source that provides 7v-30v (9v battery or 12v bench supply both work just fine for me).
- 6 pin ISP header included so the microcontroller can be reprogrammed without removing it from the board.
- 4-pin header available for future use of the I2C bus. This could be used for an eeprom to store more messages or even to make this a slave device controlled by another microcontroller (RSS ticker anyone?)
- 3 momentary push buttons are included in the design. I may tweak the firmware in the future to include the use of these buttons.