espressif/esp-idf

The gptimer alarm doesn't trigger when set with an old counter (IDFGH-9522)

blaizard opened this issue · 3 comments

Answers checklist.

  • I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
  • I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
  • I have searched the issue tracker for a similar issue and not found a similar issue.

General issue report

The documentation for the gptimer states that: (from https://docs.espressif.com/projects/esp-idf/en/v5.0.1/esp32/api-reference/peripherals/gptimer.html#set-up-alarm-action)

If an alarm value is set and the timer has already exceeded this value, the alarm will be triggered immediately.

However the following code demonstrates that this doesn't seem to be the case:

static bool testCallback(::gptimer_handle_t, const ::gptimer_alarm_event_data_t*, void* userCtx)
{
      std::atomic<int>& counter = *static_cast<std::atomic<int>*>(userCtx);
      ++counter;
      return false;
}

::gptimer_handle_t gptimer_;

::gptimer_config_t config{};
config.clk_src = GPTIMER_CLK_SRC_APB;
config.direction = GPTIMER_COUNT_UP;
config.resolution_hz = 10000; // 0.1ms
config.flags.intr_shared = false;
::gptimer_new_timer(&config, &gptimer_);

::gptimer_event_callbacks_t callback{
    .on_alarm = testCallback
};
std::atomic<int> flag{0};
::gptimer_register_event_callbacks(gptimer_, &callback, &flag);

::gptimer_enable(gptimer_);

::gptimer_start(gptimer_);

::gptimer_alarm_config_t alarm{};
alarm.alarm_count = 10;
::gptimer_set_alarm_action(gptimer_, &alarm);

while (flag.load() == 0);
std::cout << "flag=" << flag.load() << std::endl;

It never prints flag=1. However, if I change alarm.alarm_count = 15000;, a value after the current counter, it works as expected. So it seems that the statement in the documentation is false.

TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_late_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{
    esp_rom_printf("alarm isr count=%llu\r\n", edata->count_value);
    return false;
}

TEST_CASE("gptimer_trig_alarm_with_old_count", "[gptimer]")
{
    gptimer_config_t timer_config = {
        .resolution_hz = 10 * 1000, // 10KHz, 1 tick = 0.1ms
        .clk_src = GPTIMER_CLK_SRC_DEFAULT,
        .direction = GPTIMER_COUNT_UP,
    };
    gptimer_handle_t timer;
    TEST_ESP_OK(gptimer_new_timer(&timer_config, &timer));

    gptimer_event_callbacks_t cbs = {
        .on_alarm = test_gptimer_alarm_late_callback,
    };
    TEST_ESP_OK(gptimer_register_event_callbacks(timer, &cbs, NULL));
    TEST_ESP_OK(gptimer_enable(timer));
    TEST_ESP_OK(gptimer_start(timer));

    vTaskDelay(pdMS_TO_TICKS(1000));

    gptimer_alarm_config_t alarm_config = {
        .reload_count = 0,
        .alarm_count = 10, // 1ms
    };
    TEST_ESP_OK(gptimer_set_alarm_action(timer, &alarm_config));

    vTaskDelay(pdMS_TO_TICKS(100));

    TEST_ESP_OK(gptimer_stop(timer));
    TEST_ESP_OK(gptimer_disable(timer));
    TEST_ESP_OK(gptimer_del_timer(timer));
}

I tried with the above code, I can see the alarm interrupt is triggered.

Running gptimer_trig_alarm_with_old_count...
alarm isr count=9999
MALLOC_CAP_8BIT: Before 300800 bytes free, After 300800 bytes free (delta 0)
MALLOC_CAP_32BIT: Before 382668 bytes free, After 382668 bytes free (delta 0)
/IDF/components/driver/test_apps/gptimer/main/test_gptimer.c:559:gptimer_trig_alarm_with_old_count:PASS
Test ran in 1127ms

Thanks @suda-morris for looking into this. Indeed, your example shows that it works as intended. I just tried your example on my setup but it also doesn't work. However, if I change alarm_count to 11000 (1.1s) it prints alarm isr count=11026. So it is the same behavior as my example.
I am using an esp32, with ESP-IDF 5.0.1 and running my example on qemu. What is your setup?

I just tried on a real target (esp32 devkit) and it works as expected. Seems that the issue is with my qemu setup then. Therefore I am closing this issue. Thanks @suda-morris again for helping me here.