ESP32 - gptimer

Stavo guardando come ottenere delle temporizzazioni esatte su ESP32 (esatte anche al μsec) e mi sono messo a studiare la parte relativa a "Timer Group (TIMG)" dopo di ché, seguendo quanto riportato nel reference delle API, nella sezione "General Purpose Timer (GPTimer)" ho provato a buttare giù un semplice programmino che abilita un gptimer in modalità contatore con riavvio automatico che, alla scadenza di un determinato tempo, genera un "interrupt" ...

Sperando che possa essere utile a tutti, riporto, nel post che segue, il codice funzionante ...

... non è altro che un "blink" ma controllato da un gptimer che usa come clock il clock di sistema "GPTIMER_CLK_SRC_APB" (di solito 80 MHz) ed imposta la risoluzione ad 1 MHz (quindi ad 1 μsec); è configurato per contare fino a 500'000 (quindi 1/2 secondo) generare un interrupt e ripartire automaticamente da 0.

Nella ISR che viene richiamata non faccio altro che invertire una variabile e, quindi, accendere o spegnere un LED. :slightly_smiling_face:

Guglielmo

/*
   ESP32 GPTimers:
   https://docs.espressif.com/projects/esp-idf/en/v5.5.2/esp32/api-reference/peripherals/gptimer.html

   Simple demo program - Guglielmo Braguglia, Feb 2026
*/

#include "driver/gptimer.h"

static volatile uint8_t  LEDstatus = 0;

static bool IRAM_ATTR gptimer_isr( gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data ) {
  BaseType_t high_task_awoken = pdFALSE;

  if ( !edata || !timer ) {
    return false;
  }

  LEDstatus = !LEDstatus;
  digitalWrite ( LED_BUILTIN, LEDstatus );

  return ( high_task_awoken );
}

esp_err_t        esperr;

gptimer_handle_t gptimer = NULL;

gptimer_config_t timer_config = {
  .clk_src       = GPTIMER_CLK_SRC_APB,
  .direction     = GPTIMER_COUNT_UP,
  .resolution_hz = 1000000,  // 1MHz, 1 tick=1us
  .flags = {
    .intr_shared = false,
    .allow_pd    = false     // disable power-down
  }
};

gptimer_event_callbacks_t cbs = {
  .on_alarm = gptimer_isr,
};

gptimer_alarm_config_t alarm_config = {
  .alarm_count  = 500000,    // Set the actual alarm period, since the resolution is 1us, 500000 represents 0.5sec
  .reload_count = 0,         // When the alarm event occurs, the timer will automatically reload to 0
  .flags = {
    .auto_reload_on_alarm = true
  }
};

void setup() {
  delay(500);

  pinMode ( LED_BUILTIN, OUTPUT );
  digitalWrite ( LED_BUILTIN, LEDstatus );

  Serial.begin (115200);

  esperr = gptimer_new_timer(&timer_config, &gptimer);
  if ( esperr != ESP_OK ) {
    Serial.printf("ERRORE gptimer_new_timer: %s\n", esp_err_to_name(esperr));
    return;
  }

  esperr = gptimer_set_alarm_action( gptimer, &alarm_config );
  if ( esperr != ESP_OK ) {
    Serial.printf("ERRORE gptimer_set_alarm_action: %s\n", esp_err_to_name(esperr));
    return;
  }

  esperr = gptimer_register_event_callbacks(gptimer, &cbs, NULL);
  if ( esperr != ESP_OK ) {
    Serial.printf("ERRORE gptimer_register_event_callbacks: %s\n", esp_err_to_name(esperr));
    return;
  }

  esperr = gptimer_enable(gptimer);
  if ( esperr != ESP_OK ) {
    Serial.printf("ERRORE gptimer_enable: %s\n", esp_err_to_name(esperr));
    return;
  }

  esperr = gptimer_start(gptimer);
  if ( esperr != ESP_OK ) {
    Serial.printf("ERRORE gptimer_start: %s\n", esp_err_to_name(esperr));
    return;
  }

  Serial.println();
  Serial.println("Setup completato, timer avviato.");
  Serial.println();
}

void loop() {
  // put your main code here, to run repeatedly:
  delay( 10000 );
}

Guglielmo

1 Like

Ovviamente è uno "scheletro di programma" molto semplice da adattare poi per le proprie esigenze reali ... considerate che quantro trovate al link prima indicato è tutto fatto per un compilatore 'C' e che in 'C++', se non lo si adatta, da errori e quindi ... qui avete già il codice corretto che compila in ambiente Arduino (C++).

I gptimers sono molto flessibili e possono essere programmati in vari modi per risolvere problemi di temporizzazioni che si possono incontrare.

Buono studio :slightly_smiling_face:

Guglielmo

Ottimo lavoro, come al solito.

Per semplificare il setup(), si potrebbe sfruttare la macro ESP_ERROR_CHECK() che fa praticamente quello che fai nello sketch.

....
  ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
  ESP_ERROR_CHECK(gptimer_set_alarm_action( gptimer, &alarm_config ));
  ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL));
  ESP_ERROR_CHECK(gptimer_enable(gptimer));
  ESP_ERROR_CHECK(gptimer_start(gptimer));
....
/**
 * Macro which can be used to check the error code,
 * and terminate the program in case the code is not ESP_OK.
 * Prints the error code, error location, and the failed statement to serial output.
 *
 * Disabled if assertions are disabled.
 */
#ifdef NDEBUG
#define ESP_ERROR_CHECK(x) do {                                         \
        esp_err_t err_rc_ = (x);                                        \
        (void) sizeof(err_rc_);                                         \
    } while(0)
#elif defined(CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT)
#define ESP_ERROR_CHECK(x) do {                                         \
        esp_err_t err_rc_ = (x);                                        \
        if (unlikely(err_rc_ != ESP_OK)) {                              \
            abort();                                                    \
        }                                                               \
    } while(0)
#else
#define ESP_ERROR_CHECK(x) do {                                         \
        esp_err_t err_rc_ = (x);                                        \
        if (unlikely(err_rc_ != ESP_OK)) {                              \
            _esp_error_check_failed(err_rc_, __FILE__, __LINE__,        \
                                    __ASSERT_FUNC, #x);                 \
        }                                                               \
    } while(0)
#endif

Si, la conoscevo, ma l'ho evitata apposta :wink: ... con quella perdi il controllo dato che chiama la abort() e manda in errore il sistema facendolo riavviare ... io volevo mostrare un sistema per avere comunque "il controllo" del codice e poter decidere cosa fare ... che poi io in quell'esempio non faccio nulla è vero, ma, in un'applicazione reale, nel IF si potrebbe decidere di fare altre cose ... :roll_eyes:

Guglielmo