3

I've got an experimental box of tricks running that, every 100 ms or so, will spit out a 4 microsecond long +5V pulse of electricity on a TTL line. The exact time that this happens is not known ahead of time, but it's important -- so I'd like to use the Red Hat 5.3 computer that essentially runs the experiment to service this TTL, and create a glorified timestamp.

At the moment, what I've done is wired the TTL into pin 13 of the parallel port (STATUS_SELECT, one of the input lines on a parallel port) on the linux box, spawn a process when the experiment starts, use chrt to change its scheduled priority to 99 -- i.e. high -- and then just poll the parallel port repeatedly in a while loop until the pin goes high. I then create an accurate timestamp, and, in a non-blocking way write it to disk.

Obviously, this is inefficient -- sometimes the process is suspended, and a TTL will be missed. As the computer is, itself, busy doing other things (namely acquiring data from my experimental bit of kit -- an MRI scanner!) this happens quite often. Polling is easy, but probably bad.

My question is this: doing something quickly when a TTL occurs seems like the bread-and-butter of computing, but, as far as I can tell, it's only possible to deal with interrupts on linux if you're a kernel module. The parallel port can generate interrupts, and libraries like paraport let you build kernel modules relatively quickly, where you have to supply your own handler.

Is the best way to deal with this problem and create accurate (±25 ms) timestamps for an experiment whenever that TTL comes in -- to write a kernel module that provides a list of recent interrupts to somewhere in /proc, and then read them out with a normal process later? Is that approach not going to work, and be very CPU inefficient -- or open a bag of worms to do with interrupt priority I'm not aware of?

Most importantly, this seems like it should be a solved problem -- is it, and if so do any wise people wish to point me in the right direction? Writing a kernel module seems like, frankly, a lot of hard, risky work for something that feels as if it should perhaps be simple.

Landak
  • 904
  • 14
  • 26
  • From my perspective dealing with pulses of 4us, you're likely below what the parallel port is capable of, polling aside. Since your rate is 100ms between, I can imagine some very simple external hardware that interfaces with one of the serial ports USB, ... The PC and periodically send the current time to synchronize the HW with it. – kenny Jul 11 '14 at 23:18
  • I feared that might be the case. Can you recommend any off-the-shelf hardware that works well with linux? All I care about is the rising edge! – Landak Jul 11 '14 at 23:24
  • 1
    A small microcontroller, like an Atmel ATmega32U4, would work nicely. It's available in a number of the Arduino boards. –  Jul 11 '14 at 23:54
  • 2
    Or even just a simple RS flip-flop, to hold the TTL level high for a few more us, until your polling routine gets around to polling for it, then just use another pin on the parallel port to reset the flip-flop after whatever the polling routine does is finished doing whatever it's doing. – mti2935 Jul 12 '14 at 00:04
  • 1
    Not off-the-shelf, but @mti2935 flip-flop would work, but I would probably use a USB Serial port's CTS (trigger occurred) and RTS (clear the trigger). Or just a micro as duskwuff suggests, but I would chose a PIC...cause that's how I roll ;) – kenny Jul 12 '14 at 02:20
  • 2
    You probably want to ask this on [electronics.se] – Jim Garrison Jul 12 '14 at 05:21
  • 1
    @Landak: I use [Teensies](http://pjrc.com/teensy/) for similar stuff. It has native USB support, so you send HID events (including say a 64-bit cycle counter as a timestamp) when a pulse is detected. No drivers needed, and you can handle the events in a simple userspace program. All with open tools, and you can use GCC-AVR to program it in C. I'm not affiliated in any way at all to PJRC, but I do love these boards. – Nominal Animal Jul 12 '14 at 12:34
  • 1
    Offloading the data acquisition to a dedicated board or microcontroller just for the purpose of capturing this one input, as some of the other commenters have suggested, would certainly work. But, if you want to stick with the parallel port to capture this input, you just need a simple memory device to act as a buffer by going high when it receives the pulse, then you clear it after your routine reads it. This can be done with a simple 74LS279 TTL SR latch for a few bucks, and you can just use one of the output pins on the parallel port (e.g. DO-D7) to clear it. – mti2935 Jul 13 '14 at 13:38
  • @mti2935 That's exactly what I did, in the end, albeit with an 4013B rather than the 74LS279 (it's what I had in a draw). Thank you all for your variety of interesting ideas! – Landak Jul 15 '14 at 22:32
  • Even simpler, using a D flip flop. – mti2935 Jul 16 '14 at 00:42

1 Answers1

2

The premise that "it's only possible to deal with interrupts on linux if you're a kernel module" dismisses some fairly common and effective strategies.

The simple course of action for responding to interrupts in userspace (especially infrequent ones) is to have a driver which created a kernel device (or in some cases sysfs node) where either a read() or perhaps a custom ioctl() from userspace will block until the interrupt occurs. You'd have to check if the default parallel port driver supports this, but it's extremely common with the GPIO drivers on embedded-type boards, and the basic scheme could be borrowed into the parallel port - provided that the hardware supports true interrupts.

If very precise timing is the goal, you might do better to customize the kernel module to record the timestamp there, and implement a mechanism where a read() from userspace blocks until the interrupt occurs, and then obtains the kernel's already recorded timestamp as the read data - thus avoiding the variable latency of waking userspace and calling back into the kernel to get the time.

You might also look at true local-bus serial ports (if present) as an alternate-interrupt capable interface in cases where the available parallel port is some partial or indirect implementation which doesn't support them.

In situations where your only available interface is something indirect and high latency such as USB, or where you want a lot of host- and operation-system- independence, then it may indeed make sense to use an external microcontroller. In that case, you would probably try to set the micro's clock from the host system, and then have it give you timestamp messages every time it sees an event. If your experiment only needs the timestamps to be relative to each other within a given experimental session, this should work well. But if you need to establish an absolute time synchronization across the USB latency, you may have to do some careful roundtrip measurement and then estimation of the latency in order to compensate it (see NTP for an extreme example).

Chris Stratton
  • 39,853
  • 6
  • 84
  • 117