Question

I am looking for a simple means of triggering my data acquisition software using an external TTL pulse. I need to sample data from multiple sources synchronously with a 5 Hz reference clock. The acquisition does not need real-time priority but I want to ensure that my software is triggered as soon as possible and exactly once per external clock cycle. I would prefer to do this by somehow getting an interrupt from the external trigger without needing to use a fast polling loop. As far as I can tell, you can't just use a parallel port pin for an interrupt in a modern OS like Linux. Any ideas?

I am also thinking of generating broadcast packets on my network to notify other machines on the network that a trigger event has occurred. Due to network latency however there may not be enough time available in the 200ms period between triggers to do the acquisition.

Was it helpful?

Solution

Rather than use the parallel port, have you considered using a serial device? Since you have a TTL signal, you'll possibly need a level converter to convert TTL to RS232 +/- 12V levels. Once you're using a serial device, you can use the standard serial ioctl() calls to detect a change in control signal status.

Specifically, you could use the TIOCMIWAIT ioctl on the connected serial device to wait for a change on say the DCD line, which you would connect to your clock source.

Your user space application would be blocked waiting in the TIOCMIWAIT ioctl system call until there is a change of status on your clock line, at which point your app would become runnable and return from the ioctl. You might have to take care to ensure that you handle the case where you get a change of status interrupt on both rising and falling edges of your serial control signals. On some UART hardware (eg TL16C554A UART) it's possible that you'll only get an interrupt for a signal transitioning in a single direction. For the TL16C554A for example, the TIOCMIWAIT would only fall through on the rising edge of any Ring Indicate signal change.

Using the serial ioctls in this manner also has the advantage that you could use a USB-Serial dongle that supports TIOCMIWAIT if required (eg PL2303), and still retain user level software compatibility, albeit at the expense of increased latency due to USB.

If you require lower latency than can be achieved through user space, you'd be best to write a kernel driver module which can handle the timing and sampling, but I wouldn't suggest this route unless absolutely needed. It's easier to develop user space code.

Here's some incomplete sample C code snippets for using the TIOCMIWAIT ioctl.

int serial_fd = open(cmdline.device_name, O_RDWR | O_NONBLOCK | O_NOCTTY);
static const unsigned int ri_flag = TIOCM_RNG;

/* Set up serial port here using tcsetattr.  Set CRTSCTS | CLOCAL to ensure status interrupts
 * are generated.
 */

while (1) {
        /* Wait for positive RI transition.  TIOCMIWAIT takes a mask
         * as argument, only returning when the appropriate signal has changed.
         */
        if (ioctl(serial_fd, TIOCMIWAIT, ri_flag)) {
            fprintf(stderr, "ioctl() failed waiting for RI edge [%s]\n", strerror(errno));
            break;
        }

        /* Do sensor sampling here.  You could use TIOCMGET to first verify that
         * the clock line is in the expected state, eg high, before continuing.
         */
}

OTHER TIPS

Polling is a fine method for such a slow data rate. Poll at 1 ms. That should be fine. Trying to use a hardware interrupt is going to cause much pain.

Google for "Interrupt Linux GPIO" if you want to do it the hard way. :)

https://developer.ridgerun.com/wiki/index.php/How_to_use_GPIO_signals

Consider connecting the external pulse source to the 'CD' ping of a real (! not a USB to RS232 converter) serial port. Then you can use the "PPS api" to get an as exact timestamp from which the pin "went high" as possible.

You might need a TTL signal shifter; not all serial ports work correctly with TTL signal levels.

The PPS api is normally used for time keeping. E.g. connect the PPS pin of a GPS module to your pc and let NTP sync with that. This gives you microsecond accuracy.

This PPS api is supposed to be more accurate than any other userspace solution (e.g. the TIOCMIWAIT ioctl) as it is completely handled in the kernel, immediately when the interrupt (as triggered by the CD signal change) comes in. With the ioctl solution you have at least a context switch. I did some testing on a raspberry pi and a userspace solution gives at least 6us jitter.

The PPS api gives you a timestamp from when the pulse was detected.

Why not use a USB to RS232 converter: I read somewhere (in relation to timekeeping) that USB devices are polled once every +/- 1ms. This polling frequency also depends on how busy the system is with other USB devices and I think the internal clock of the USB device may also influence things. I've never measured this though.

Relevant URLS:

Regarding the network functionality: use UDP broadcasts, do not use TCP.

I ended up using the serial port CTS line for the trigger using the TIOCMIWAIT ioctl per Austin Phillips answer. Since RS232 requires +/-12V levels I was able to get the necessary power for this level shifter from the other serial control lines.

Level shifter schematic

The Python code to implement this solution can be found in question: Python monitor serial port (RS-232) handshake signals

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top