Sunday, December 18, 2011
It's alive!
Wednesday, December 14, 2011
Dave's Lab Power Supply Design
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?
Friday, October 21, 2011
A full adder -- complete!
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.
Thursday, October 20, 2011
A full adder
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
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.
Tuesday, September 20, 2011
My Front Panel boards arrived!
Monday, September 5, 2011
Front Panel Software
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:
- Reset all output pins to 0.
- Set an output pin to a given state (1 or 0).
- Specify the pins which should be monitored for input.
- Return the value of all input pins.
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',)
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:
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
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]
Friday, September 2, 2011
Switching to Kicad
It was this tutorial that finally got me to switch. Highly recommended. It walks you through the creation of a small power supply board, from schematic capture all the way through layout and routing. I liked the way everything fit together, and so I decided to switch.
One wrinkle. Kicad is unusable on the Mac as of "kicad_osx_v3056_DEV". There's some display problem which causes part moves leave trails, requiring you to hit refresh all the time. There was another problem which made parts disappear until you hit refresh. In summary, not usable.
As it happens, Kicad works pretty well on Linux, and VMWare Fusion makes Linux (Ubuntu) work pretty well on my Mac, so now I've got a VM running Ubuntu running Kicad. I don't even have to use GNOME -- I just ssh into the VM and remote-display Kicad back to the Mac's X server. Coordinating files between the two machines is a bit of a pain, but I've started checking everything into Mercurial, so I use a master repository to coordinate between the two. I'll certainly be happy if/when Kicad starts working properly on the Mac, but until then I've got a perfectly workable solution.
A couple of downsides, now that I've switched, and have spent some time working with Kicad:
- The documentation is spotty. Not entirely surprising, but annoying just the same. The documentation that does exist is fine, but there are lots of gaps. I tried Kicad once before, back before I settle on Eagle, and had discarded it because the docs were so incomplete that I had a hard time getting my head around it. Without the Teho Labs tutorial I mentioned above, I wouldn't have tried it again.
- The part libraries are nowhere near as extensive as those for Eagle. Or perhaps they are, but they just don't contain what I want. I found a site which has auto-conversions of Eagle parts, but they weren't really what I wanted either. I've resigned myself to having to design the occasional symbol and many footprints. This doesn't turn out to be as bad as I had at first thought, especially since I don't use that many parts. The first schematic/board involves the design of several parts, but subsequent ones just reuse those created for the first. This part generator, in particular, is very useful.
Monday, July 25, 2011
Friday, July 8, 2011
Impedence Tutorial
Part 1
Part 2
Wednesday, July 6, 2011
Voltage Regulator Board
Sunday, June 12, 2011
Starting to build the X&O RR
Today, I decided how, and did it. I used thumbtacks to attach the 1:1 sheet to the plywood. I then placed every turnout on the layout, and partially nailed them down. I also built the connecting pieces for the switches which are really close together, and placed (and lightly nailed) every other piece of non-curve track. The curves will be flex track, and will involve some nudging, so I didn't bother trying to fix their positions just yet. Here's everything lightly-nailed:
Then I removed everything, annotated the holes so I know where to pick up next time, and labeled all the pieces.
One little snag I hit. My shiny new Xcelite 170M shear cutters ... well ... sheared:
I tried to soldier on with the cutting of custom-sized connecting pieces from Flex Track without the shear cutters, but the quality of the cuts suffered pretty dramatically as a result. Right tool for the job and all that -- this is pretty clearly a necessary tool.
Before laying the track, I tested each turnout. I first tried hooking the turnouts up to the rail outputs of my DCS50 DCC controller. I figured that DCC looks an awful lot like AC, so maybe it'd work. It made the turnout solenoid vibrate, but not much else. A Google search revealed a number of turnout guides which mention using the AC Accessory outputs on the controller to power the turnouts. Alas, I have no such output. It looks like I need a separate power supply for the turnouts. Whether that's a straight DC power supply with individual switches or something fancy like a Digitrax DCS64 (to allow DCC switch control), I have yet to decide.
Back to turnout testing. When I realized that I couldn't use the DCS50, I hooked the turnouts up to my bench DC power supply. At 15V (and a power-supply-imposed max of 1A), most turnouts switched properly, if a bit hesitantly. A couple of them got stuck. Turning it up to 20V fixed that problems. I don't know if this is an artifact of my use of the bench power supply or something more. Will I need something like this capacitive discharge controller?
I had intended to move straight from this initial placement step to the laying of roadbed and track, but now I realize that I need to think more about how the wiring will work. If this layout were on a table, that'd be easy -- I could drill holes, and route the wires under the table. As my wires will share the same side of the plywood with the track, I need to figure it out now. So much to do.
Monday, June 6, 2011
Transistor basics with DC
(*) It could've been because I started with The Art of Electronics, which is a bit like drinking from a fire hose.
Tuesday, May 17, 2011
My own microprocessor
I’ve decided to build the microprocessor described in CODE. In that book, Petzold describes the development of a processor pretty much from scratch. He describes everything but the control logic, so that’ll give me a nice exercise to start out with. Follow along for the non-control logic (with a few tweaks along the way), and build the control logic from scratch. Once I do that, I’ll feel confident enough to build the second version from scratch, to my own design.
Petzold describes his circuits in terms of relays, with a control panel made of switches. Obviously I’m going to use TTLish ICs for the circuits, but I still need some sort of control panel. If the 7-segment LED circuit taught me anything, it’s that switches need debouncing if they’re going to have a prayer of working properly. The most complicated front panel in Petzold’s book has 27 switches on it, each of which would presumably need its own debouncing circuit. No thank you. Furthermore, that front panel is eventually used for loading in programs. Toggling programs in may have been fun in 1972, but I really don’t have the patience for it today.
Instead, I’m going to use an AVR for the front panel. I’ll have a program which displays a UI running on my Mac. It’ll talk to the AVR via RS232, and will tell the AVR which ports to enable/disable/read. Eventually I’ll write something which uses that same interface to “toggle” in new programs. All will be sweetness and light.
Monday, May 16, 2011
Gran Fondo NYC
We started on the lower deck of the GWB, in the NJ-bound lanes. They had initially capped the race at 8,000 riders, but fell well short of that. According to the announcer, just under 2,500 people lined up on the bridge. So, a bunch of people on the bridge, but a manageable number. Not so many that I wasn't on my bike, pedaling, by the time we crossed the starting line. Over the GWB, exit to the right, an overpass over the highway, and then down into Palisades Park. There were police at every intersection, both here and throughout the ride, ensuring that bikes had right of way. I only had to stop twice for traffic signals during the entire ride. It would be difficult to overstate how wonderful it was to have the police directing traffic for us.
I brought a bunch of GU and two Clif Bars, but was planning to rely primarily on the aid stations for nutrition and hydration. The aid stations were nicely spaced and nicely stocked, allowing me (with one exception) to refill both bottles before they ran dry. The one exception was the last aid station, which was a mile or two past when I ran dry. If nothing else, this has convinced me that I need a seatpost-mounted bottle cage just like I have on my P2C. Two bottles just isn't enough for a long ride. Either you're stopping every 40 miles or less for a refill (more if it gets hot, as it did for this ride), or you're constantly in danger of running dry. Riding dry is no fun at all, nor is riding in fear of it. With three bottles, I'd be able to do 50 miles between refills, which would suit me just fine. I like to eat something real (i.e. more than a Clif Bar or a GU) every 50 miles, so having my hydration stops match up with my nutrition stops would be ideal.
But back to the race. We headed out on River Road. So many people passed me on River Road, but I caught several on the hills. I was keeping my speed down on the inclines, as I knew there'd be plenty of misery ahead. Once we popped out on 9W I started to push it a bit (20mph until Nyack) until I was able to truly convince myself to stop trying to keep up with groups on the flats. Thanks to the traffic control and my penchant for riding solo, I had State Line Hill to myself, and was able to take the entire descending traffic lane. I've never had such a care-free full-speed descent of State Line Hill.
Up Old Mountain Rd, over Toga/Rockland hill -- basically the standard Little Tor / Orchards route. The first timed climb of the day was Little Tor. I'd been practicing this one, and was ready for it. It's 3/4mi long, with just over 400' of climb. There's a sharp incline at the start, and then constant misery for the rest of the way up. This was my third nonstop time over (I stopped twice on my first time over Little Tor, so that time doesn't really count). There's a picture of me grimacing my way to the top of Little Tor, with Christophe Vandaele (of SBR fame) right on my wheel. I don't know whether he passed me or not, but it's nice to know that I was faster than someone getting over that hill. Even if he was on a single speed, and likely working twice as hard as I was. I finished the climb in 8:26.96 according to the chip, and 9:58 according to MyTracks/Strava, which uses a different starting point. 9:58 is a PR for me by just over a minute.
After Little Tor, on to Buckberg Mountain -- a hill I hadn't climbed before this race. Buckberg Mountain Road is an absolute bastard. The specs aren't that crazy -- at 367', it's about 50' shorter, and it's almost 0.5mi longer (1.2mi vs 0.75mi) -- but the profile is evil. Rather than being a continuous climb a la Little Tor, it's a series of steep windy sections (with increasing grade) interleaved with less-steep (but still ascending) sections. Yeesh. It might be easier the second time around (and there most definitely will be a second time around), but it's always going to be hard. Official time: 10:12.51, MyTracks/Strava time: 11:10.
The approach to Buckberg was through parts of Stony Point that I've never ridden before, and which are a nice change from the NYS Bike Route 9 route that goes east of 9W, through Haverstraw. Buckberg Mountain Road can also be viewed as a way to bypass 9W through Tompkins Cove, but it's a hell of a way to do it.
With Buckberg Mountain done, off we go to Bear Mountain -- the toughest climb on the ride. Strava rates the other climbs on the ride as category 4. Bear gets a 2. The grade isn't as brutal as, say, Little Tor -- the rating comes from the total climb (1200' from 9W) and the distance (4.5mi, again from 9W). There's also the matter of just getting to Bear. There's a lot of up and down during the few miles from Buckberg to Bear, including one category 4 climb.
But eventually we get to Bear Mountain (hooray for the police blocking traffic so we could get onto 7 Lakes from 9W without even slowing down). I'm used to counting distance and height from 9W, but the GFNY organizers put the timed climb start at the traffic circle, which is about 3/4mi of the way in, and about 200' up. Anyway, I decided I wanted to do the entire thing nonstop, from 9W all the way to the summit, so I skipped the aid station. This made my ride unpleasant, as my bowels were feeling a bit compressed, but I'd probably do it again given the chance, as it let me knock out the whole 1200' in one go -- something I'd never done nonstop before. This was only my second time up Bear Mountain. The first time I did it, I had to stop twice on Perkins to catch my breath.
Climbing Bear was a "simple" matter of grinding through the climb, counting off the tenths of a mile until the top. My power meter says I was doing an average of 200 watts for 35 minutes up that hill. First, there's 7 Lakes Drive, which is about 2.25mi from 9W. 7 Lakes is relatively easy, and even has some oh-so-very-brief flat bits. Then comes Perkins, which is narrower, steeper in sections, and goes all the way to the top. And oh, what a blessed top. Pictures, resting, and then almost 4 miles of descent to the aid station. Real bathrooms, and my newfound favorite ride food: PB&J bagels. I started eating those at each aid station from Bear Mountain on, and was very pleased with the results. I basically did the ride on those and GU, with a few Stacy's Pita Chips thrown in now and again for salt.
In the distant future (about 20mi away) is the fourth timed climb -- southbound over Little Tor -- but first there was a bit of a diversion over to Gate Hill Rd. The route to Gate Hill took us back over the Bear Mountain approach climbs (a 1mi cat 4 being the most notable), to the base of Buckberg Mountain. We didn't retrace back to the summit, thank goodness, but we did climb over the side, on Mott Farm Rd. I think we probably did at least half of the Buckberg climb, over some truly evil inclines. Once past the intersection with Buckberg Mountain Road, the route was flat to descending (ish) to the base of the Gate Hill climb. Very pretty scenery back there.
Gate Hill begins about a mile before the Parkway, and climbs about 700' to its intersection with Willow Grove. Why this wasn't a timed climb, I'll never know. Its only saving grace was the elevation profile. Gate Hill is a series of climbs interleaved with flat sections (perhaps they only felt like flat sections by comparison). The motivator for that climb is the knowledge that you're going to get to descend Willow Grove. And what a glorious descent. About 325' in 1mi with a gradual turn in the road, and nobody entering on the right. That hill never gets old. Let me rephrase. Descending Willow Grove never gets old. Ascending Willow Grove is a different story entirely. A very sad story.
Once we descended Willow Grove, we had about 2mi to go to the aid station near the base of Little Tor. Rehydrate, re-nutrate(?), and up over Little Tor we go. This side is pretty easy. There are some individual climbs leading up to it, followed by a "big" constant climb that ascends about 200'. Nowhere near as difficult as doing Little Tor in the other direction. The race organizers were able to close Little Tor in our direction of travel -- northbound on the way out, and southbound on the way back. As such, I was able to descend the south face of Little Tor without fear of traffic. This was the first time I've ever enjoyed a trip down the south side of Little Tor. With traffic, you normally have to ride down on a sketchy shoulder. Without, I was able to do the whole thing in the traffic lane, which was heavenly.
We returned along South Mountain to Ridge, but kept going on Ridge to Strawtown, rather than going back to 9W via 304. In West Nyack, we jogged over to Western Highway, and then to King's Highway, which took us to Piermont. This return was largely flat, with some light rollers, and was a nice change from the brutality of the preceding miles. At the end of King's Highway, a quick jaunt down Orangetown led us to Valentine, Highland, and back to 9W. This was the first time I'd gone from King's Highway to 9W without going over the bridge. It's a nice detour to know, even though it means you have to climb Highland to get back to 9W.
The organizers didn't see fit to give us police protection along 9W, which was reasonable, since we weren't on it for very long. The final aid station was in the dip before IBM, in the parking deck under the unoccupied office building. That final station was only 20 miles after the one that preceded it, but nonetheless I'd somehow managed to go through both bottles during that 20 miles. I didn't refill after eating at the Little Tor stop, so maybe I did the 20 on a bottle and a half or less. Regardless, I ran dry about a mile before this final aid station. Had the last station not been there, I would've been forced to stop at the deli before State Line to refill -- no way was I going to to State Line and friends on dry bottles.
Refilling at the final aid station, even though it was only 12mi from the finish, turned out to be a wise decision for other reasons as well. Due to apparent permitting reasons, the organizers had to change the finish line location from somewhere in NJ (Edgewater-ish?) to Palisades Park. In order to get permission to do that, they had to have a "simple" (their words) finish line. I had assumed that "simple" would at least mean water. Turns out not so much. The only thing at the finish line was a timing mat. I know at least one person who skipped the final aid station, assuming that the finish line would have water, and ended up riding dry back to the city. My stop saved me from that (I've done it before -- it's not fun), and left me with enough to drink all the way home.
With a finish line in Palisades Park (specifically, the southern traffic circle), the end of the route has to involve a trip down River Road. Specifically, it must enter Palisades Park at Alpine Approach, and must descend River Road. I feel for the out-of-towners who had to do that descent (NJ hasn't repaved that road in something like 300 years) -- especially for the many who were seen fixing flats near the bottom of it. Oh well. We didn't want them to leave having only seen the best of NYC-area cycling -- they should see the worst of it as well.
The congestion on River Road hill wasn't nearly as bad as I had expected, and so I was able to keep to the relatively smooth parts. A few more miles on River Road, and I rolled across the finish line in style. 102mi. 8700' of climbing. The hardest century I'd ever done: FINISHED.
Now, to get home. The finish line is about 1/3 the way up River Road from the water, with an ascent of the remaining 2/3 needed to get to the bridge. I'd blown that ascent all out of proportion, and had been very nervous that I wouldn't be able to make it after having completed the GFNY. Boy was I mistaken. That hill was nothing compared to what I'd been riding, and I sailed (ok, crawled) up it. A short ride home, and a nice relaxing evening.
A word on training. I wouldn't have been able to complete this ride without the awesome training plan I got from Brad Gansberg. That man had me climbing more hills that I thought existed. But it paid off. Oh, how it paid off. Midweek rides which included several repeats of River Road hill plus a trip up Eisenhower. Weekend rides which routinely topped 6000' of climbing. So many hard rides. But I'm oh so happy with the result.
Next stop -- a 10,000' ride. Maybe one of the ones put on by the PA Randonneurs.
RTS/CTS handshaking and waveforms
Here’s what I’m trying to do: I have a DB-9 serial port hooked up to an ATMega644 microcontroller (MCU), with an SP3232EB RS-232 transceiver in the middle, as shown below:
Three-wire RS-232 (RX, TX, GND) wasn’t sufficient for my purposes, so I’m going to do five (RX, TX, RTS, CTS, GND).
When we talk about RX and TX, we’re going to do it from the MCU’s perspective. That is, we read from the PC on RX, and we transmit to the PC on TX. RTS and CTS have non-reversible meaning, so no clarification needed.
How does this all work? RTS/CTS handshaking is used to ensure that the peripheral (the MCU, in our case) is ready to handle data from the host (the PC). It doesn’t appear to apply in the other direction (or if it does, I haven’t found the need to care). The idea is that the PC is powerful enough to handle just about anything you may care to throw at it, but the dinky MCU isn’t so lucky. The PC can easily overflow any buffers the MCU may care to put in place, so the MCU needs a way to tell the PC when the PC is allowed to send new data. That’s where the handshaking comes in.
There are two ways to do RTS/CTS handshaking. The first is request/acknowledgement. When the PC is ready to send data, it uses RTS (request to send) to tell the MCU that it (the PC) has data to send. I say “uses” because the logic levels are a bit funny, as described below, but are unimportant for now. The MCU sees that the PC is ready to send data. When the MCU is ready to receive it, it uses CTS to tell the PC that the data can be sent. The PC watches CTS to see a) when it can start sending data, and b) when it must stop. The second type of RTS/CTS handshaking does bidirectional metering (more details here). I've chosen to implement request/acknowledgement, but conversion to bidirectional would be straightforward.
When interfacing RS-232 and TTL, we have to deal with two different standards for logic levels. TTL is 0-to-not-zero. 0-to-5, in the case of my circuit. 5V is asserted; 0V is not asserted. RS-232, on the other hand, uses positive and negative voltages. A positive voltage is referred to as a space, while a negative voltage is a mark. The transceiver’s job is to translate between RS-232 and TTL voltages. It does so by mapping RS-232 positive to TTL 0, and RS-232 negative to TTL 1. That is, the TTL voltages are roughly the inverse of the RS-232 voltages.
Here’s the letter p (0x70), being sent across the wire at 9600 baud, 8N1:
RTS and CTS are normally held high (TTL 1). When high, no request (RTS) has been made, and no permission (CTS) has been granted. When the PC is ready to send data, it brings TTL RTS low. The MCU sees this. When the MCU is ready to receive data, it brings TTL CTS low. The PC sends data as long as TTL CTS is low, and stops when the MCU takes TTL CTS high. One imagines that the sender is allowed to finish the current symbol (byte), but I haven’t verified that.
Now, to wire everything up. As stated above, we’re using RX and TX from the perspective of the MCU, as that’s how everything is labeled on the MCU’s end (i.e. in the MCU itself and in the transceiver).
SB3232EB (MAX232) | |||||
---|---|---|---|---|---|
Function | Direction | DB-9 pin | RS-232 pin | TTL pin | AVR pin |
RX | PC to AVR | 3 | 13 (R1IN) | 12 (R1OUT) | 14 (PD0/RX) |
TX | AVR to PC | 2 | 14 (T1OUT) | 11 (T1IN) | 15 (PD1/TX) |
RTS | PC to AVR | 7 | 8 (R2IN) | 9 (R2OUT) | 17 (PD3) |
CTS | AVR to PC | 8 | 7 (T2OUT) | 10 (T2IN) | 16 (PD2) |
I messed up the wiring a couple of times until I drew up the above chart. Then everything went together smoothly.
Here’s the end result, captured by an Open Bench Logic Sniffer via the OLS LogicSniffer client:
We’re looking at an attempt by the PC to send a multi-character string as fast as it can, only to be metered by the MCU. Please excuse the bubbles -- when zoomed in, they show the byte values being transmitted. As we can see, the MCU idles with CTS low (ready to receive). A character comes in, and the MCU raises CTS (stopping further transmissions from the PC) while it processes the received byte. In this case, the program running on the MCU sends some data back to the PC describing the received byte. Once that’s done, the MCU lowers CTS to receive another byte, raises CTS once that byte is received, and repeats the process. By doing this, we receive everything the PC wanted to send -- nothing gets lost due to MCU buffer overflows.
Here's some sample code, written for the ATMega644:
#include <avr/io.h> #define UBRR_ASYNC_NORMAL(baud) \ (((((F_CPU * 10) / (16L * baud)) + 5) / 10) - 1) #define RTS PD3 #define CTS PD2 #define BAUD 9600 static int uart_haschar(void) { return (UCSR0A & (1 << RXC0)); } static int uart_getchar(void) { while (!uart_haschar()); return UDR0; } int main(void) { UBRR0H = UBRR_ASYNC_NORMAL(BAUD) >> 8; UBRR0L = UBRR_ASYNC_NORMAL(BAUD); UCSR0B = (1 << RXEN0) | (1 << TXEN0); UCSR0C = (0 << USBS0) | (3 << UCSZ00); DDRB = 0b11101111; // PB4 = MISO DDRD = 0b11110110; // PD0 = RX, PD3 = RTS // Inhibit CTS by setting it to TTL 1 (RS232 off) PORTD |= (1 << CTS); for (;;) { // Wait for host to set RTS (set it to TTL 0) while ((PIND & (1 << RTS)) != 0); // Set CTS (TTL 0 == RS232 on) PORTD &= ~(1 << CTS); char c = uart_getchar(); // We have a character, so clear CTS (TTL 1 == RS232 off) PORTD |= (1 << CTS); // Do something with c } return 0; }
There, of course, other ways to guard against buffer overflows. We could read from the UART using an interrupt handler, but that a) complicates the code considerably and b) only works if we can keep up with the uncontrolled data rate over the long run. Perhaps more realistically, we could structure our commands to the MCU such that they all have responses, offloading the responsibility for guarding against overflows onto the PC. That won’t work, though, if we want the microprocessor to be able to generate its own messages asynchronously.
Updated: Clarified terminology for ATMega/AVR/MCU, added diagram, fixed T1IN pin number, described the other way to look at RTS/CTS handshaking for bidirectional use.
Updated: Changed initial setting of PORTD so it just sets the CTS bit. Not absolutely necessary, since there's nothing else in this code that'll be affected by it, but it'll help anyone who tries to cut'n'paste.
Sunday, April 17, 2011
Shift Registers
In order to do this, though, I need 8-9 output pins on the microcontroller which can be devoted to the output stage. As it happens, the ATTiny2313 should have enough, and is about the size of the ATTiny85 and one of the shift registers.
But enough about that. I wanted to play with shift registers, even if I wasn't going to use them in the digit decoder. To do that, I used an ATMega644 to drive a single 7-segment display. For input, I used four switches connected to a 74HC165N shift register. Output was via a 74HC565N shift register. Why shift registers and an ATMega644, which is wildly overpowered for this application, and which could read the switches and drive the 7-segment display directly? I didn't want to have to worry about microcontroller constraints (RAM size, etc), and wanted to concentrate on the shift registers.
This is the most complicated circuit I've ever breadboarded, and it just about fills up the breadboard. Everything from the ATMega644 to the left on the lower row is necessary support stuff (from right to left, the MCU, RS232 transceiver, and power switch. So basically support stuff takes up 1/3 of the board, leaving me the other 2/3. I'm going to need to switch to the big board soon. I'm tempted to leave the ATMega644 set up on this one so I don't have to rewire it and the transceiver whenever I want to use it. At the very least, I'll probably leave it set up until I verify whether I can make the hex decoder work.
Lessons learned (or relearned):
- Fuses are important. Not the kind that blow when there's too much current (though they're useful too), but the kind Atmel uses to configure AVR processors. The first one to bite me was the one that controls the JTAG interface. With it enabled (which it is by default), PC2-PC5 are dedicated to JTAG use. PC0 and PC1 function normally, which gave me one heck of a time trying to figure out why an early 3-LED circuit (one each on PC0-PC2) would only partially work.
- The other important fuses are the ones which control the oscillator. Atmel has a bewildering array of oscillator configuration options, involving 8 of the 19 fuses on the ATMega644. This AVR fuse calculator was very helpful.
- To write fuses, invoke avrdude as
avrdude -p part -U fusename:w:0xvalue:m
- To read fuses to stdout, invoke it as
avrdude -p part -U fusename:r:-:h
- I'd learned about aliasing while watching an EEVblog review of the "budget" Agilent 2000-series oscilloscope, and this time I got to see it in person. I wanted to verify that the processor was running properly at 16MHz, so I set the CKOUT pin, and ran it through my PCSU1000. I don't remember which Time/Div setting I was using, but I was seeing a stable output sine wave at about 1kHz, rather than 16MHz. Hilarity ensued until I remembered Dave's mention of aliasing, at which point I decreased the Time/Div setting. Low and behold, a 16MHz wave appeared. So that was exciting.
- You don't use the same variable for writing AVR ports as you do for reading them. PORTn for output, PINn for reading. Reading from PORTn, expecting to get input values, will only lead to sadness and dismay.
Wednesday, April 6, 2011
Plans for my next design, and a breakout board for the AVRISP2
For giggles, let's cost it out (all prices from mouser.com for individual quantities); $6.70 for the ICM7212, $0.47 for the timer, and $0.69 for the mux. We'll assume that the resistors, capactors, and transistors needed for the 555 timer glue and for the inverter are free. $8.55 for the whole thing. Oh, and the ICM7212 is huge.
All of this is a long way of justifying doing it with a microcontroller instead. The added advantage of doing it with the microcontroller and shift registers is that I'll likely see them again (and again and again), so it'd be helpful to gain some experience.
- The Helping Hands are amazingly useful. Now I have a stable suspended platform on which to solder. It makes things like header pin alignment considerably easier.
- The default font in TextEdit (12pt Helvetica?) has precisely the right vertical spacing when you print at 50%.
- To attach the labels, print on a full-size sheet of paper, cut out the width to fit (from the top of the VCC to the bottom of the MOSI), but leave it an inch or two longer than necessary. This gives you a nice handle with which to hold the label while you superglue it to the plastic thing on the header.
- I'm not a huge fan of long pad-to-pad solder bridges. I like wires. They're much less fussy than bridges.
- Hot glue is, once again, my friend. I used it to protect the underside of the PCB.
- This version's pin mapping between the 2x3 header and the 1x6 is based on shortest distance. Unfortunately, that approach results in a 1x6 ordering that doesn't match the pin ordering on the ATmega644. As I'll likely be doing most of my programming with the mega, I'd rather the breakout board just drop into place, without requiring me to string wires hither and yon. Version 2's mapping will follow the ATmega644 pin ordering. VCC and GND may be shifted back on the board, behind the 2x3, allowing them to plug directly into the breadboard power rails.
- Pin labels should be visible regardless of breakout board orientation. In this version, they're only visible from the front, which makes it awkward when I install it with the front facing away from me. The paper is fragile, but I knew that going in. We'll see how it works out. I may need to switch to another material.
Monday, April 4, 2011
Mac Serial Program
Thursday, March 31, 2011
Evil Ride
The River Road repeats were exciting, as always. I seem to be able to knock out three without much difficulty (though my back isn't a huge fan). The unpleasantness kicks in about halfway up the fourth. I can only imagine how exciting next week's five repeats will be. All the whining aside, it is pretty cool to know that I can string together that many repeats and still walk away from it, so to speak.
This was only my second time up Eisenhower. The descent to Eisenhower, on Hillside (from 9W), is easily one of my favorite descents in the area. Perhaps I'll grow jaded, but for now it's lots of fun. I don't know exactly why I like it more than State Line. Maybe it's that the descent is windier. Maybe it's because I haven't done Hillside three thousand times. Who knows.
Eisenhower has three hills. The first and the third are brutal. It's like force work, without the inconvenience of needing to change to a higher gear. The scenery is nice, though. Giant mansions (in varying degrees of tastefulness) line the road. You could hear a pin drop in this neighborhood after everyone's gone to work, so there's almost no traffic. Just climb, climb, climb, climb.
Here's the descent to, and ascent of, Eisenhower. Yes, the camera was on sideways. Sorry.
Sunday, March 27, 2011
4-bit counters
Pretty picture and circuit: