rambo/TinyWire

tws_delayMicroseconds?

mattgilbertnet opened this issue · 5 comments

This is really a feature request more than an "issue".

This is surely a low priority, if it's even a concern for anyone other than me, but is there any chance of a tws_delayMicroseconds() being implemented?

On 2013.12.17 01:23 , mattgilbertnet wrote:

This is really a feature request more than an "issue".

This is surely a low priority, if it's even a concern for anyone other than me, but is there any chance of a tws_delayMicroseconds() being implemented?

delaymicroseconds is a bit tricky to implement, especially if you want
it to be accurate see:
https://github.com/rambo/arduino-tiny/blob/master/hardware/tiny/cores/tiny/wiring.c#L155

Doing function calls like TinyWireS_stop_check() there will completely
mess up the delicate timing (as will any callbacks the stop_check may or
may not trigger...)

You could do a naive implementation that looks something like this:

tws_delayMicroseconds_inaccurate(uint16_t usec)
{
uint16_t start = (uint16_t)micros();
while ((uint16_t)((uint16_t)micros() - start) < usec)
{
TinyWireS_stop_check();
}
}

but it will be wildly inaccurate, the overhead per iteration is going to
be many cycles, which means several microseconds even if there is no
STOP condition, if there is a STOP then your timing will be totally
blown out of the water since the function call to the callback alone
will mess this up, and whatever the callback spends time on is added as
well.

So if you need microsecond timing you're better off disabling interrupts
and using delayMicroseconds() and perhaps using a separate io-line to
tell the master that you're busy.

/Rambo

Thanks! This looks pretty straightforward, and since precise timing is less important for my application than having some range of timing on the scale of microseconds, I think this will do the trick.

jockm commented

This is just a nit, but wouldn't the naive loop be better written precalculating the end time?:

tws_delayMicroseconds_inaccurate(uint16_t usec)
{
    uint16_t stopMicros = (uint16_t)micros() + usec;
    while (((uint16_t)micros()) < stopMicros)
    {
        TinyWireS_stop_check();
    }
}

@jockm: Please don't use "(((uint16_t)micros()) < stopMicros)", because that leads to occasional difficult-to-debug glitches when TinyWireS_stop_check() takes more than 1 microsecond. For example, if we try to delay usec = 9 microseconds, and the first micros() call gives us 0xFFF6, this code sets "stopMicros" to 0xFFFF, and TinyWireS_stop_check() plus the loop overhead takes 2 microseconds, then this precalculating approach ends up stuck in the loop for over 65,000 microseconds (possibly forever), which is not what we wanted. The "((uint16_t)((uint16_t)micros() - start) < usec)" properly handles rollover even with TinyWireS_stop_check(); taking anywhere from practically 0 to nearly 16,000 microseconds. See https://arduino.stackexchange.com/questions/12587/how-can-i-handle-the-millis-rollover and https://stackoverflow.com/questions/61443/rollover-safe-timer-tick-comparisons and https://www.baldengineer.com/arduino-how-do-you-reset-millis.html for more details.

jockm commented

@carycode Fair point, but you should note that I was just copying rambo's style from above. This comment is better addressed there