Wichtige Info

Die Inhalte, die du hier siehst stelle ich dir ohne Werbeanzeigen und ohne Tracking deiner Daten zur Verfügung. Trotzdem muss ich die Server bezahlen sowie Zeit in Recherche, Umsetzung sowie Mail Support stecken.
Um dies leisten zu können, verlinke ich in einigen Artikeln auf die Plattform Amazon. Alle diese Links nennen sich Afiliate Links. Wenn du dir mit diesem Link etwas kaufst, dann erhalte ich eine kleine Provision. Dies ändert jedoch NICHT den Preis, den du bezahlst!
Falls du mich also unterstützen möchtest, kannst du auf den Link zum Produkt klicken und hilfst mir dabei, dieses Hobby weiter zu betreiben.
Da ich Keine Werbung schalte und keine Spenden sammle, ist dies die einzige Möglichkeit, meine Systeme und mich zu finanzieren. Ich hoffe du kannst das verstehen :)



ESP IDF - Delay und Ticks


Einleitung

Dieser Beitrag ist Bestandteil einer Beitragsserie über das Framework ESP-IDF

Ich komme nativ aus dem Arduino Umfeld und habe bisher entweder C Programme für den "Desktop" gesschrieben, oder in Arduino für Mikrocontroller.
Arduino hat den charmanten Vorteil bereits viele nützliche Funktionen in "sprech Englisch", wie ich es nenne, mitzubringen. Eine dieser Funktionen nennt sich "delay(time)". Mit dieser Funktion können bestimmte Befehle verzögert ausgeführt werden. Eine Funktion die immer mal nützlich sein kann.

Dieser Beitrag zeigt, wie man mit dem Framework ESP-IDF ebenfalls eine Delay Funktion realisieren kann und wie diese funktioniert.

Hauptteil

Zuersteinmal ist es wichtig zu wissen, wie ein Delay funktioniert und dafür muss das Konzept von "Ticks" bekannt sein.

Was bedeutet Tick?

Ein Mikrocontroller weiß von sich aus erst einmal nicht, wie lange eine Sekunde dauert bzw. wenn dieser nicht gerade eine RTC verbaut hat, kann dieser das nicht wissen. Was jeder Prozessor ohne Einschränkungen aber kennt und nutzt ist die eigene Taktung (gemessen in Hz). Betriebssysteme, wie auch FreeRTOS nutzen eine temporär genaue Zählervariable (genannt tick), welche in Hz definiert wird. Ticks bilden für uns damit eine definierte Zeiteinheit innerhalb von Systemen gemessen vom letzten Systemstart. Als Beispiel hat MS Windows 10.000 Ticks pro Sekunde - dies entspricht dann 10MHz, FreeRTOS hat normalerweise 100Hz, also hat dann 1 Sekunde, 100 Ticks, bzw. 1 Tick dauert 10ms. Wie das genauer funktioniert, kannst du dir zum Beispiel hier anschauen.

Wofür werden diese Ticks verwendet?

Nun ist noch zu klären, wofür diese Ticks denn gut sind. Es ist jetzt bereits bekannt, dass wir mit diesen Ticks für uns bestimmen können, wie viel Zeit vergangen ist seit dem Systemstart und damit werden wir am Ende auch unser "delay" nutzen können. Aber Ticks haben in FreeRTOS eine wichtige Aufgabe, denn diese sind dafür verantwortlich, wie oft die Aufgabenplanung stattfindet.

Der Prozessor wird bei jedem Tick unterbrochen und es wird geschaut, ob ein Task angepasst werden muss, also entweder zur Auführung kommt, aus dem Suspended Status kommt oder sonstiges. Task States kannst du hier nachlesen oder demnächst vereinfacht auf diesem Blog. Wichtig ist aber für das folgende: Je höher deine Tick Rate eingestellt ist, desto häufiger wird der Task unterbrochen, um die Aufgabenplanung anzupassen.

Zwischen den jeweiligen Ticks, wird dann eine Aufgabe bearbeitet, welche von FreeRTOS eingeplant wurde. Im Standardfall hat der Task also 10ms Zeit, wobei das auch nicht ganz genau stimmt. Die Tick time kann minimal vom Soll abweichen, aber dazu im Delay Part mehr :)

Tick Rate beim ESP einstellen per ESP-IDF

Wie oben bereits beschrieben, ist normalerweise eine Tick Rate von 100Hz definiert. Es kann jedoch sein, dass kürzere (oder längere) Zeitintervalle benötigt werden, sodass Anwendungen schneller reagieren können oder mehr Zeit für die Ausführung erhalten, je nach deiner Anforderung. In diesem Fall kann die Auflösung verändert werden per ESP-IDF und zwischen 1Hz und 1000Hz angepasst werden. Damit kann die Interrupt Zeit von 1ms (1000 Hz) auf bis zu 1Sekunde angepasst werden. Heißt bei z.B. einer Tick Rate von 1Hz, wird der counter pro Sekunde um 1 erhöht (die Einheit von Hertz ist n/t -> also 1/s -> bzw. 1 Schwingung pro (/) Sekunde, wenn man das physikalisch aufschlüsseln möchte.

Um die Frequenz anzupassen kann per IDF folgender Befehl ausgeführt werden:

start idf.py menuconfig

Mit diesem Befehl, wird das folgende Fenster geöffnet werden. Von diesem Menü aus, muss dann der folgende Pfad geöffnet werden

. > Component Config > FreeRTOS > Kernel > configTICK_RATE_HZ - Nun muss noch der gewünschte Wert eingetragen werden. Bitte beachten: Eine höhere Auflösung (kürzere Tick time), ist nicht zwingend besser!

Wir wird aus dem Tick nun der Delay? - Delay wie unter Arduino

Jetzt sollte dir ungefähr bekannt sein, was es mit den Tick (s) auf sich hat und wofür diese in FreeRTOS benötigt werden. Wir könnten auch damit problemlos arbeiten um Delays zu realisieren, denn wir könnten den Tick Count mit "xTaskGetTickCount" abrufen und dann warten bis dieser n höher ist. Jedoch ist dies weder angenehm umzusetzen. Aus diesem Grund bringt IDF eine nette funlktion mit, welche sich pdMSTOTICKS(ms) nennt. Mit dieser Funktion können wir die Ticks in ms umwandeln und damit uns einen kleinen Alias bauen, um eine Delay funktion zu haben, welche erstens in ms angegeben wird und vor allem Wertstabil arbeitet! - Denn wenn der Tick Count umgestellt wird, stellt sich auch die Zeit eines Ticks um. Wenn wir also Ticks als statischen Wert für delays verwenden würden, dann könnte es zu unschönen Effekten kommen, wenn der Tick Count bei einem Gerät geändert wird.

Folgendes Beispiel zeigt dieses Verhalten auch

#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE

static const char* TAG = "Main";

void app_main(void)
{
    //Set GPIO 2 as Output
    gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT);

    while(1)
    {
        ESP_LOGI(TAG, "Turn on LED");
        gpio_set_level(GPIO_NUM_2, 1);
        vTaskDelay(1000);
        ESP_LOGI(TAG, "Turn off LED");
        gpio_set_level(GPIO_NUM_2, 0);
        vTaskDelay(1000);
    }
}

Je nachdem, wie hoch die Tick Frequenz eingestellt wird, sind die 1000 Ticks mal 1 Sekunde oder auch 10 Sekunden lang. Das System wartet hier immer 1000 Ticks, was für uns in nicht vorhersehbarem Verhalten münden könnte. Ein Umstand, der natürlich zu vermeiden gilt. Die hier werwendete LED an GPIO 2 blinkt dementsprechend, je nach eingestellter Tick Frequenz unterschiedlich schnell.

Um nun einen Delay verwenden zu können, machen wir folgendes:
Wir definieren den folgenden Alias

#define DELAY(ms) vTaskDelay(pdMSTOTICKS(ms))

Wenn wir nun die Funktion DELAY(n) aufrufen, wird ein TaskDelay erstellt mit n Millisekunden. Diese werden automatisch in die benötigten Ticks umgerechnet. Ein Beispiel basierend auf unserem Test sieht folgendermaßen aus:

#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

#define DELAY(ms) vTaskDelay(pdMS_TO_TICKS(ms))
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE

static const char* TAG = "Main";

void app_main(void)
{
    //Set GPIO 2 as Output
    gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT);

    while(1)
    {
        ESP_LOGI(TAG, "Turn on LED");
        gpio_set_level(GPIO_NUM_2, 1);
        DELAY(1000);
        //vTaskDelay(1000);
        ESP_LOGI(TAG, "Turn off LED");
        DELAY(1000);
        gpio_set_level(GPIO_NUM_2, 0);
        //vTaskDelay(1000);
    }
}

In diesem Beispiel blinkt die LED an GPIO 2 nun jede Sekunde.

Abschluss

Ich bin noch recht neu im Feld von ESP IDF, jedoch sind die Funktionen innerhalb der SDK wirklich vielfältig und um ein vielfaches mächtiger, als bei Arduino - Dementsprechend steil ist aber auch die Lernkurve. Ich erfahre derzeit sehr viel sowohl über die Hardware, als auch über die Software hinter ESP, da IDF ein ordentliches Verständis voraussetzt um mit IDF zu arbeiten. Es gibt zum Beispiel auch die Möglichkeit Delays nur für bestimmte Komponenten des Programms (Tasks) zu definieren und währenddessen andere Aufgaben abzuarbeiten. Innerhalb von Arduino konnte man das früher bereits auf dem ESP8266 mithilfe von SMT machen, indem man einen Task erst auf eine spätere Zeit registriert hat (damals z.B. auch hier von mir gemacht worden).

Mit FreeRTOS kann ein delay deutlich simpler abgebildet werden und auch sonst ist die Erstellung von Systemen einfacher. Ich werde versuchen in den nächsten Tagen und Monaten imemr wieder interessante Dinge hier niederzuschreiben, sodass ich auch später ggf. eine Referenz habe, wenn ich etwas vergessen sollte ^^.


Back…