~derf / interblag / entry / Attaching custom hardware to the parallel port
dark mode

First, there's an excellent parallel port howto on epanorama.net.

What I'd like to add: You can't just control LEDs or relay coils / transistors with the parallel port, it's also pretty easy to talk to microcontrollers (an ATTiny 2313 in my case).

interfacing

Basically, you need one or two parallel port pins as inputs to the AVR, and then come up with some sort of protocol.

one-wire

No clock signal, so we rely on proper timing. The most primitive solution is to toggle the pin n times and have the AVR increment a counter on every toggle, then read the counter value after a fixed time without counter increment has passed. Excruciatingly slow, but dead simple to implement both on the computer and the AVR.

two-wire

One pin for data, one for the clock (e.g. set data, then toggle clock, set an AVR interrupt on rising edge on clock to read data pin). I didn't try it yet.

more?

So far, the computer talks to the AVR but not vice versa. Two-way communication shouldn't be hard, but I didn't try it yet. If I do, I may write a new entry.

hardware

To be on the safe side, I decided to completely isolate the parallel port from the rest of the circuit.

components used:

  • KB817 opto-coupler. forward voltage ~1.2V, collector-emitter voltage 35V/6V
  • 3.3K resistor between opto-coupler and parallel port, since the parallel port provides 2.4 to 5V
  • BC338-40 transistor to make sure the opto-coupler output is registered by the microcontroller

The rest is usual stuff. The parallel port circuit is located in the bottom left of the schematic.

software

microcontroller

Assuming the transistor (optocoupler) is connected to INT0

  • set an interrupt on INT0 rising edge: increment counter and reset timeout
  • set a timer interrupt: check timeout, if zero handle counter as command

code snippet:

ISR(INT0_vect)
{
    cli();
    command++;
    cmd_wait = 6;
}

ISR(TIMER1_COMPA_vect)
{
    if (cmd_wait)
        cmd_wait--;
    else if (command) {
        run_command();
        command = 0;
    }
}

the whole file is available on github: main.c

computer

I'm using the parapin library, see pgctl.c on github. Note that timing is important here, so I'm running the code at the lowest possible niceness.

That's it. An example project is available as derf/pgctl on github.

The whole point of this post is: interfacing with the parallel port is easy. It doesn't have a future, but if you still have one, you can use it for quite a lot of things.