Introduction: I2C Controlled 7 Segment LED Display

About: A software engineer in the windy west of the United States.
Seven segment displays are great for displaying numbers.  They can can be seen in the dark and don't consume too much power especially if the brightness is throttled back with a resistor.  The biggest drawback is that each digit takes 8 output pins to control, 16 for a two digit version as shown here.  By using an I2C input/output (I/O) expander all 16 individual LEDs can be controlled with only two output pins.  And those same two output pins can control over 100 devices.

I2C is a really great way to interface with innumerable peripherals.  The best Instructable on how to do this with an AVR microcontroller and where I got the basis for all my source code to control this LED module was I2C Bus for ATtiny and ATmega by doctek.  A really excellent resource.

The nice thing about making this a self contained module is that any microcontroller can interface with it that supports I2C and in my opinion it simplifies design when parts like this can be thought of as their own entity.

Step 1: Design

I put the seven segment LED display OVER the I/O expander.  This saves a ton of space and works out pretty nice but involves a bit of "tricky" business.  See the "tricky" section for details.  Because any pin of the seven segment displays can be connected to any pin of the I/O expander you can plan out the most convenient wiring scheme and take care of what pins make which number in software.  The way I wired the seven segment displays to the I/O expander is rather clever if I do say so myself and allow for a very small footprint and a minimal of wiring but might be a bit hard to explain.  I'll do my best and hope the pictures help.

I've recently discovered stripboard and have immediately fell in love with it.  I picked up from the Christmas card with inbuilt retro video game that enameled (a.k.a magnet) wire is great for use on stripboard.  I've always heard you should strip the wire with sandpaper.  User bradsprojects recommends stripping the wire with a glob of molten solder.  I've found that even this handy technique is almost unnecessary.  You can just go ahead and solder the wire without even stripping it first.  Solder will absolutely not stick to the varnish coating so as long as you've got a good solder connection you know the varnish is gone.  It might take a little more heat to get through the varnish than if you had stripped the wire properly.  But as long as the connection looks good it is good.  The larger the gauge of the wire the more heat it will take to burn through.  I used larger gauge for ground and power connections and smaller gauge for everything else.  All my enameled wire came from the Radio Shack three pack part number 278-1345.

You might notice that I soldered the I/O expander directly to the stripboard.  It seems everybody uses sockets for their ICs regardless of whether or not they intend to ever swap out the IC.  Other than the swap-out song and dance people give for using sockets, the other reason is a fear of heating up the IC too much and frying it.  Here's the thing, ICs are meant to be soldered.  They can take the heat.  You see ICs by the hundreds all the time on professionally made boards in all your favorite electronics.  These are all soldered on using a reflow method that heats up the IC way more than soldering one pin at a time.  So fear not, solder that IC right on to your board.  You'll save space and have a better connection.  And as long as you don't linger a ridiculously long time on a pin, the IC will be just fine.  Sorry about the rant, but I think it needs to be said.

I whipped up this project in Google Sketchup because it's quite useful to see how the parts fit together and Sketchup is so easy and fun to use.  I'm glad I did it because the image from Sketchup is better than any I took with my camera.  You'll notice that every datasheet for every part you ever see has exact dimensions on it.  That makes creating it in Sketchup a breeze.  If you'd like a tip for Sketchup, make everything in a x10 scale.  That is, if the part is .3 inches, make it 3 inches in Sketchup.  Sketchup has a little trouble with thin little wires and parts with small dimensions.  Doing everything in a x10 scale helps.  You can always print it out at a x10 scale so it's the correct size on a piece of paper, something else I like to do so I can get a real-world idea of size.  The Sketchup file is attached if your interested.

Step 2: Parts and Cost

All part numbers are Digikey.

All the following take the format:
description - part number - cost each - total cost

I/O Expander - MCP23017-E/SP-ND - 1.44 - 1.44
2x LED 7 segment display - 160-1576-5-ND - 0.88 - 2.64
2x female header 4pos - S7002-ND - 0.46 - 0.92
4x female header 2pos - S7035-ND - 0.28 - 1.12
.1uF ceramic capacitor - BC1148CT-ND - 0.066* - 0.066
2x resistor - 510QBK-ND - 0.064* - 0.128
stripboard - various - 0.25 - 0.25 - You're only using a small piece of a stripboard so it's hard to put a dollar value on it.  I got mine off of Ebay because I wanted a stripboard cutting tool too.  When I need more stripboard I'll probably get it from
enamel wire - Radio Shack 278-1345 - negligible
Total: $6.56

*The minimum order on these parts is more than 1 thus the odd price per unit.  When you meet the minimum order (5 or 10 units) the price works out to an even cent amount.

Step 3: Schematic and Datasheets

I made the schematic and board layout with ExpressBCB's excellent free software.  If the picture isn't clear enough you might have to download the software to see the schematic and PC board layout that's attached.  The program is blissfully small and very useful.  The image of the board layout has around 30 image notes.  I apologize for this but it's a bit complicated how the LED display fits over the I/O expander so all those notes seemed necessary.

EDIT: It seems that some of my notes on my original picture mysteriously disappeared.  The first notes I made were the ones that vanished.  My guess is that there's some limit on the number of notes you can put on each picture and when I made too many the first one were sent to the oblivion.  Or maybe it was just some glitch with the web interface or something.  Any way, the first picture has the regular stuff and the second picture relates to my "tricky" step.

Step 4: The "tricky" Bit.

OK.  Bear with me.  This is pretty unconventional but worked out great and if I do say so myself, was a tad clever.  What you need to do is solder enameled wire directly to one of the two ground pins for the LED display.  I used the largest gauge that comes in the three pack of Radio Shack enameled "magnet" wire, part number 278-1345.  After I soldered the wire directly to the ground pin I put some shrink wrap tubing around it to help strain relief.  That little pin on the LED display can't take too much wiggling.  I know because I broke two pins.  Thank the DIY gods that it was only one of two ground pins on each display.  I got lucky.  The enameled wire then passes through the stripboard without making electrical connection.  So be sure not to rub off the enamel where it passes through.  By passing it through the stripboard it helps in strain relief and make the module a little more tidy.  Then just solder the wire to an available ground connection.

Step 5: Code

I used this seven segment LED display as part of a digital thermometer.  It used an 8 pin AVR microcontroller.  The I2C code was adopted from the very excellent Instructable I2C Bus for ATtiny and ATmega.  He explains the I2C but protocol and implementing it with and AVR so well that there's no sense in me rehashing it here.

There's no getting around that mapping each pin of the I/O expander and LED display to an actual numerical digit is tedious work.  This is seen at the top of the file i2c_2dig_7seg.c in the attached code.  I attached the code to my digital thermometer that drives the I2C seven segment display for your reference.

The I/O device is Microchip Technology MCP23017-E/SP or Digikey part number MCP23017-E/SP-ND.

The basic I2C command for making all the pins outputs
{address,IODIR,0, 0}, or  {0x40,0,0,0}
or more specifically:
   messageBuf[0] = 0x40;
   messageBuf[1] = 0;
   messageBuf[2] = 0;
   messageBuf[3] = 0;
   USI_TWI_Start_Read_Write( messageBuf, 4)

The basic I2C command for writing the output pins is {address, GPIO reg,
port A val, port B val}.  For example if you wanted to write the values 0x45 and 0x67 to the A and B port pins it would be {0x40,0x12,0x45,0x67}
or more specifically:
   messageBuf[0] = 0x40;
   messageBuf[1] = 0x12;
   messageBuf[2] = 0x45;
   messageBuf[3] = 0x67;
   USI_TWI_Start_Read_Write( messageBuf, 4)

Note that the I2C address for this particular device is 0x20 but you have to left-shift it one bit because the least significant bit of the first byte is the read/write bit.  See I2C Bus for ATtiny and ATmega for details.

Step 6: Wire It Up

I breadboarded the design first of course.  There were a few bugs in my code but nothing too tough to figure out.  My biggest problem actually came when I wired it onto the breadboard.  The LED displays weren't working at all.  My excellent LCS-1M - A Full-Featured, Low-Cost Hobby Oscilloscope showed data was dutifully transferring across the I2C bus.  But the LEDs were all messed up.  I was quite perplexed, everything looked fine, it breadboarded fine.  It turns out I had the LED modules in upside down.  Yeah, that's right, upside down.  The board underneath has no obvious up and down to it.  In fact I was a bit torn as to which way to make "up" when I was writing the software.  So when I soldered it together I just got them upside down.  It was easy enough to fix and worked like a charm.

After everything was working properly I experimented with various resistor sizes to throttle back the LEDs and save on power.  Each LED takes a nominal 25mA and it so happens that 25mA is the max current the I/O expander I used can supply.  So at first I figured I would just wire the display directly to the I/O expander and call it a day.  It turns out that the LEDs were ridiculously bright.  And with 16 LEDs in total that's 400 mA.  I tried various resistors, one per display, two in total, NOT one per LED.  I decided that a 500 ohm resistor between the ground pin of each display and the ground of the board gave a good amount of brightness and still conserved on power.  Purists will tell you that each individual LED should have it's own resistor so the display is just as bright when 8 LEDs are displayed as when only 2 are displayed.  In practice there's no discernible difference between 8 or 2 LEDs being displayed even with only one resistor per display.  According to my multimeter 3.23 volts were across each 510 ohm resistor which is 6.3mA each or 12.6mA total compared to 400mA total if all LEDs were driven at 25mA.  That's a 97% reduction in power and they are still plenty bright.

Step 7: Conclusion

All in all I'm quite pleased with this display.  I took this last picture along with a US quarter so you can get an idea of scale.  I love how small it is and how easy it is to interface with a microcontroller.  Now any project can easily have a 2 digit seven segment LED display!

If you have any questions please leave a comment.