Friday, October 28, 2011

Decoupling with Bypass Capacitors

To ease myself into this hobby, I began with the SparkFun Beginning Embedded Electronics tutorials. They were a great way to start, and had me well on my way to making my first RS-232-controlled blinkenlight in no time. As part of that tutorial, they told me to install bypass caps near Vcc on all ICs. Okfine, monkey can add prophylactic caps everywhere. But why? The SparkFun page describes their function at a high level, but I've always wanted to know more. Cap monkey want pictures. Cap monkey want to know why he has a pill box compartment dedicated to his supply of 100 or so 0.1uF caps.

This post is what I've been looking for. Details. Pictures. Sample circuits. What more could a cap monkey want?

http://www.vagrearg.org/?p=decoupling

Friday, October 21, 2011

A full adder -- complete!

I started this post back in October when I actually finished the work, but I forgot to post it then.  So here it is now.

Three million wires later, my 8-bit full adder is complete.

Here it is with the front panel attached.  I use the front panel in lieu of switches and LEDs.


Here's the circuit by itself:



Each pair of columns calculates four bits.  Each bit needs nine gates, and consists of two dedicated 7400s (actually 74HCT00s) and a quarter of a common 7400.  If we number the ICs in column-major order, bit 0 is calculated by ICs 1, 2, and a quarter of 3; bit 1 is calculated by ICs 4, 5, and a quarter of 3; bit 2 is calculated by ICs 6, 7, and a quarter of 3; bit 3 is calculated by (wait for it) ICs 8, 9, and the final quarter of 3.

Due to layout weirdness, bits 0-3 are calculated by the right pair of columns, while bits 4-7 are calculated by the left pair of columns.  I'd love to say the vertical offset is intentional, but it's actually due to a layout mistake or change in plans, the details of which I've since forgotten.

Wiring this thing up was mind-numbing, but it was awesome to see it come to life (I tested each bit after I wired it up).

Here's the schematic.  My original intent was to build a 4-bit adder, but I had so much fun wiring(!), that I decided to do it twice, for 8 bits.


Thursday, October 20, 2011

A full adder

A full adder, slowly but surely....

Here's 3.5 bits (out of a planned 8):



... and 5.5 bits:






Only 1.5 bits to go!

Saturday, October 8, 2011

Front Panel up and running

At long last, I finished populating the front panel board.  Oh what an adventure that was.  I also built up quite the list of things to change for v2, if ever there is a v2.  Even if there isn't, I learned a lot about what to do and not to do for the next PCB I design.

Before I get into the details, here are some pictures of the completed front panel board:


Here's a video of the front panel controlling a simple 2-input AND gate from a 74HCT08N.  The whole purpose of the front panel is to make LEDs and switches unnecessary, but LEDs make an appearance in this circuit for validation purposes -- just to confirm that the front panel is doing what it says it's doing.


Things I learned:

Lots more and better silkscreening.  I'm having a heck of a time identifying the correct input and output pins, to say nothing of distinguishing between the +5 and GND pins.  I should've silk-screened the pin numbers all along the bottom headers.  Silk-screened IC numbers (the manufacturer's part-number -- not the Ux or ICx number) are less useful when they're under the IC -- especially if you're using sockets.  I also failed to leave any orientation marks to help me plug in the ISP cable correctly.

Status indicators.  There aren't any LEDs on the board, which can make it difficult to tell whether the board is on, whether it's receiving data, or in general what it's doing.  Eventually it would be fun to have LEDs on every input and output pin, but in the meantime, for v2, I'd settle for power and one that flickers when data is transmitted or received.

More space for I/O cable connections.  I'm still conflicted as to how I'd like to connect the front panel to the PCB.  I had originally thought to use ribbon cables with IDC connectors, but those are wide enough that I'd need to either widen the whole board or make a third and fourth row of I/O pins.  A further complication there would be that I'd need cables with IDC 1x4 on one end and 8x1 0.1" sockets on the other.  That could get expensive, but we'll see -- connector prices jump dramatically once you stray even slightly from the beaten path.

Pin orderings matter for the ISP connector.  I used the P6 connector in the schematic capture program, which numbers pins like an IC -- ascending counter-clockwise from the upper left.  Unfortunately, the pins are supposed to be numbered in zig-zag style, with 1 and 2 on the top row, 3 and 4 on the middle row, and 5 and 6 on the bottom row.  The end result of that was chaos, and a non-functioning ISP connection.  I ended up cobbling together a cable to go between the ISP and PCB which unraveled the confusion.

Pin orderings matter for the DB-9 connector.  The DB-9 part I used on the schematic used the male DB-9 pin numbering, which is the mirror image of the pin numbering for the female DB-9 that I'd eventually be using.  Happily, I was able to get myself out of that situation by mounting the DB-9 on the bottom of the PCB rather than the top.  Doing so actually made the design better, as my USB-to-serial adaptor takes the form of a heavy dongle that plugs into the DB-9 port, so mounting it on the bottom of the board kept the dongle from flipping the board over.  Unfortunately, getting to this realization required much gnashing of teeth, the purchase of an unnecessary male DB-9 connector, the sacrifice of a female DB-9 connector, and much soldering and desoldering.

Tuesday, September 20, 2011

My Front Panel boards arrived!

My Front Panel boards arrived!  Thanks Laen!  I placed the order on August 28th, and received the boards yesterday (September 19th).  They look black (this is what happens when you use a camera phone with bad lighting), but they're really purple.  Oh, so purple.


Monday, September 5, 2011

Front Panel Software

[This is a continuation of the Front Panel Hardware post; you should read it first]


Embedded Software


The software which runs on the front panel's microcontroller is about as simple as I could make it.  It uses the ATMega168's built-in UART to communicate with the host, with RTS and CTS controlled manually by the embedded software.

To keep things simple, I made everything query-response.  This way there's no need for timers or anything like that on the microcontroller -- the embedded software does nothing without first being asked.  There are four essential commands:
  1. Reset all output pins to 0.
  2. Set an output pin to a given state (1 or 0).
  3. Specify the pins which should be monitored for input.
  4. Return the value of all input pins.
Pins are identified using a three-digit number decimal number.  The first digit is 0 for output pins, and 1 for input pins.  The second and third digit are the zero-padded pin numbers as silk-screened on the PCB.  By separating the input and output pin spaces like this, we simplify the host program, and minimize the chances of error.  Here's a diagram of the pin numbers:

Host Software


The program running on the host is responsible for displaying the user interface, for passing output state changes to the front panel, and for keeping the indicators up to date.  I've implemented it in Python, using WxWidgets as the UI toolkit.

One of the primary requirements is that the user interface be customizable.  This is necessary because we'll be using the front panel with a number of different circuits, each of which will have different input and output configurations.  Accordingly, the configuration isn't hard-coded in the front panel program, but is instead driven by an external config file.  Here's an example:

toggle('Bit': 0='0', 1='1',)
momentary('Actions': 2='A', 3='B',)
indicator('Result': 100='1', 101='2', 102='3', 103='4',)
(Yes, it's true.  I was too lazy to tune the regexps to allow for the omission of the trailing commas)

We're using output pins 0-3, and input pins 0-3 to display a user interface with three different types of UI element.  Here's what it looks like when running:


The bit 0 and 1 buttons are toggle buttons -- one click is required to turn on the associated pin, and a second click is required to turn it off.  Action buttons A and B are momentary -- their associated pins turn on when the button is pressed, and turn off when it is released.  Indicators Result 1-4 change their background color when their input pins go high, and appear as shown when said pins go low.

Not a terribly complicated program, but I did have an interesting battle, and (re?)learned something new along the way:  UI toolkits and threads are like oil and water.  Tkinter (the default toolkit for Python) is like oil on fire.

In general, you confine all interactions with the UI to a single thread -- the UI thread.  Anyone else who needs to change the UI posts something to the UI thread, and lets the UI thread take care of it.  If you don't do this -- if you muck with the UI from a thread other than the UI thread -- the world ends in fire.  If you're lucky, you get a stack trace.  If not, just fire.

Tkinter exposes a monolithic event loop -- you call Tk.mainloop(), and it runs until it's time for your program to exit.  There doesn't appear to be a way to break the loop up into its component parts so you could, say, wait for UI events and events from your non-UI threads using an interface like poll(2).  Instead, everything is supposed to be done through the Tkinter event system.  External things wanting attention post events -- events which are passed to callbacks which have registered using bind.

This would be fine, except for the fact that the event-poster isn't thread safe either.  That is, you can't post an event from the non-UI thread.  In fact, the only way for a non-UI thread to get the attention of the UI thread appears to be through polling.  The UI thread asks to be woken up every n milliseconds, during which time it checks a queue of things that need attention.  I don't like this approach at all.

After much gnashing of teeth (I still can't believe Tkinter doesn't have a thread-safe event post) and failed attempts to get around this problem without using polling, I found wxWidgets (specifically wxPython).  WxWidgets has the same threading restrictions as Tkinter and other UI toolkits, in that it requires all UI interactions to take place on the UI thread.  The crucial difference, however, is that wxWidgets specifically allows events to be posted from non-UI threads.  Once you have that, everything is easy.

Aside: Why do I use threads?  For the most part I don't, except for the serial port handler.  Serial port reads and writes are handled by independent threads.  This allows me to have non-blocking writes, which in turn allows me to do writes from the UI thread, since I don't have to worry about blocking it.  On the read side, it's much simpler to have a separate read thread than to mix reading and writing.  The only downside of this approach is that some reads (indicator status updates) need to directly affect the UI, and thus I need a toolkit which allows me to talk to the UI thread from a non-UI thread.  WxWidgets allows that, and so everything comes together nicely.




Sunday, September 4, 2011

Front Panel Hardware

As described in an earlier post, I'm designing a front panel for the circuits in CODE.  After much delay (this is what happens when you have too many hobbies), I've got the first version ready.  This post will describe the hardware.  A subsequent post will describe the software -- both that which runs on my Mac and that which runs on the front panel's microcontroller.

At its simplest, the purpose of the front panel is to control outputs and monitor inputs.  The PC sends commands to the front panel via RS-232, and receives responses the same way.  So at the very least, we'll need a microcontroller and an RS-232 transceiver (a future version might switch to USB, but not this one).  Everything else depends on the number of input and output lines.

The most complicated front panel in CODE calls for 27 output lines and 8 inputs.  Even an ATMega644 can't handle that many (40 pins minus 7 for non-general-purpose pins, 4 for the ISP, and 4 for RS-232 leaves 25), so one way or the other we're using a latch for some of the inputs or outputs.  As it happens, I fried my ATMega644, and ended up prototyping this circuit with an ATMega168.  A '644 would've been a few dollars cheaper once latch and PCB size are taken into account, but it is what it is.  Perhaps a subsequent version will use the '644 (or whatever the USB-enabled equivalent may be).

The ATMega168 has 28 pins.  Subtract out the preassigned pins, those used for the ISP, the clock, and those used for RS-232 (I'm using RX/TX and RTS/CTS, though that too could be changed in a future version), and we have 13 pins.  13 pins is just enough to get us 27+8, assuming we use three SIPO latches and 3 direct lines for the output, and one PISO latch for the input.  So that's what I've done.

Here's the schematic:

This was my first schematic drawn using Kicad, and I'm very happy with the way it turned out.  I had to draw a couple of the symbols myself (the switch, to my surprise, and the barrel connector), but other than that it was pretty painless.

Having drawn the schematic, having gone through the Teho labs tutorial, and knowing that I'd be using the front panel again and again without wanting to monopolize the breadboard, I decided to go all the way to having a PCB fabricated.

The first step on that road, with Kicad, is footprint assignment.  Eagle differs in this respect, as you pick your footprints when you lay out the schematic.  I have to say I really prefer the Kicad approach here.  When I'm putting together a schematic (especially for something I have no intention of turning into a PCB), I don't want to have to care about the footprint.  Not at all.  So anyway, picking footprints.  Kicad was missing a few that I wanted, so I ended up making my own.  That was much less painful than I had expected.

After footprint assigment, board layout.  Here's where I discovered that I'm nowhere near as intelligent as the auto-router.  Sigh.  All that education -- for nothing.  When I first laid out the board, I figured no way the auto-router could route this -- it's too complicated (I laugh at that now).  So I routed it myself.  A couple of hours later, I had it routed, but the last couple of traces had 3-4 vias each as they wended their way about the board.  Oh all right, I'll try the auto-router.  A few minutes later, it has the entire board routed, with no vias.

Here's the routed board, in all its glory:


In real life, though, the board won't be green -- it'll be purple.  I decided to use Dorkbot PDX to make the PCB. They have reasonable prices and a reasonably short turnaround time.  Shorter than the places which make the boards overseas, but more expensive.  The price difference was low enough that I was happy to pay it to have the board made here.  I can't wait for it to arrive.

[This post is continued in Front Panel Software]