UPDATE: Design Files (source code and gEDA PCB and gerber files) These files are (C)2007-2008 Allen Wan and are licensed under GPLv3.
Some time ago, Eric Rechlin and I decided we were going to make an RS-232* cable for the HP 50g. For some strange reason, HP designed the 50g with a serial port, but then didn't bother offering the cable that went with it.
No problem, we thought. We can just slap together something with a MAX232 for a couple of bucks and free the 50g of this unfortunate "business decision". Actually, I was just in it to get some use out of a new soldering/electronics lab that I was putting together and I also need an excuse to design a PCB, something that I had never done before and wanted to try. What better way to learn it all by putting together a silly little hack. Rumor had it (i.e. Eric said) that it was just a serial port at TTL/CMOS levels that just needed to be converted to RS-232* signal levels. To makes things better, the calculator supplied +5v and GND!
This might be a good place to note that I had never even owned an HP calculator when I started. I grew up in the Dallas/Fort Worth area and my dad worked for TI since I was 2. TI calculators seemed to always just "appear" when I was a kid, so that's what I used. In fact, I never really liked graphing calculators either. Although I had a TI-86, I rarely used it, preferring my TI-34's. I had four of them. I collected one of every version released. They've been discontinued and the last one I had just had its LCD screen give out. It was replaced with an HP 50g!
As our design progressed, we soon discovered that the warnings by others who had tread this path were not unfounded. The problems were largely a result of not realizing what we were being given. The signal levels from the calculator were 3.3v and appeared to come directly from the internal microcontroller. The provided power was actually at around 6v and was a direct connection to the batteries. This connection remains even when the calculator is turned off. So far, we were still not deterred. In hindsight, this was just us being fool-hardy since a far stranger thing also occurs when the calculator turns off. The Rx line is driven low! This is when we should have realized that this was not going to be the trivial task that I had expected.
Somewhere between deciding that we wanted to have an activity time out and realizing that we somehow needed to turn off the receive line when the calculator turns off, we added a microcontroller to our design. Additionally, the 6v power supplied was technically above spec for our devices. Worse still, 5v wasn't going to be low enough either. The data lines from the calculator connected directly to the internal microcontroller (of the calculator) which ran at 3.3v. The ESD protection diode on the receive line was simply shunting current to the internal 3.3v rail when we drove the line at 5v. This meant we needed a voltage regulator to bring the 6v down to 3.3v so that we could safely communicate with the calculator without risking damage.
Unfortunately, space was not on our side and voltage regulators tend to use a lot of space for power dissipation. The call was made to use an in-line LED instead. There were many benefits to this design. An LED is much cheaper than a voltage regulator. At just an 0603, a surface mount LED is much smaller than most voltage regulators. An inline LED also provides a wealth of information about what is happening on the board. The luminance of an LED is linearly proportional to the current through it. By looking at the brightness of the LED, it is possible to determine how much current is being used and with experience, deduce which circuit elements are powered on.
In the end, we settled on a 565nm LED with a bandgap of 2.2eV = (4.135E-15 * 3.0E8 / 5.65E-7) = (Plank's constant x speed of light / wavelength of light) and a built-in voltage of 1.8v = (2.2 - 0.4)= (bandgap - approximate gap between Fermi level and conduction and valence bands) which means that the internal voltage of our circuit will be (6 - 1.8) = 4.2v to (4.5 - 1.8) = 2.7v. Not bad for a ten cent LED. Our microcontroller is speced down to 1.8v and the level converter is speced from 5.5v to 3v. This technically means that at 2.7v we are slightly beyond spec but as a practical matter this doesn't seem to be an issue since typical behavior is that the level converter still functions and most batteries traverse that voltage region (from 4.8v to 4.5v for 4xAAA) fairly quickly as they discharge.
Now, if you made it through that brief jumble of math and physics you may now be wondering why this doesn't still spell trouble for over driving the receive line. After all, 4.2v may be lower but it's still higher than the 3.3v rail of the calculator's microcontroller. Here, we use an in-line 2.2k resistor to drop any remaining voltage thus minizing the amount of current shunted to the 3.3v rail which in the worse case comes out to (4.2v - 3.3v) / 2.2k = 409uA, neglecting any forward voltage drop across the ESD diode which will help a little.
This same resistor is also used by the microcontroller to detect when the calculator either turns off or closes IO. In both cases, the calculator drives the receive line low. The idle state of the Rx line from the level convertor is high and so this would otherwise cause the drivers to drive in opposite directions. With the in-line resistor, current is limited and the voltage difference drops across the resistor. The microcontroller polls for this situation by taking logic values on both sides of the resistor. When a difference is detected, the microcontroller shuts down the level converter and enters one of two sleep states which is determined by the state of the Tx line. The calculator drives the Tx line high when closing IO and low when turning off.
The arrival of our first boards gave us a couple major scares. The pads I had used were for a TSSOP and not a SSOP. Also, I swapped the Rx and Tx lines to the DE-9 connector. Luckily a MAX3221 also comes in a TSSOP which happens to be the only other size that it comes in which was promptly ordered. Also, the new soldering station came in handy for cutting the Tx and Rx lines and reversing them. (The board was reved to fix both of these problems before production.) We also found a clip on connector for a 14 pin SOIC which allowed us to in-system-program the ATtiny24V even after attaching it to the board. The normal method of including a program header in the design just wasn't going to work with our space constraints.
Code was quickly hacked to detect the various state changes. Eric had found a cable with the appropriate connector on one end and a USB A plug on the other. It turns out that the Minolta DiMAGE S-304/404/414 happens to use the same connector for USB as the HP 50g does for its serial connector. We plugged everything together and success!!!
Sort of ....
This is where I should have taken a lesson from my few programming classes. Debugging software takes twice as long as writing it. In the case of firmware, I think an order of magnitude needs to be added to that rule. It was soon realized that although basic functionality was easily realized, preventing garbled or dropped characters while powering down the microcontroller to conserve power was a much more difficult challenge.
Although, we had always known that the powerlines from the calculator remained on even when the calculator was turned off, it was assumed that we could simply sleep the microcontroller and use an interrupt to start things up again. This view unfortunately nievely turned a blind eye towards the timing constraints that such an approach inherently would have.
By its very nature, trying to power up a microcontroller which then starts the charge pumps on the level shifter all in order to catch the first transition of a bit stream coming from the calculator is a race condition that should not be winable. With at least one order of magnitude clock advantage and a start bit that happens to be positive and thus can simply be Vcc (as opposed to V- which has no acceptable TTL value since GND is undefined for RS-232*) it is possible to catch the first start bit at 115200 baud. Doing so required tweaking the firmware to insure that the timing constrained code paths take as little time as possible.
There is one area where a compromise of functionality had to be made. A special state occurs when closing IO on the calculator after having used the serial port. The receive line is pulled low but unlike when the calculator is turned off, the transmit line is driven high. In order to detect when the calculator leaves this state, a pull-up resistor is enabled on the receive line of the calculator. The problem here occurs due to the transmission standard common to RS-232*, specifically 8N1. The calculator will often read 255 which happens to correspond to ÿ in the ASCII character chart. This is because the line starts low as the calculator comes out of sleep. The pull-up resistor will then pull the line high and once the MAX3221 in enabled, the steady state of the line is TTL high so it remains high. This all cooresponds to the calculator receiving 0111111111. The first 0 is the start bit and the last 1 is the stop bit. Depending on the speed that the calculator is set to, different numbers of 0s or 1s may be read but regardless a false character appears. This little glitch proved especially annoying because the cause seemed unavoidable.
As a consequence, our solution is less than ideal. Basically, we attempt to timeout the stop bit. The first 0 and then 1 are unavoidable based on our current view of the problem. Once the 0 to 1 transition is detected by the microcontroller, the state change is known. To prevent the false character, the receive line is immediately driven low by the microcontroller and remains low for 8ms which is just enough time to deprive the stream of a stop bit at 1200 baud. Unfortunately, 8ms is a substantial amount of time at 115200 baud. that is simply a design tradeoff that I made. Theoretically, a quick send and response sequence after closing IO may cause the first couple bytes to be missed. It is important to note that this only happens after opening IO and then closing IO. It does not occur after simply turning the calculator on. It is our understanding that few people use the close IO command and so this may well remain a theoretical problem. The work around for this problem is quite simple. Either do not close IO, or use the open IO command and then wait 10ms before transmitting.
Another issue that some people encountered involved the autopowerdown feature that comes with the MAX3221. From a design perspective, it is difficult to see why anyone wouldn't want it. When not presented with a valid RS-232* value on the receive line, a MAX3221 powers down by default. On the surface, this makes complete sense. If nothing is plugged in, why run the charge pumps. Turning off the charge pumps saves power and at seemingly no loss. Two important applications have proven this assumption incorrect. Some serial connectors on multimeters, in order to obtain full electrical isolation, steal power off the RS-232* signals. Thus, they can not present valid signal levels until after the charge pumps start on our side to provide the signals from which to steal power. A variation on this problem is attempting to get two calculators to communicate with each other through a null modem cable. Again, neither side will power up until the other side does. Then there are the hobbyists who for whatever reason wish to do a single direction transmit where there is no receive line connected. To address these issues, we are introducing a cable which will function (remain on) even when the RS-232*/DE-9 side is unplugged (i.e. when no valid RS-232* is present). This feature is standard on all new cables and is activated by flipping a switch inside the cable housing. This switch is not intended to be constantly toggled and should be left on or off. (Another board revision was required in order to add pads for the switch and connect the necessary control lines in order to force on the MAX3221.)
(05/16/2008) Recently, we decided to change the default state that the cables ship so that the autopowerdown feature is disabled. This was done by inverting the polarity of the switch in the firmware and then depopulating the switch. If that many negatives confuses you, email me and I'll try to clarify it. The real result that people will probably care about though, is now you can connect two HP 50g's to each other with a null modem adapter.
If you have made it this far, I congratulate you. I hope our experience helps anyone else who is attempting to build a similar interface. Our Bill Of Materials is as follows:
There are at least three other independent designs out there of which I am aware.
And of course the product of the above discussion which I will term the Allen and Eric cable (although Eric may disagree with the order :-) for only $20 shipped! (within the U.S.)
I'd like to thank Scott Thompson and Tim Wessman for their help with testing and debugging our design.
NOTE: The use of the term "RS-232", in this article and in general when used by Eric and myself when describing this cable refers to RS-232 compatible voltage signaling levels vs. TTL/CMOS signaling levels. The other signal lines are NOT implemented and only the Rx, Tx and GND lines are connected. Furthormore, we do not provide true RS-232 voltage value of +/-12v but instead drive to RS-232 compatible values of about +/-5v. This is pretty much industry standard regarding the term RS-232. If you have any questions regarding compatibility, feel free to email me directly.