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.