Microcontroller ESP32 – Sketch Timer

esp32-basics-010-beitragsbild

Der Heartbeat-Sketch hat einen entscheidenen Nachteil: Das Funktionieren des Programms und das Ein- und Ausschalten der LED wird über eine massive Zeitschleife (delay(100) bzw. delay(900)) erkauft. Das heißt, dass in einer extrem kurzen Zeit die Aktivität ausgeführt wird und danach die restliche Rechenzeit verbrannt wird. Dies ist sicher für Demozwecke geeignet, aber nicht für einen praktischen Einsatz. Hier kommt eine Variante eines Interrupts – im ESP-Kontext Timer genannt – zum Einsatz, dessen Funktion darin besteht, zu einem definierten Zeitpunkt bzw. Zeitabstand die eine oder überlappend mehrere Aktivitäten auszuführen und dazwischen den Rechner pausieren zu lassen. Diese Pause kann bei nachfolgenden Sketchen für weitere Aktionen genutzt werden. Der Sketch esp32-basics-010-timer soll dieses Prinzip darstellen. Die verwendete Hardware ist mit dem Heartbeat-Sketch identisch.

Ich hatte diesen Sketch schon vor einiger Zeit mal angearbeitet und jetzt festgestellt, dass er nicht mehr funktioniert. Eine Recherche, u.a. auf https://forum.arduino.cc/t/too-many-arguments-to-function-hw-timer-t/1389998, hat hervorgebracht, dass die Timer-Funktion mit dem Arduino-IDE-Boardmanager 3.0.0 geändert wurde. Beim Kompilieren des Codes wird die Fehlermeldung Compilation error: too many arguments to function ‚hw_timer_t* timerBegin(uint32_t)‘ ausgegeben. Die Änderungen im Sketch sind zwar minimal, aber auch signifikant. Ich habe dies weiter unten in den Listings vermerkt.

Das nachfolgende Listing gilt für den derzeit (September 2025) aktuellen Boardmanager ab Version 3.0.0, das nachfolgende kurze Listing zeigt die Setup-Definition, die verwendet werden muss, wenn ein Boardmanager in der Version <3.0.0 verwendet wird. Es besteht hier keine Abwärtskompatibilität.

Funktion des Sketchs

  • Es wird keine harte Portdefinition verwendet, stattdessen erfolgt die Zuweisung des GPIO-Ports über das Makro LED_PIN.
  • Löst der Timer aus, wird der Zähler count um eins erhöht, er hat damit nacheinander eine gerade oder ungerade Zahl (Funktion onTime).
  • Im Setup erfolgen zuerst Standarddefinitionen zu Übertragungsgeschwindigkeit und GPIO-Port, dieser wird als Ausgang genutzt.
  • xxx

Das Listing zeigt den Code für den Sketch esp32-basics-010-timer.ino:

// esp32-basics-010-timer.ino
#define LED_PIN 2                  // erstellt das makro led_pin, dem der wert 2 zugewiesen wird

// timer                           // festlegungen timer
volatile int count;                // integer zaehler count, durch volatile wird der wert vor jedem zugriff aus hauptspeicher gelesen
int totalInterrupts;               // integer wert, zaehlt die anzahl der interrupts
hw_timer_t * myTimer = NULL;       // erzeugt variable my_timer vom typ hw_timer_t

void IRAM_ATTR onTime() {          // definition der funktion ontime(), iram_attr ist eine spezielle anweisung des esp32,
                                   // es wird schneller interner RAM (IRAM) anstelle von flashspeicher genutzt
   count++;                        // timer zaehlt counter um eins hoch
}

void setup() {
   Serial.begin(115200);           // uebertragungsgeschwindigkeit zum esp32 in bit/s
   pinMode(LED_PIN, OUTPUT);       // gpio2 wird als ausgang gesetzt
   digitalWrite(LED_PIN, LOW);     // led definitiv ausschalten

   // 80Mhz: 80000000 / 80 = 1000000 tics / second
   uint64_t alarmLimit = 1500000;  // genau 64 bit breite integerzahl alarmlimit
   myTimer = timerBegin(1000000);  // timer initialisieren               
   timerAttachInterrupt(myTimer, &onTime);    // interrupt konfigurieren, ordnet eine isr (interrupt service routine) zu    
   timerAlarm(myTimer, alarmLimit, true, 0);  // true - timer neu gestartet, 0 - unendliche wiederholungen
}

void loop() {
    if (count > 0) {
       count--;                               // zuruecksetzen counter
       totalInterrupts++;                     // anzahl der interrupts um eins erhoeht
       Serial.print("Total Interrupts: ");    // ausgabe auf dem seriellen monitor der arduino-ide
       Serial.println(totalInterrupts);       // ausgabe auf dem seriellen monitor der arduino-ide
       if ( totalInterrupts%2 == 0) {         // % = modulo-operator
         digitalWrite(LED_PIN, HIGH);         // einschalten, wenn counter gerade ist
       } else {
         digitalWrite(LED_PIN, LOW);          // ausschalten, wenn counter ungerade ist
       }
    }
}

Die Timer-Funktion bis < Boardmanager 3.0.0 ist nicht aufwärtskompatibel, hier dazu das Listing. Die Setup-Routine wird komplett gegen die im Listing für den Boardmanager ab 3.0.0 ersetzt:

void setup() {
   Serial.begin(115200);                // uebertragungsgeschwindigkeit zum esp32 in bit/s
   pinMode(LED_PIN, OUTPUT);            // gpio2 wird als ausgang gesetzt
   digitalWrite(LED_PIN, LOW);          // led definitiv ausschalten

   // 80Mhz: 80000000 / 80 = 1000000 tics / second
   myTimer = timerBegin(0, 80, true);              // timer initialisieren
   timerAttachInterrupt(myTimer, &onTime, true);   // interrupt konfigurieren, ordnet eine isr (interrupt service routine) zu 
   timerAlarmWrite(myTimer, 1000000, true);        // setzt einen alarm zu jeder sekunde          
   timerAlarmEnable(myTimer);
}

Weitere Links zum Thema:

https://wolles-elektronikkiste.de/interrupts-teil-3-timer-interrupts

https://forum.arduino.cc/t/esp32-timer-reloading/1364408

https://arduino.stackexchange.com/questions/96504/timers-interrupt-is-not-working-in-esp32s2-board

https://github.com/espressif/arduino-esp32/blob/3.0.2/libraries/ESP32/examples/Timer/RepeatTimer/RepeatTimer.ino#L42

Schreib einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert