3.2. Zeitmessung¶
Das time-Modul fasst Funktionen zum Schlafen (Anhalten des Skripts für eine bekannte Dauer) und zum Messen der Dauer von Vorgängen zusammen. Auf einem Mikrocontroller sind diese nicht nur nettes Beiwerk; sie sind die Art und Weise, wie das Skript die Interaktionen mit der Außenwelt taktet – wie lange ein Pin auf High gehalten wird, wie lange zwischen Abtastungen gewartet wird, wie lange ein Knopfdruck zurückliegt.
3.2.1. Schlafen¶
Drei Sleep-Funktionen blockieren das Skript für die angeforderte Dauer:
time.sleep(s)– pausiert fürsSekunden. Akzeptiert eine Gleitkommazahl, sodasstime.sleep(0.5)eine halbe Sekunde wartet.time.sleep_ms(ms)– pausiert fürmsMillisekunden. Das Argument muss eine Ganzzahl sein.time.sleep_us(us)– pausiert fürusMikrosekunden.
import time
print("now")
time.sleep_ms(500)
print("half a second later")
Verwenden Sie time.sleep_ms() für typische „kurz warten“-Bedürfnisse und time.sleep_us() nur, wenn das Timing eng sein muss. Auch das einfache time.sleep() ist in Ordnung, aber die Varianten mit Ganzzahl-Argument vermeiden die Gleitkomma-Umwandlung und lesen sich bei kurzen Intervallen natürlicher.
Sleep ist ein blockierender Aufruf. Während das Skript schläft, tut es nichts anderes – es liest keinen Pin, bedient keinen UART, aktualisiert keine LED. Greifen Sie zu sleep, wenn Blockieren genau das ist, was Sie wollen; verwenden Sie das nicht-blockierende Muster unten, wenn nicht.
3.2.2. Die Uhr auslesen¶
Um zu messen, wie lange ein Codeabschnitt dauert, lesen Sie die Uhr davor und danach aus:
time.ticks_ms()– der aktuelle Tick-Wert in Millisekunden.time.ticks_us()– dasselbe in Mikrosekunden.
import time
start = time.ticks_ms()
do_work()
elapsed = time.ticks_ms() - start
print("took", elapsed, "ms")
Das funktioniert meistens. Der Tick-Zähler läuft über (springt zurück auf null) nach einer großen, aber endlichen Anzahl von Ticks, und eine naive Subtraktion über diesen Überlauf hinweg erzeugt eine völlig falsche negative oder positive Zahl.
Der Tick-Zähler springt zurück auf null, wenn er das Ganzzahl-Limit erreicht. Eine einfache Subtraktion über diesen Überlauf hinweg ist falsch.¶
3.2.3. ticks_diff¶
Um die verstrichenen Ticks korrekt zu erhalten, sogar über einen Überlauf hinweg, verwenden Sie time.ticks_diff():
import time
start = time.ticks_ms()
do_work()
elapsed = time.ticks_diff(time.ticks_ms(), start)
print("took", elapsed, "ms")
Die Argumentreihenfolge ist ticks_diff(later, earlier) – der Ausdruck liest sich als „wie weit liegt later nach earlier“. Das Ergebnis ist eine vorzeichenbehaftete Ganzzahl; positiv bedeutet, dass later tatsächlich später ist, negativ bedeutet, dass es in der Vergangenheit liegt. Die Funktion behandelt den Überlauf intern.
Tipp
Paaren Sie time.ticks_ms() / time.ticks_us() immer mit time.ticks_diff(). Die rohe Subtraktion ist meistens korrekt; der Zeitpunkt, zu dem sie falsch ist, ist, wenn ein Skript lange Zeit gelaufen ist – normalerweise der schlechteste Zeitpunkt, um eine Timing-Störung zu debuggen.
3.2.4. Nicht-blockierende Zeitmessung¶
Ein Mikrocontroller-Skript hat normalerweise mehr als eine Sache „gleichzeitig“ zu erledigen: einen Knopf lesen, eine LED blinken lassen, einen Sensor abfragen, einen UART bedienen. sleep taugt dafür nichts – während ein sleep läuft, stehen die anderen Aufgaben still.
Das Standardmuster besteht darin, pro Aufgabe eine Deadline zu führen und in jeder Schleife zu prüfen, ob die Deadline überschritten wurde. Die Entscheidung zu handeln baut auf time.ticks_diff() auf, niemals auf sleep:
import time
next_blink = time.ticks_ms()
next_poll = time.ticks_ms()
state = False
while True:
now = time.ticks_ms()
if time.ticks_diff(now, next_blink) >= 0:
state = not state
# update LED here
next_blink = time.ticks_add(next_blink, 500)
if time.ticks_diff(now, next_poll) >= 0:
# read sensor here
next_poll = time.ticks_add(next_poll, 100)
Die LED schaltet alle 500 ms um, der Sensor wird alle 100 ms abgefragt, und keine der Aufgaben blockiert die andere. time.ticks_add() verschiebt eine Deadline um ein bekanntes Inkrement, ohne in die Falle des Überlaufs zu tappen.
Diese Struktur – eine einzige Schleife, mehrere zeitgesteuerte Aufgaben, kein sleep im Rumpf – taucht überall dort auf, wo Hardware-Code verwendet wird: Software-Entprellen von Schaltern, Motorsequenzierung, UART-Lese-Timeouts. Dieselben drei Funktionen (time.ticks_ms(), time.ticks_diff(), time.ticks_add()) decken jeden Fall ab.