IEEE1284 interface for DIY electronics
Background
Ever since desktop computers ceased to provide parallel printer ports, DIY hardware hackers have been at a disadvantage. The old-school parallel port was the quickest and easiest way to get direct control of some 5-volt digital logic lines out of the computer, which could be directly connected to homebrew electronic circuits without needing a microcontroller or other complex storebought interface equipment. You could, for example, blink an LED with software, just by hooking it directly to the parallel port (with a resistor of course).
Those days are long gone. Everything is now USB (or HDMI), and to get anywhere, you'll need a microcontroller to speak the USB protocol. That's fine if you're building a relatively complex system which needs a microcontroller anyway. But what if you just want to "bit-bang" a few wires? Well, you can buy these USB "printer cables", usually for around $20US or less, which contain a dedicated USB microcontroller: they convert USB to a D-25 connector which can drive an old-school parallel printer. But can you "bit bang" with these? No, not directly. You have to do some handshaking to get data to flow. At least a small amount of "glue logic" is required. Hence, this blog entry.
The USB printer cables invariably speak IEEE1284 protocol. In the old days, printer drivers would access the low-level control registers of the parallel port, and would drive the signal wires so as to implement IEEE1284 handshaking. But DIY hackers did not have to use this protocol, they could instead put any logic level on any wire whenever desired, thus implementing a custom protocol -- or no protocol at all. That option is gone; the USB adaptor cables do not implement anything corresponding to the low-level control registers of the old parallel port (those registers pre-dated the IBM PC itself, FYI, coming originally from the Intel 8255 chip). In some ways, this is perhaps a good thing. The circuits which depended on low-level port manipulation thereby became dependent specifically on the x86 PC architecture; other computers with D-25 printer ports such as various Unix systems, etc., would not work. Nowadays, since it is necessary to actually implement IEEE1284, at least devices should end up being more portable. Instead of low-level writes and reads to x86 I/O registers, all software will access the port through a normal Unix device file, such as /dev/usb/lpt1.
About the design
This is an implementation of the simplest IEEE1284 mode, called Compatibility Mode.
At a minimum, according to my experimentation, it seems to be necessary to latch incoming data on the falling edge of nSTROBE (pin 1), wait for nSTROBE to go H, and then to briefly pulse nACK (pin 10) in response. It doesn't seem to work to tie nACK to L, or to tie it directly to nSTROBE: it insists on having some action (transitions) later in time, after nSTROBE is done; hence logic circuitry is required. The 74hc00 circuit produces a fixed-width pulse on nACK, a fixed delay after the nSTROBE pulse goes H. Aka "auto-answer". I don't have my scope nearby, so I guessed at the R-C values.
I saw someone else speak of ignoring nACK and pulsing BUSY instead, but I found the nACK handshaking to work fine with modern Linux, and I think that's how the protocol is supposed to work. I keep BUSY and the several other status lines tied to their non-asserted or "ready to go" values. Apparently in the past, some systems or software ignored nACK, i.e., sending bytes without blocking -- which would be great! -- but the system would still respect BUSY if asserted. As mentioned, back in the day, which wires were respected or ignored was entirely arbitrary and software-dependent. Now, I hope and assume that if we stick to actual IEEE1284, it'll work with any of these USB cables.
I didn't test whether nACK can overlap with nSTROBE: i.e., could I just delay the end of the nSTROBE pulse, with a single R-C? I didn't take a chance. There are enough gates in the 74hc00, so I delay both the start and end of nACK, such that the whole thing happens after nSTROBE, no overlap. All I can really say is, so far it seems to work reliably.
I haven't tested what the max data rate is. For my purposes, this interface makes the port into a transparent set of data lines that can now be fed a given timed waveform, such as for driving stepper motors, matrix-scanning keyboards, etc., and this hardware can be driven directly from Linux software, without an intervening MCU. With significant effort, I've finally recovered the real functionality that was lost when they took away the parallel port! ("Universal", my -- grumble, grumble...)
Bidirectional? Not yet.
I haven't tried to apply this to the higher IEEE1284 modes, i.e., bidirectional data. Back in the day, I used unidirectional data but then used some of the "status" lines for a back-channel. I don't yet know how to read those signals, in this new IEEE1284 paradigm, or if they're even implemented as such. Probably for bidirectional data, one of the higher modes (ECP/EPP) would make more sense (8 bits of back-channel, as opposed to 5 at best); and I expect that all of the USB adaptor cables implement all of the modes, unlike back in the day, so no reason to avoid the higher modes for compatibility concerns. Anyway, for now, I only need the "forward" data path. Stay tuned, later on when I (predictably) end up having to also develop the back-channel, I'll share that info here.
The CNC machine uses three A4988 driver boards to control the X, Y, and Z stepper motors. Each A4988 board takes two logic inputs, step and direction. So overall, the machine can be `controlled by six digital logic lines, such as bits 0-5 of a data byte. (Initially, the machine has no position encoders, limit switches, or other forms of feedback. The software assumes a known starting position, then keeps track of motor steps, assuming no slippage or skipped steps. This is the simplest form of CNC. I hope it works for this -- it's always worth a try!)
Usage
The USB adaptor cables themselves should be plug-n-play with modern operating systems. (If not, I can't help you here!) E.g., on my Linux system, I plugged a freshly-purchased cable in, saw a buncha stuff on /var/log/messages and/or syslog, and then I could see a newly-created device special file, /dev/usb/lpt1.
I will write a custom C program to control my CNC machine, but the actual communication over the USB "parallel port" becomes very simple, just normal writes to the device file. This can also be done from the shell, for testing. E.g., I have an LED on bit 7 of my interface, just for testing connectivity. Here, as root, is how I can toggle the LED:
echo -n -e '\x80' > /dev/usb/lpt1 # LED on
echo -n -e '\x00' > /dev/usb/lpt1 # LED off
(Make sure the target exists and is a device special file, otherwise you'll just create a text file containing the given character. Or so I heard.)
Between that, and the fact that "sleep" in the Linux shell can now take a floating-point value (i.e., it can be used for fraction-of-second delays, like usleep() is used in a Linux C program: e.g., "sleep 0.00125" -- did you know that?), you can pretty much directly control stepper motors and such, from a shell script. Of course timing waveforms done this way, are subject to general multitasking jitter.

Comments
Post a Comment