This afternoon I got my information radiator/team status board finished.
Here’s what the completed Arduino shield looks like:
On the front, we have four seven-segment LED digits, where the digits are 1” tall. I considered using a pre-built shield that displays digits but the ones I saw all used pretty tiny digits. I would have gone larger than this but I didn’t have the power to drive a bigger one from USB. The bigger ones also tend to use higher voltages which would have meant a separate power supply.
You will also notice the absence of any LED driver chip. The Arduino is perfectly capable of running the driver chip without any any separate hardware, and some of the Maxim chips are $10+ per item.
Across the top are the 4 transistors to drive the common cathode of each of the chips; they switch the cathode to ground to turn on the digits sequentially.
Finally, on the lower-left we have a header that is used to connect the LPD8806 LED strip to the board.
If you look closely you can see some small wires soldered on the surface of the board. They connect to the headers that hook into the Arduino
Here’s the bottom view. Sorry it isn’t better, but I inexplicably had my camera set to super-low resolution.
The seven-segment displays have 10 pins each; the 7 segments plus the decimal point, and then two common terminals. The 8 ones connected to the segments are all connected in parallel with the blue wire, and then connected with the dropping resistor (68 ohms) to the output pin of the microcontroller. This is the top part of the board.
The bottom section has the connections for the transistors; the collectors hook directly to the common terminals on the displays, the emitters hook to ground, and then the bases hook to the microcontroller pins through a 1K base resistor.
Finally, the header for the LPD8806 strip grabs +5V and ground from the headers, and then the data and clock lines from two more microcontroller pins.
That’s 12 pins for the display, 2 pins for the LPD8806, 2 pins for serial communication giving 16 pins used. Plus one more pin for timer interrupt debugging (more on that below). I think that leaves me 3 pins free.
All the wire is 30-gauge wire-wrap wire insulated with Kynar. It’s very thin and easy to work with but still hefty enough to carry the current we need to carry, and it’s color-coded.
Wire-wrapping can be used without soldering on square pins, but most of the components here have round pins. Soldering wire that is that thin is fairly challenging, so I use my old had wire-wrapping tool that I bought from Radio Shack in the mid-1980s (and still available for about 6 bucks) to wrap the wire around the pin/wire, and then solder it. It gives a very secure connection though it is a bit tedious; 48 connections just on the blue wire to hook the display segments together.
The LED multiplexing code worked correctly pretty much right when I wrote it. But I wasn’t sure whether it was fast enough. The easy way to figure this out is to allocate one pin (pin 4 for me) to output. At at the start of the interrupt handler, set it high. And at the end of the handler, set it back low.
Now you can hook your oscilloscope up to that pin, and it will look something like this:
____ ____ | | | | ______| |_______________________| |____
The high portion is the time spent in the interrupt, and you can see that it’s only spending about 20% of the time in the interrupt.
Driving the LPD8806 strip
The LPD8806 strip came from Adafruit. It uses a protocol that is SPI-like, and they provide a nice library to use it. The library has two modes – one that uses hardware SPI, and one that drives the pins directly (“bit-bang”). If I had really been planning ahead, I could have used the hardware mode, but those pins were already in use. That left the bit-bang version. So, I coded it up, added a call to update it when we switched digits, and uploaded the sketch.
It worked, but there was a slight glitch in the LEDs and on the scope whenever it updated. The write was taking long enough that it was missing the next interrupt. I dropped the number of active LEDs down, and when I got down to 4, the glitch went away. On the scope I could see that I *just barely* had enough time to do four.
I don’t really have to write out the color data for all the leds at once, but that’s the only way the library works, so I’m going to be hand-rolling some code. Use the library code as a base, I ended up with the following:
void WriteByteToStrip(byte value)
{for (uint8_t bit=0x80; bit; bit >>= 1)
{
if (value & bit)
{
digitalWrite(DataPin, HIGH);
}
else
{
digitalWrite(DataPin, LOW);
}
digitalWrite(ClockPin, HIGH);
digitalWrite(ClockPin, LOW);
}
}
The if-then sets the data pin to high or low, and then toggles the clock pin. This uses the digitalWrite() support in the Arduino, which is very convenient but does a *lot* of work:
- It has to map the number of the pin to one of the ports on the Arduino
- It has to create a bit mask for the appropriate bit in that mask
- It either OR’s the bit mask with the port to set the bit, or ANDs the complement of the bit mask with the port to clear the bit.
Take a look at wiring_digital.c to see what work it does.
If you know what pin is going to be used, this can be done much more efficiently; you can figure out what port your are using and precompute the masks to set or clear the bit, and then just update the bit with a single operation.
The code to call WriteByteToStrip() is very simple as well:
void UpdateLedStrip(int currentItem)
{static int ledToUpdate = 0;
int dimAmount = (ledToUpdate == currentItem) ? 1 : 3;
DumboItem* pDumboItem = &dumboData.m_items[ledToUpdate];
WriteByteToStrip((pDumboItem->m_green >> dimAmount) | 0x80);WriteByteToStrip((pDumboItem->m_red >> dimAmount) | 0x80);
WriteByteToStrip((pDumboItem->m_blue >> dimAmount) | 0x80);
ledToUpdate++;
if (ledToUpdate == dumboData.m_itemCount){
WriteByteToStrip(0);
ledToUpdate = 0;
}
}
This writes one one led’s worth of data (3 bytes) each interrupt, so if there are 10 leds, they will get updated at 100Hz, which is more than quickly enough. A look at the scope shows that there is plenty of time left in the interrupt handler. I can also see the terminal byte being written; some of the updates take just a little bit longer.
Here’s an action picture. It’s set up with 8 values, and there is a separate LED for each of those values. It will increment through the values every second or so, making the current LED brighter and showing the associated value on the LED display.