MicroPython: Experimente mit Timer

Ein Timer ist eine logische Instanz für einen Zeitgeber, der einen Befehl oder eine Funktion zeitabhängig ausführen kann.

  • einmalig nach Zeit in Millisekunden oder
  • periodisch wiederholend mit Angabe einer Frequenz in Hertz (Anzahl in der Sekunde) oder nach Zeit in Millisekunden

Hier experimentieren wir mit der Timer-Funktion von MicroPython. Probieren verschiedene Parameter bei der Initialisierung aus und versuchen herauszufinden, wofür Timer nützlich sind.

Experimente zum Unterschied zwischen „time.sleep“ und Timer

In einem Programmcode werden wiederholende Kommandos gerne in eine Endlos-Schleife gesetzt und mit dem Kommando „time.sleep()“ oder „time.sleep_ms()“ eine Verzögerung oder Pause hinzugefügt, um eine Zeitabhängigkeit hinzuzufügen.
Den Unterschied zwischen einer Schleife mit „time.sleep“ und einem Hardware-Timer wollen wir mit folgender Aufgabe ergründen. Wir wollen die Millisekunden zählen und ausgeben, die ein Programm läuft. Dafür brauchen wir einen Zähler, eine Datenausgabe und eine wiederholende Ausführung.

Experiment 1: Endlos-Schleife OHNE Berechnung

Eine typische Lösung ist, eine Endlos-Schleife laufen zu lassen und eine Verzögerung einzubauen. Das folgende Programm überprüft, wie genau die time.sleep_ms()-Funktion arbeitet, also ob die tatsächliche Zeitmessung (t) mit dem erwarteten Zählwert (count) übereinstimmt.

# Bibliotheken laden
import time

# Interval in ms
interval = 1000

# Startwerte
t = 0
count = 0
start = time.ticks_ms()

# Endlos-Schleife
while True:
    time.sleep_ms(interval)
    count += interval             # Zeit in ms zählen
    t = time.ticks_ms() - start   # Zeit in ms messen
    print('Zählen:', count, 'ms', '|', 'Messen:', t, 'ms')

Das Ergebnis wird sein, dass die Werte vom Zeitzähler und vom Zeitmesser über eine längere Zeit übereinstimmen. Das ist deshalb der Fall, weil in der Schleife nur gezählt und gemessen wird, aber sonst keine Funktion hat.

Experiment 2: Endlos-Schleife MIT Berechnung

Was aber, wenn in der Schleife eine Berechnung erfolgt, die länger als 1000 Millisekunden (= 1 Sekunde) dauert?

Das Programm misst fortlaufend die vergangene Zeit in Millisekunden, indem es in einer Endlosschleife jede Sekunde eine Pause macht, den Zeitunterschied seit dem Start berechnet und diesen zusammen mit einem eigenen Zähler ausgibt. Zusätzlich führt es in jeder Schleife eine kleine Rechenaufgabe (Summe von Quadraten) aus, um den Prozessor kurz zu beschäftigen.

# Bibliotheken laden
import time

# Interval in ms
interval = 1000

# Startwerte
t = 0
count = 0
start = time.ticks_ms()

# Endlos-Schleife
while True:
    time.sleep_ms(interval)
    count += interval             # Zeit in ms zählen
    t = time.ticks_ms() - start   # Zeit in ms messen
    print('Zählen:', count, 'ms', '|', 'Messen:', t, 'ms')
    sum(i**2 for i in range(500))

In diesem Fall weicht der Zeitzähler vom Zeitmesser ab und über die Zeit wird die Differenz sehr groß.

Zwischenfazit

Zeitkritische Aufgaben sollten nicht in einer Schleife verarbeitet werden, weil:

  • die Schleifenlaufzeiten ungenau sind
  • andere Operationen kleine Verzögerungen verursachen
  • die Systembelastung zusätzliche Schwankungen erzeugt

Dadurch ist die Ausführungszeit nicht exakt vorhersehbar. Für präzise Zeitsteuerung werden daher Hardware-Timer verwendet, die unabhängig vom Hauptprogramm arbeiten und exakte Zeitpunkte einhalten können.

Experiment 3: Timer-Lösung

Der Programmcode misst regelmäßig Zeitintervalle mithilfe eines Hardware-Timers.
Im Gegensatz zur einfachen Schleifenlösung läuft der Timer unabhängig vom Hauptprogramm und sorgt so für genaue, zeitstabile Messungen, auch wenn der Prozessor zwischenzeitlich andere Aufgaben erledigt.

# Bibliotheken laden
import time

# Interval in ms
interval = 1000

# Startwerte
t = 0
count = 0
start = time.ticks_ms()

def counter(value):
    global count, t
    count += interval             # Zeit in ms zählen
    t = time.ticks_ms() - start   # Zeit in ms messen
    print('Zählen:', count, 'ms', '|', 'Messen:', t, 'ms')

timer = machine.Timer(period=interval, mode=machine.Timer.PERIODIC, callback=counter)

# Endlos-Schleife
while True:
    time.sleep_ms(interval)
    sum(i**2 for i in range(500))

Fazit

Funktional sind die alle Experimente völlig identisch. Der Unterschied liegt im Detail. Was die Genauigkeit angeht, wird die Timer-Lösung präziser sein.

Die Timer-Lösung hat noch einen zweiten Vorteil, wenn eine Funktion dem Programmcode hinzugefügt werden soll, dann kann man das ohne große Änderung einfach ergänzen. Und der Zähler bleibt davon unberührt. In der Schleifen-Lösung müsste die Schleife ergänzt oder umgebaut werden.

Wenn eine Funktion zeitabhängig ist und präzise (Echtzeit) sein soll, dann sollte man die Timer-Lösung bevorzugen.
In einer while-Schleife mit sleep-Pausen sind zeitabhängige Funktionen nicht genau genug realisierbar.

Wenn es also wichtig ist, dass eine Funktion oder ein Kommando nach einer bestimmten Zeit ausgeführt werden soll, dann sollte man das mit einem Timer realisieren. Der arbeitet unabhängig vom Hauptprogramm.

Parallele Verarbeitung

Ein Mikrocontroller verarbeitet den Programmcode Zeile für Zeile nacheinander ab. Abweichungen in der Reihenfolge ergeben sich nur bei Verzweigungen, Wiederholungen durch Schleifen und Sprünge beim Aufrufen von Funktionen. Es gibt aber im Rahmen gewisser Grenzen mehrere Möglichkeiten eine quasiparallele Ausführung von Programmcode zu erreichen.

Experiment mit Parameter „mode=Timer.PERIODIC“

Im folgenden Programmcode wird die Onboard-LED initialisiert. Außerdem wird ein periodischer Timer initialisiert, der den aktuellen Wert des GPIO 25 toggelt. Diese Funktion ist im Parameter „callback“ definiert.

from machine import Pin, Timer

led = Pin(25, Pin.OUT, value=0)

timer = Timer(period=1000, mode=Timer.PERIODIC, callback=lambda t:led.toggle())

Der Parameter „mode=Timer.PERIODIC“ führt dazu, dass die Funktion im Parameter „callback“ jede Sekunde (1000 ms) ausgeführt wird.

Experiment mit Parameter „mode=Timer.ONE_SHOT“

Im folgenden Programmcode wird die Onboard-LED initialisiert. Außerdem wird ein einmalig auszuführender Timer initialisiert, der den aktuellen Wert des GPIO 25 toggelt. Diese Funktion ist im Parameter „callback“ definiert.

from machine import Pin, Timer

led = Pin(25, Pin.OUT, value=0)

timer = Timer(period=1000, mode=Timer.ONE_SHOT, callback=lambda t:led.toggle())

Der Parameter „mode=Timer.ONE_SHOT“ führt dazu, dass die Funktion im Parameter „callback“ ein einziges Mal nach einer Sekunde (1000 ms) ausgeführt wird.

Experimente mit Parameter „period“

Im folgenden Programmcode wird die Onboard-LED initialisiert. Außerdem wird ein periodischer Timer initialisiert, der den aktuellen Wert des GPIO 25 negiert. Diese Funktion ist im Parameter „callback“ definiert.

from machine import Pin, Timer

led = Pin(25, Pin.OUT, value=0)

timer = Timer(period=1000, mode=Timer.PERIODIC, callback=lambda t:led.value(not led.value()))

Der Parameter „mode=Timer.PERIODIC“ führt dazu, dass die Funktion im Parameter „callback“ jede Sekunde (1000 ms) ausgeführt wird.
Der Parameter „period“ gibt die Wiederholung in Millisekunden (ms) an. Oder anders ausgedrückt, wie lange es dauert, bis der Timer auslöst. 1000 ms entspricht einer Sekunde.

  1. Erhöhe den Wert von „period“ auf 2000. Die LED blinkt langsamer.
  2. Verringere den Wert von „period“ auf 100. Die LED blinkt deutlich schneller.
  3. Verringere den Wert von „period“ auf 20. Die LED flackert.
  4. Verringere den Wert von „period“ so weit, dass die LED nicht mehr flackert, sondern dauerhaft zu leuchten scheint.

Experimente mit Parameter „freq“

Im folgenden Programmcode wird die Onboard-LED initialisiert. Außerdem wird ein periodischer Timer initialisiert, der den aktuellen Wert des GPIO 25 negiert. Diese Funktion ist im Parameter „callback“ definiert.

from machine import Pin, Timer

led = Pin(25, Pin.OUT, value=0)

timer = Timer(freq=1, mode=Timer.PERIODIC, callback=lambda t:led.value(not led.value()))

Der Parameter „mode=Timer.PERIODIC“ führt dazu, dass die Funktion im Parameter „callback“ ein Mal in der Sekunde ausgeführt wird.
Der Parameter „freq“ gibt die Frequenz der Wiederholung in Hertz (Hz) an. 1 Hz bedeutet eine Wiederholung in der Sekunde. Mit dem Parameter „freq“ kann man also angeben, wie oft in der Sekunde der Timer ausgelöst werden soll.

  1. Verringere den Wert von „freq“ auf 0.5. Die LED blinkt langsamer.
  2. Erhöhe den Wert von „freq“ auf 10. Die LED blinkt deutlich schneller.
  3. Erhöhe den Wert von „freq“ auf 20. Die LED flackert.
  4. Erhöhe den Wert von „freq“ so weit, dass die LED nicht mehr flackert, sondern dauerhaft zu leuchten scheint.

Wann nimmt man den Parameter „period“ und wann „freq“?

Den Parameter „freq“ zu verwenden macht dann Sinn, wenn die Zeiteinheit oder der Bezug zu einer Sekunde gegeben ist. Wenn also etwas innerhalb einer Sekunde X mal erfolgen soll. Dann beträgt die Frequenz X Hz. Das macht also nur dann Sinn, wenn X 1 oder größer ist. Man kann auch mit kleineren Werten als 1 arbeiten. Dann muss man aber mit Gleitkommazahlen rechnen.

Den Parameter „period“ zu verwenden macht dann Sinn, wenn der Wert selber eine Zeit ist. Zum Beispiel Sekunden oder Millisekunden. Wenn also etwas nach einer bestimmten Zeit ausgelöst werden soll.
Mit dem Parameter „period“ zu arbeiten macht auch dann Sinn, wenn der Wert des Parameters „freq“ kleiner als 1 ist.
Der Parameter erwartet eine Zeit in Millisekunden. Wenn der Wert nicht in Millisekunden vorliegt, kann er auch umgerechnet werden.

  • 1 Sekunde = 1000 Millisekunden
  • 0,5 Sekunden = 500 Millisekunden
  • 5 Sekunden = 5000 Millisekunden
  • 60 Sekunden = 60000 Millisekunden

Anwendungen mit Timer

Anwendungen mit einem Timer sind immer dort zu finden, wo etwas wiederholt werden soll. In der Regel abhängig von einer Zeit.

Weitere verwandte Themen:

Frag Elektronik-Kompendium.de

Hardware-nahes Programmieren mit dem Raspberry Pi Pico und MicroPython

Elektronik-Set Pico Edition

Das Elektronik-Set Pico Edition ist ein Bauteile-Sortiment mit Anleitung zum Experimentieren und Programmieren mit MicroPython.

  • LED: Einschalten, ausschalten, blinken und Helligkeit steuern
  • Taster: Entprellen und Zustände anzeigen
  • LED mit Taster einschalten und ausschalten
  • Ampel- und Lauflicht-Steuerung
  • Elektronischer Würfel
  • Eigene Steuerungen programmieren

Elektronik-Set jetzt bestellen Online-Workshop buchen

Online-Workshop: Programmieren mit dem Raspberry Pi Pico

Programmieren mit dem Raspberry Pi Pico

Gemeinsam mit anderen und unter Anleitung experimentieren? Wir bieten unterschiedliche Online-Workshops zum Raspberry Pi Pico und MicroPython an. Einführung in die Programmierung, Sensoren programmieren und kalibrieren, sowie Internet of Things und Smart Home über WLAN und MQTT.

Online-Workshop buchen

Besuchen Sie unser fast monatlich stattfindendes Online-Meeting PicoTalk und lernen Sie uns kennen. Die Teilnahme ist kostenfrei.

Termine und Newsletter-Anmeldung

 

Elektronik-Sets für das Hardware-nahe Programmieren