This is part 3 of a three-part series. You can find part 1 here, and part 2 here.
We’ve arrived at the conclusion of the HexOut series. Here we look at the bottommost layer, which handles input. If you’ve read the first two articles, you’ll know that the top layer is Display, and the middle layer is Output. The Input layer takes the (up to) 16-bit wide signal from any source you like, and encodes it into a pattern of bits that will illuminate the segments on the displays that represent that bit pattern in hexadecimal. That bit pattern is then sent serially up to the upper layers for display. So let’s see what makes the Input layer tick.

Here again is the final product. It takes an 8-bit or 16-bit wide binary input, and translates that into a hexadecimal value. The top Display layer is just four seven-segment displays connected to a header. The middle Output layer takes an encoded stream of bits from the Input layer and pushes them up to the displays on top.
The Input layer is the most interesting, I think. This is where the brains of the operation are, so to speak. Here’s the schematic (and Eagle file):
You may recall from the previous articles that I went to quite a few lengths to save signal pins. Now you can see why- this device is driven by an ATTiny13A, and pins are precious indeed on this little guy. It does a bang-up job for us here, though, so it’s a small price to pay. You’ll also notice two chips that you don’t see much of in most hacks- the 74HC597. The sexy supermodel sister of this chip is the 74HC595; it gets all the attention because it’s useful for driving blinky lights.The 74HC597 is essentially the opposite- it takes a wide parallel input, and shifts it serially into a single bit output. It can do a couple of other tricks that the 595 can’t, but for our purposes, this is just the thing.
The basic operation here is simple. The 16-bit wide input bus is connected to the 597 shift registers. The microcontroller regularly reads the input bus by latching the registers, then shifting the contents into itself. The software then processes these bits into hexadecimal letters represented by seven-segment displays. Those display bits are then sent back out, and up to the Output layer above. This cycle is repeated at around 8Mhz. At this frequency, changes on the input bus can be detected faster than data changes on stuff I ever build (for now, anyway). That gives the illusion of sniffing changes on the bus instantly, when in fact we’re actually just sampling it at a high rate.
Okay, so does this all work? Let’s go to the breadboard to find out:

Looking good! On the breadboard there, you can see the ATTiny and one of the 74HC597 shift registers. Like the other layers, I only simulated half of this one, since the other half is identical. At the point this photo was taken, I had already completed the upper two layers, and only the Input layer remains on the breadboard, ready to be built for real. I tested the Input layer using a bank of pull-up resistors on DIP switches, which you can see on the smaller outboard breadboard. Outboard breadboard? That's a mouthful.
With the basic design ready to go on the breadboard, it was time for some software. As usual, I busted out the USBtinyISP programmer, my BreadHead programming header, and got to work. Here’s the code I came up with:
The main loop reads the bits from the input registers, clocking them as many times as needed. Those bits are stored in an accumulator. Once that’s done, the accumulator is broken out into four nibbles, which are used as an index into the hexadecimal “font” used on the displays. This lookup gives the output bit pattern, which is then sent serially to the Output layer.
At the end of this cycle, there’s one extra clock pulse. That pulse is needed to finish the shifting of the Output bits. You may recall from part 2 that I’m using a trick with the 74HC595s whereby I tie the two clock signals together to save a pin. This means that an extra pulse is needed at the end to finish displaying, since the latch clock is one pulse behind the shift clock. This might have been tricky to handle in the logic, but in code it’s a trivial fix! That’s one of the great things about straddling the software/hardware boundary in projects like this. You can solve each problem in whichever domain is easiest.
Similarly, you’ll note that the function to push the font bits to the display is reversing them. This is because, as you may recall, I’m using common-anode displays driven by 74HC595 shift registers on the output. This makes them effectively active-low. That’s easy to compensate for here in the software. Again- solving each problem in whichever domain is easiest.
Okay, it’s PCB time!

Pretty straightfoward stuff here. As always, the red traces are topside jumpers. The big header on the left is the input bus. The little header up top (JP3) is power for the whole device. The middle header (JP4) connects up to the Output layer. The switch is for powering the device on and off.
I used that Eagle file to generate photo-developer masks:

The Input layer is the middle pair of masks. I always make two of each and overlap them during exposure to compensate for any flaws in the printing.
Then I exposed the presensitized PCB, developed it, and etched it with my usual process:
Next, it’s drilling and jumper time. Sometimes I can’t get a contiguous ground plane on my PCB, so I bridge the chunks with some bare jumpers.

Well, almost ready. I somehow always manage to forget one jumper somewhere when laying out the PCB. A little underside hackery to the rescue, once again.
With the board all built, it’s time for final test. No problem, right? It worked on the breadboard, and the PCB has no errors, so it will surely work, right? Right?
Bzzzzt. Fail. With the complete board stack all hooked up, the display was not responding properly to inputs. It was outputting ‘0000’, no matter what the inputs given. This is where the modular design approach that I took really paid off. The upper two boards had been tested in isolation, and were known to work. So the problem had to lie in this new one. There were two possible problems- either the input bus was not being read properly, or the microcontroller was sending the wrong bits out to the display.
I eliminated the latter possibility by commenting out all the input processing code, and hard-coding a known bit pattern into the accumulator. When I did this, everything seemed to work perfectly. That narrowed the problem to the bits coming from the 74HC597 shift registers into the microcontroller. It’s logic analyzer time!

This was a great excuse to use this bad boy, which I got for a great deal on eBay. You may mock me for building what is essentially a very primitive logic analyzer, when I have a real one on my bench, but remember folks- it's the journey, not the destination.
The logic analyzer revealed that the correct data was coming out of the shift registers and going into the microcontroller, but the bit patterns coming out the other side were always the ‘0’ in the font. The green line on the scope is the output pattern. You can see one whole nibble, plus a bit of the next one. That’s the pattern to show a zero on the display. The red line is the input bits from the 74HC597 registers, and you can see that is clearly not a string of 0 bits. That could mean only one thing- the microcontroller was ignoring the input data! A quick look at my pin configuration in the source code revealed that I had neglected to set that data pin on the ATTiny as an input! How did this ever work on the breadboard? Well, just before programming the final chip, I did a little “clean up” on the code, and messed up one of the C macros which label the I/O pins. Whoops!

Here's a final test of the whole unit, once again showing the most delicious of all bit patterns, 1011111011101111

Here's a close-up showing the relationship of the three layers. One big mistake I made in the design is not allowing room for standoffs in the corners. I put them on the corners where there was room, but I had to rely on clamping force with some washers in one spot (back left corner in this photo). The "missing" corners aren't entirely needed because the header connections between boards hold those areas securely. I'll definitely fix this in the next version, though.
That’s HexOut! I have a feeling it will come in quite handy in my upcoming digital design projects. Thoughts? Snide remarks? Head on down to the comments below.