3.2. Pomiar czasu#
Moduł time grupuje funkcje do usypiania (wstrzymywania skryptu na znany czas) oraz do mierzenia, jak długo coś trwa. Na mikrokontrolerze nie są to udogodnienia opcjonalne; to dzięki nim skrypt nadaje tempo interakcjom ze światem zewnętrznym – jak długo utrzymywać pin w stanie wysokim, jak długo czekać między próbkami, ile czasu minęło od ostatniego naciśnięcia przycisku.
3.2.1. Usypianie#
Trzy funkcje usypiania blokują skrypt na żądany czas:
time.sleep(s)– wstrzymuje nassekund. Przyjmuje liczbę zmiennoprzecinkową, więctime.sleep(0.5)czeka pół sekundy.time.sleep_ms(ms)– wstrzymuje namsmilisekund. Argument musi być liczbą całkowitą.time.sleep_us(us)– wstrzymuje nausmikrosekund.
import time
print("now")
time.sleep_ms(500)
print("half a second later")
Użyj time.sleep_ms() do typowych potrzeb „poczekaj chwilę”, a time.sleep_us() tylko wtedy, gdy pomiar czasu musi być precyzyjny. Zwykłe time.sleep() również jest w porządku, ale warianty z argumentem całkowitym unikają konwersji zmiennoprzecinkowej i czytają się bardziej naturalnie dla krótkich odstępów.
Sleep to wywołanie blokujące. Gdy skrypt śpi, nie robi nic innego – nie odczytuje pinu, nie obsługuje UART, nie aktualizuje diody LED. Sięgaj po sleep, gdy blokowanie jest tym, czego chcesz; gdy nie jest, użyj wzorca nieblokującego opisanego poniżej.
3.2.2. Odczyt zegara#
Aby zmierzyć, jak długo trwa fragment kodu, odczytaj zegar przed i po:
time.ticks_ms()– bieżąca wartość taktów w milisekundach.time.ticks_us()– to samo w mikrosekundach.
import time
start = time.ticks_ms()
do_work()
elapsed = time.ticks_ms() - start
print("took", elapsed, "ms")
Działa to przez większość czasu. Licznik taktów przekręca się (wraca do zera) po dużej, ale skończonej liczbie taktów, a naiwne odejmowanie obejmujące to przekręcenie daje skrajnie błędną liczbę ujemną lub dodatnią.
Licznik taktów wraca do zera, gdy osiąga limit liczby całkowitej. Zwykłe odejmowanie obejmujące to przekręcenie jest błędne.#
3.2.3. ticks_diff#
Aby poprawnie uzyskać liczbę upłyniętych taktów, nawet przy przekręceniu, użyj time.ticks_diff():
import time
start = time.ticks_ms()
do_work()
elapsed = time.ticks_diff(time.ticks_ms(), start)
print("took", elapsed, "ms")
Kolejność argumentów to ticks_diff(later, earlier) – wyrażenie czyta się jako „jak daleko jest later po earlier„. Wynik jest liczbą całkowitą ze znakiem; dodatni oznacza, że later faktycznie jest później, ujemny oznacza, że jest w przeszłości. Funkcja obsługuje przekręcenie wewnętrznie.
Wskazówka
Zawsze łącz time.ticks_ms() / time.ticks_us() z time.ticks_diff(). Surowe odejmowanie jest poprawne przez większość czasu; momentem, w którym jest błędne, jest sytuacja, gdy skrypt działa od dłuższego czasu – zwykle najgorszy moment na debugowanie usterki czasowej.
3.2.4. Pomiar czasu bez blokowania#
Skrypt mikrokontrolera zazwyczaj ma do zrobienia więcej niż jedną rzecz „jednocześnie”: odczyt przycisku, miganie diodą LED, odpytywanie sensora, obsługa UART. sleep się do tego nie nadaje – gdy działa jeden sleep, pozostałe zadania są wstrzymane.
Standardowy wzorzec polega na utrzymywaniu terminu (deadline) dla każdego zadania i sprawdzaniu w każdej iteracji pętli, czy termin już minął. Decyzja o działaniu opiera się na time.ticks_diff(), nigdy na 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)
Dioda LED przełącza się co 500 ms, sensor jest odpytywany co 100 ms, a żadne z zadań nie blokuje drugiego. time.ticks_add() przesuwa termin o znany przyrost, nie wpadając w pułapkę przekręcenia.
Ten kształt – pojedyncza pętla, kilka zadań sterowanych czasem, bez sleep w ciele – pojawia się wszędzie tam, gdzie trafia kod sprzętowy: programowe eliminowanie drgań styków przełączników, sekwencjonowanie silników, limity czasu odczytu UART. Te same trzy funkcje (time.ticks_ms(), time.ticks_diff(), time.ticks_add()) obejmują każdy przypadek.