rambo / TinyWire

My modifications to TinyWire Arduino libs
284 stars 121 forks source link

tws_delayMicroseconds? #8

Closed mattgilbertnet closed 10 years ago

mattgilbertnet commented 10 years ago

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?

rambo commented 10 years ago

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

mattgilbertnet commented 10 years ago

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 8 years ago

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();
    }
}
carycode commented 7 years ago

@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 6 years ago

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