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.
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:
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!
I used that Eagle file to generate photo-developer masks:
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.
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!
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!
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.