The goal was to have the project controlled by an Arduino. In order to be able to show nice things on the display, we need a programmable controller, and an Arduino is cheap, reliable, and has good programming tools available.
Of course the Arduino is not able to drive 144 LEDs directly. This means we need to supplement the Arduino with a controller board that can receive the on/off status for each LED, and then actually turn it on or off.
And thirdly, we’ll need to come up with a good way to mount all 144 LEDs as a matrix, and how to connect the LED board to the controller board.
The matrix PCB is made starting from a pre-etched and pre-drilled experiment PCB with parallel traces. I used a dremel to make all the necessary trace cuts, and put in jumper wires (lots of them) to make the “vertical” traces, since we’re essentially making a grid of traces with the LEDs at the intersections. Bringing all the traces to the edge of the PCB to connect them to flatcables took quite a bit of wire.
Each column in the matrix has 6 LEDs, and each row has 24 LEDs. The LEDs have their ground pins connected to the row, and their + pins connected to the column. Each column is protected by a 220 ohm resistor. The 220 is an approximate value; the LEDs are rated at 2V/20mA, and the power supply will be 5V. In each column, at most one LED will be on at the same time, so the maximum current should be limited to 20mA. 3V/0.02mA=150 ohm, but a slightly higher value will still be OK while making the batteries last a bit longer.
|LED array PCB: bare, and with all interconnections soldered in place|
There are quite a few wires to run from the LED matrix to the controller board: 6 rows + 24 columns = 30 wires. I used pre-wired 6 and 10 pin connectors with headers to make it easy to disconnect the LED matrix from the controller when testing and assembling everything: pre-wired 10 pin connector.
It is a well-known fact that our eyes are not able to see more than 15 or so changes per second, and we can use that to our advantage: we can cycle through the rows one by one, and turn on or off only the LEDs in a single row at a time. If we do this fast enough (more than 15 times or so per second), the rows will seem to be on simultaneously, because our brain will not register the very brief “off” times for each row. This means effectively that we will “only” need to drive 24 LEDs at the same time. This simplifies the problem, but even so, the Arduino cannot drive 24 LEDs directly, so we still have to solve the problem of driving more LEDs than the Arduino has output pins.
The solution lies in serial-to-parallel conversion. If we can output the state of each LED one after the other, and then have them run simultaneously, we can make do with way less output pins than there are LEDs to drive. In fact, we only need 3 pins: the LED state signal, a clock signal to indicate cycle through the 24 LEDs, and a status signal to say whether the Arduino is in the process of sending data.
The 74HC595 is an 8-bit serial-to-parallel shift register. In “input” mode, it will listen to a clock signal and a data signal. 8 ticks of the clock enable the chip to read 8 input bits one after the other. When all bits have been shifted in, the chip is put in “output” mode, where it outputs all the bits at once on 8 of its pins. This video shows how this works in practice. Note how the data is moved into the chip bit by bit, and when all data is fed into the chip, the output pins are enabled all at once.
The 74HC595 has a 9th output pin that is a “carry” pin. The chip will hold the 8 latest bits that were shifted into it; if we send more data than 8 bits, the value of the bit that was sent first is moved to the “carry” pin. The carry pin can be connected to the data input pin of another 74HC595. If we then connect the clock and latch pins of the second chip to the clock and carry pins of the first chip, we effectively create a 16-bit shift register. In fact, we can extend the series indefinitely to create much larger shift registers.
Combining the “row shifting” with the “column shifting”, we arrive at a solution where we can drive 144 LEDs (or more if we’d like) with 5 pins on the Arduino. Of course, we have to be sure that the Arduino can cycle through the rows quickly enough to create the optical illusion where all rows are enabled at the same time. Switching the pins on and off “by hand” (sending 1 bit at a time) would be relatively slow (and require managing the clock pin as well), but the Arduino supports a serial protocol called “SPI” (Serial Peripheral Interface) where we can send a byte (8 bits) at a time, instead of 1 bit. Load the byte in a specific register, and the Arduino will control both the output pin and the clock pin to make sure that the LED controller loads all the bits in the byte correctly.
This is the schematic for the controller circuit. It is composed of 2 separate parts: 3 chained 74HC595 shift registers to handle the 24 LEDs in each row, and a CD4017 decade counter to switch from one row to the next. The rows are driven indirectly through a transistor, in order to not draw too much current from the 4017, and also to invert the chip output: when an output goes high, the transistor starts conducting, and connects the corresponding row to GND.
The controller PCB layout was created in Eagle PCB, and printed on toner transfer paper using a laser printer. This was ironed onto the copper side of a PCB, and etched using ferric chloride.
|Controller PCB: underside||Controller PCB: component side|
|Controller PCB with Arduino connected to it|
You’ll note in the picture that there are a few jumper wires on the track side of the PCB. It turned out that in the original version of the PCB layout, which was used to etch the PCB, a few traces were missing. I discovered this when checking the PCB against the schematic, and noticed that for some of the chips, suspiciously many pins didn’t have any traces going to them. Luckily it was fairly easy to make the connections using jumpers, and after rechecking the traces, it was time to solder on the chip sockets, headers, etc.
Then, the next step was to test the board. I did this by powering the board without chips (except the voltage regulator), and checking if all the chips received + and GND on the right pins, and if the voltage was OK. Then I soldered the resistors and the transistors in place, and checked if they received + and GND properly. I then checked the connections to the matrix PCB by connecting the matrix PCB, and jumpering the chip output pins one by one to + on the 74HC595s (columns) and CD4017 (rows). This effectively simulates the controller, and when jumpering both a column and a row, the corresponding LED should light up on the matrix. I tested exhaustively, and discovered a few bad solderings that made for non-conducting connections.
Finally, it was time to do real testing.
|Everything assembled and connected|