Parallel port hacking with FreeBSD

Nothing quite beats the PC parallel port for electronics tinkering. FreeBSD has a user-space interface to it just for this purpose, "man ppi" has the details.

Accessing the port using /dev/ppi0 is straightforward. Do this as root:

# chmod a+rw /dev/ppi0

then this code lets you read and write to the port:

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/dev/ppbus/ppi.h>
#include <sys/dev/ppbus/ppbconf.h>

static int ppi_fd;

static void do_init(void)
{
  char port[] = "/dev/ppi0";

  ppi_fd = open(port, O_RDWR);
  if( ppi_fd < 0 ) {
    perror(port);
    exit(1);
  }
}

static void do_out(unsigned long outval)
{
  int val = outval, n;

  n = ioctl(ppi_fd, PPISDATA, &val);
  if( n < 0 ) {
    perror("ioctl PPISDATA");
    exit(1);
  }
}

static unsigned long do_in(void)
{
  int val, n;

  n = ioctl(ppi_fd, PPIGSTATUS, &val);
  if( n < 0 ) {
    perror("ioctl PPIGSTATUS");
    exit(1);
  }
  return val;
}

So much for talking on the parallel port. I had some trouble with noise on the port, and the connector (25 pin D-sub) is neither cheap nor convenient, so I made a small PCB to buffer a few signals (6 output, 2 input) and put them on a couple of 10-pin IDC headers so I could use ribbon cable to hook things up. The board plugs straight into the socket on the back of the PC, and takes its power (3.3v - 5v) from the target circuit. Here's the schematic and board for Cadsoft's Eagle.

Parts list

Connector Pin Function ppi bit
SV1 1 out 0
2 GND -
3 out 1
4 GND -
5 out 2
6 GND -
7 in 5
8 GND -
9 VCC -
10 GND -
SV2 1 out 4
2 GND -
3 out 5
4 GND -
5 out 6
6 GND -
7 in 4
8 GND -
9 VCC -
10 GND -

So to set pin 5 on SV1, something like do_out(1 << 2) does the trick. And to read pin 7 on SV1, (1 & (do_in() >> 5)) will get it.