Every project needs some blinking LEDs. This one has 300!
This 10×10 LED Matrix has 100 serially connected WS2812B LEDs which are controlled by an LPC11U24 microcontroller. The µC receives commands and image data from the PC via USB. The frame measures 400x400x35mm and is constructed from lasercut wood panels and holds without screws or glue. The grid dividers have been painted white to maximize light reflection. Initially I planned to cover the front with frosted plexiglas, but when I glued paper onto the grid during construction, the realized that it may look even better with a nice paper as the diffuser.
The WS2812B leds came on a 2m long self-adhesive strip with 60 LEDs per meter. To mount them to the woodpanel, the strip was cut between each LED and attached to the wood. To bridge the gaps, 3 wired cable pieces were soldered to the LEDs in a serpentine layout.
Controlling the LEDs is done via a one-wire serial protocol which is simple but has rather strict timing requirements. A one-bit is high for 0.8µs, then low for 0.45µs while the zero-bit is high for 0.45µs and then low for 0.8µs. To set the color of x LEDs, x * 3bytes are sent from the µC to the first LED in GRB order. The first LED takes the first three bytes and sets its three constant current sources accordingly. All following data is passed on to the next LED after the signal has been reshaped. This repeats for all LEDs on the line. To start over, the data line is pulled low for at least 50µs, after which the first LED is again ready to accept data.
There are several ways to produce the required timing: If the µC is fast enough, one can just use delays of the appropriate length. If the µC is just fast enough, the delays have to be handcoded in assembler (see the Adafruit NeoPixel Library for reference). A third way uses the State Configurable Timer available in some LPC µCs (see this forum post on LPCware.com).
My solution (adapted from this code on GitHub) uses the built-in SPI hardware of the LPC to generate the correct timing by setting the wordsize to 15bits and then transmitting
- 111111111100000 for a one-bit
- 111110000000000 for a zero-bit
Image date is stored in a big array of 300 bytes and can be received by the µC via USB. Unfortunately, the maximum packet size of USB2.0 full-speed devices is 64 bytes. This means 5 packets are needed to transmit the necessary 300 bytes. I use the first byte in each packet to indicate which part of the image is contained in the following 60 bytes and then copy them to the appropriate position in the array. Additionally, a few commands to clear, refresh, fill or set individual pixels are implemented via USB control transfers (Commands are sent to Endpoint1 and image data is sent to Endpoint2.)
On the PC side I wrote a little program in Delphi which uses libUSB-win32 to handle the USB communication. Using the program, one can paint each pixel, display 10×10 bitmaps or transmit a downsampled stream from the desktop (size and position are configurable). All image data can be gamma-corrected before transmission to ensure better color representation.
Up until now, I concentrated on the software and used an LPCXpresso development board to test the code. Next I will design a small circuit board that can be placed inside the case of the display and make it a stand-alone unit.
Future plans include some procedural effects generated by the µC (like plasma or fire), SD-card interface for automatic playback of images/videos and a bluetooth interface to stream image data from a PC or android phone.
When the project has reached a point where the software is presentable, I will make it available here or in a follow-up post.