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.
- Erhöhe den Wert von „period“ auf 2000. Die LED blinkt langsamer.
- Verringere den Wert von „period“ auf 100. Die LED blinkt deutlich schneller.
- Verringere den Wert von „period“ auf 20. Die LED flackert.
- 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.
- Verringere den Wert von „freq“ auf 0.5. Die LED blinkt langsamer.
- Erhöhe den Wert von „freq“ auf 10. Die LED blinkt deutlich schneller.
- Erhöhe den Wert von „freq“ auf 20. Die LED flackert.
- 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.
- Raspberry Pi Pico: Onboard-LED blinken lassen
- Raspberry Pi Pico: Digitale Uhr mit Anzeige (TM1637)
- Raspberry Pi Pico: Stoppuhr mit Anzeige (TM1637)
- Raspberry Pi Pico: Countdown mit Zeitanzeige (TM1637)
- Raspberry Pi Pico: Blink-Geschwindigkeit einer LED mit einem Rotary Encoder einstellen (KY-040)
Weitere verwandte Themen:
- MicroPython: Grundlagen zum Timer
- MicroPython: Laufzeit messen
- Raspberry Pi Pico: Grundlagen zur Echtzeituhr (Real-time Clock, RTC)
- Raspberry Pi Pico: Experimente mit RTC
- Raspberry Pi Pico: Grundlegende Befehle von MicroPython
Frag Elektronik-Kompendium.de
Hardware-nahes Programmieren mit dem Raspberry Pi Pico und MicroPython
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
Online-Workshop: 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.
Besuchen Sie unser fast monatlich stattfindendes Online-Meeting PicoTalk und lernen Sie uns kennen. Die Teilnahme ist kostenfrei.






