esp-idf-hal icon indicating copy to clipboard operation
esp-idf-hal copied to clipboard

`FreeRtos::delay_us` will block for less than the given time for fractional milliseconds

Open tecywiz121 opened this issue 3 years ago • 5 comments
trafficstars

embedded_hal states that delay_us:

Pauses execution for at minimum us microseconds. Pause can be longer if the implementation requires it due to precision/timing issues.

The following line always rounds down, resulting in a much shorter delay:

https://github.com/esp-rs/esp-idf-hal/blob/d446ec9a3979ff1968356136e164afb04109484a/src/delay.rs#L129

I see two solutions, either: using esp_timer_get_time and spinning after vTaskDelay, or doing integer ceiling division instead.


Ran into this trying to bitbang a 1-Wire interface, and still can't seem to get it right. Any suggestions would be helpful!

tecywiz121 avatar Apr 15 '22 18:04 tecywiz121

It is simply not possible to block using FreeRtos APIs for less than one tick (one tick = 10ms by default; can be lowered to 1ms, but not less).

So this API should either: a) Panic b) Don't block for smaller ticks at the price of deviating from the embedded-hal API (status quo) c) Not be implemented for the FreeRtos struct.

If you would like to block for such small intervals, why don't you use the other blocking API (Ets)?

ivmarkov avatar Apr 16 '22 11:04 ivmarkov

It is simply not possible to block using FreeRtos APIs for less than one tick (one tick = 10ms by default; can be lowered to 1ms, but not less).

Yep, I totally understand that limitation. What I was suggesting was using vTaskDelay to block for the longest time possible less than us, then using esp_timer_get_time to delay for the remaining time. Something like:

let end = us + unsafe { esp_timer_get_time() };
let ticks = us / (portTICK_PERIOD_MS * 1000);

if ticks > 0 {
    vTaskDelay(ticks);
}

while unsafe { esp_timer_get_time() } < end {
    // NOP
}

(this doesn't account for overflow, so probably needs to be polished a bit)


If the above isn't acceptable, I think the best option would be a new option (d):

let tick_us = portTICK_PERIOD_MS * 1000;
let ticks = (us + tick_us - 1) / tick_us;
vTaskDelay(ticks);

This implementation is allowed according to embedded-hal's documentation: Pause can be longer if the implementation requires it due to precision/timing issues.

I don't think you need to panic or not implement the function.

tecywiz121 avatar Apr 16 '22 23:04 tecywiz121

Are we fixing the non-compliance with embedded-hal, or are we trying to solve your one-wire problem?

Ran into this trying to bitbang a 1-Wire interface, and still can't seem to get it right. Any suggestions would be helpful!

If you would like to block for such small intervals, why don't you use the other blocking API (Ets)?

Again - would that not work for you?

ivmarkov avatar Apr 17 '22 13:04 ivmarkov

Are we fixing the non-compliance with embedded-hal, or are we trying to solve your one-wire problem?

This bug is about compliance with embedded-hal. I'm going to try the Ets API today!

tecywiz121 avatar Apr 17 '22 15:04 tecywiz121

Don't get me wrong - let's see if you can address your concrete problem by using Ets first. As for fixing the FreeRtos us delay.. none of the suggested methods sounds perfect - right, hence I'm a bit hesitant to address it immediately:

  • Doing a combo with esp_timer_get_time sounds complicated
  • Rounding up e.g. 3us to 10ms - as much as the embedded-hal docu is allowing this - might also be very surprising for the developer

ivmarkov avatar Apr 17 '22 18:04 ivmarkov