Re: Microsecond resolution timers - final word

Christer Weinigel (wingel@cendio.se)
Tue, 27 Jul 1999 14:28:14 +0200


Robert Norris wrote:
> No. It's not parallel port based (hell, it's not even pc based). Its a custom
> bit of hardware that uses three lines on the parallel port, and communicates
> using a simple serial-esque protocol. It transfers bytes bit-by-bit, using
> an ACK and DATA line.
>
> Here's an example. I send a bit on the data line, and the device responds
> by bringing the ACK line high .. I then have to bring ACK low and send the
> next data bit within 20-40 usecs.
>
> Is gettimeofday accurate enough for this?

If you have to handle this kind of timing my guess is that you ahve to
to do a busy wait with the interrupts disabled.

To perform a _short_ busy wait, use the udelay() function, it will sit
in a tight loop for the specified number of microseconds.

It probably would be possible to use gettimeofday to wait for a short
while but it would be overkill and on a slow computer such as an i486
it would take longer than 20 microseconds anyway.

[BTW, internally the udelay function is just a tight loop doing:

int loops = usecs * loops_per_usec;
while (loops--)
;

this is what the BogoMIPS calibration at bootup is for, it figures out
what value loops_per_usec should have.]

A code fragment to send one byte of data might look like this:

send_byte(unsigned char data)
{
int i;
int loops;
long flags;

/* save the flags and clear the interrupts, interrupt processing
would break the tight timing requirements of this loop */
save_flags(flags);
cli();

for (i = 0; i < 8; i++)
{
/* Wait for ACK */
while (!(inb(STATUS_PORT) & ACK))
;

/* Wait at least 20 microseconds */
udelay(20);

/* output the data and respond to the ACK */
outb(data & 1, DATA_PORT)
outb(RESPONSE, CONTROL_PORT);

data = data >> 1;
}

restore_flags();
}

There are a few things to be aware of though:

First of all, busy waiting with the interrupts disabled should not be
done for too long, it might cause other things relying on fast
interrupt response to break. For example a serial port receiving data
at 115200 bps will get receiver overruns and drop data when the
interrupts are disabled for more than one millisecond.

Never _ever_ wait longer than one jiffy (1/100 seconds on ix86, 1/1000
on an Alpha) because then you'll start losing timer interrupts and the
system clock will slow down.

Also, the above code ought to have some kind of counter to abort the
transfer if a timeout occurs, since if an ACK is never seen, the
machine would hang forever. (Remember that since the interrupts are
disabled the jiffies variable won't be advanced and can't be used to
determine how long has passed.)

The inner loop waiting for the ACK could be changed to this:

timeout = 1000; /* abort if no ACK seen for a 1000 loops
each loop takes at least 1 microsecond */

while (!(inb(STATUS_PORT) & ACK))
{
if (--timeout <= 0)
goto abort;
udelay(1);
}

Hope this helps,
Christer

-- 
Christer Weinigel		Cendio Systems AB
Email:	wingel@cendio.se	Teknikringen 8
Phone:	+46-13-21 46 00		583 30  Linköping
Fax:	+46-13-21 47 00		Sweden

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/