ESP32-S2 One Shot Microsecond non-blocking timer(s)

I am struggling with understanding the ESP32-S2 timer interrupt library(s). Every example I see requires the use of millis() but I need an accurate, 100uSec, non-blocking ISR driven timer. This is for display purposes while not interfering significantly with WiFi and RTC. I have tried examples in the IDE library and manipulated those to see how things work. I have looked at the ESP32-S2_timer_interrupt.h file for the extent of the available functions but these do not seem to be very clear about usage to me.

I need a code suggestion that:- On demand (i.e. in loop()) sets up a timer scaler for 1 microsecond granularity (the ESP32-S2 can divide from the 240MHz clock OK), loads a microsecond granularity register, in my case 100 uS, start a count and fire an interrupt when the desired time is reached.

So far I have had a timer ISR fire first time at 100uSec but on the second time the ISR callback never executes. I find the attachInterrupts confusing and my experiments have been unsuccessful. I think this may be because I don't understand the attachInterrupt or attchInterruptInterval plus how to use the restartTimer or stopTimer (I'm generalising as I don't have the library code in front of me). There is a mention of a Duration parameter but it seems that this is not available in the current library

I'm no novice to timer (or hardware) interrupts as I have been using assembler, PLM/80 and C on the 8080/Z80/8051 microcontrollers since 1978, also more modern devices such as the RPi and Pico but I do find the ESP32 timer libraries strange. I note there are significant difference between the Arduino IDE based libraries and the Espressif equivalents.

I have refrained from including my code snippets as these may well serve to confuse.

Thanks in advance, Tony

Then use proper ISR and not the attachInterrupt callbacks.

Curious did you try esp32 High resolution timer lib..
esp32/api-reference/system/esp_time

Took a look at it myself..
Here's an example of creating the timer in setup and activating it in the loop..
Maybe it helps..

#include "esp_timer.h"

#define LED_PIN 2


static void oneshot_timer_callback(void* arg);

esp_timer_handle_t oneshot_timer;

volatile bool timerActive = false;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Ready..");
  pinMode(LED_PIN, OUTPUT);

  //create timer parameters..
  const esp_timer_create_args_t oneshot_timer_args = {
    .callback = &oneshot_timer_callback, //link the call back
    .arg = nullptr, //not passing in anything
    .name = "one-shot" //nice name
  };
  //create timer, not running yet..
  ESP_ERROR_CHECK(esp_timer_create(&oneshot_timer_args, &oneshot_timer));
}

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

  if (!timerActive) {
    //check state of timer and stop it if needed..
    if (esp_timer_is_active(oneshot_timer)) {
      ESP_ERROR_CHECK(esp_timer_stop(oneshot_timer));
    }
    //start our one shot timer for 1 second..
    ESP_ERROR_CHECK(esp_timer_start_once(oneshot_timer, 1000000));
    int64_t time_since_boot = esp_timer_get_time();
    Serial.printf("Restarting On-shot timer with 1s period, time since boot: %lld us\n",
                  time_since_boot);
    timerActive = true;
  }
}



//call back for timer..
static void oneshot_timer_callback(void* arg)
{
  int64_t time_since_boot = esp_timer_get_time();
  Serial.printf("One-shot timer called, time since boot: %lld us\n", time_since_boot);
  digitalWrite(LED_PIN, !digitalRead(LED_PIN));//blink on board led..
  timerActive = false;//say we're done..
}

Simmulated here..

good luck.. ~q

Would RMT work?

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html

Having posted my initial message above, it made me think more clearly about the processes employed by the library. It seems I was overthinking the functions and callbacks. As a result, simplifying my code has made the ISR's work as they should.

@qubits-us , thanks for your code, I will look at that and see if it improves matters.
@sumguy , Thanks for the link, though I havn't yet read the contents. It is useful to gather different sources of info.

Tony

26/09/2023 Edit:

@qubits-us I was not aware of the library you included in your code but having looked at the header file (which was updated a few days ago and has an ESP32-S2 variant) it does use ISR's though only one at a time. Typically the comments in the library source code are not particularly enlightening though I havn't looked at the Github source Readme.md. In the circumstances of my code the library should be no problem. Thanks for sharing!

@sumguy Your link points to an Espressif API wireless operations and therefore not really of much use in an Arduino IDE. In the same page there is the timer ISR's but I was already aware of them. I don't think the API can be integrated into an Ardino IDE environment or be compatible with the Arduino version of the libraries. However thanks anyway since it broadens my knowledge of what the ESP32 is capable of.

26/09/2023 18:15 edit:

Preliminary testing of my modification of @qubits-us example follows!

#include <stdio.h>

#include <Wire.h>
#include "USB.h"

#if !ARDUINO_USB_CDC_ON_BOOT
USBCDC Serial;
#endif

// These define's must be placed at the beginning before #include "TimerInterrupt_Generic.h"
// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
#define TIMER_INTERRUPT_DEBUG         0
#define _TIMERINTERRUPT_LOGLEVEL_     4

#include "esp_timer.h"

#define LED_PIN 9

#define one_shot_duration 100
#define one_shot_compensation 14  // A fiddle factor to correct for time taken by code execution. Adjust to get accurate one shot time.

static void oneshot_timer_callback(void* arg);

esp_timer_handle_t oneshot_timer;

volatile bool timerActive = false;
volatile int64_t time_now, time_since_boot = 0;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  delay(500);
  Serial.println("Ready..");
  pinMode(LED_PIN, OUTPUT);

  //create timer parameters..
  const esp_timer_create_args_t oneshot_timer_args = {
    .callback = &oneshot_timer_callback, //link the call back
    .arg = nullptr, //not passing in anything
    .name = "one-shot" //nice name
  };
  //create timer, not running yet..
  ESP_ERROR_CHECK(esp_timer_create(&oneshot_timer_args, &oneshot_timer));

  digitalWrite(LED_PIN, LOW);//blink on board led..
  delay(500);
  timerActive = false;
  if (!timerActive) {
    //check state of timer and stop it if needed..
    if (esp_timer_is_active(oneshot_timer)) {
      ESP_ERROR_CHECK(esp_timer_stop(oneshot_timer));
    }
    //start our one shot timer for 1 second..
    ESP_ERROR_CHECK(esp_timer_start_once(oneshot_timer, (one_shot_duration - one_shot_compensation)));
    time_since_boot = esp_timer_get_time();
    timerActive = true;
  }

}

void loop() {
  // put your main code here, to run repeatedly:
  while(timerActive == true) {

  }

  Serial.printf("Restarting On-shot timer with 1s period, time since boot: %lld us\n", time_now - time_since_boot);

  while(1);
/*
  if (!timerActive) {
    //check state of timer and stop it if needed..
    if (esp_timer_is_active(oneshot_timer)) {
      ESP_ERROR_CHECK(esp_timer_stop(oneshot_timer));
    }
    //start our one shot timer for 1 second..
    ESP_ERROR_CHECK(esp_timer_start_once(oneshot_timer, 1000000));
    int64_t time_since_boot = esp_timer_get_time();
    Serial.printf("Restarting On-shot timer with 1s period, time since boot: %lld us\n",
                  time_since_boot);
    timerActive = true;
  }
*/
}



//call back for timer..
static void oneshot_timer_callback(void* arg)
{
  time_now = esp_timer_get_time();
//  Serial.printf("One-shot timer called, time since boot: %lld us\n", time_since_boot);
  digitalWrite(LED_PIN, LOW);//blink on board led..
  timerActive = false;//say we're done..
}


And the result is:-

Restarting On-shot timer with 1s period, time since boot: 100 us
Restarting On-shot timer with 1s period, time since boot: 100 us

(I used the reset button to get the second result so it really is one shot) and also I didn't modify the print text from '1 s' to 100uS!

Now I have to work the example into my main code. I'm not sure at this stage which hardware timer is used here and perhaps I should find out. The ESP32-S2 has two groups of two hardware timers so if I need to have multiple timers in use, then knowing which timer is being used would be helpful.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.