3.2. Časování#

Modul time sdružuje funkce pro spánek (pozastavení skriptu na známou dobu) a pro měření, jak dlouho něco trvá. Na mikrokontroléru to nejsou nepovinné vymoženosti; jsou způsobem, jakým skript řídí tempo interakcí s okolním světem – jak dlouho držet pin ve vysoké úrovni, jak dlouho čekat mezi vzorky, jak dlouho uplynulo od posledního stisku tlačítka.

3.2.1. Spánek#

Tři funkce spánku blokují skript po požadovanou dobu:

  • time.sleep(s) – pozastaví na s sekund. Přijímá float, takže time.sleep(0.5) čeká půl sekundy.

  • time.sleep_ms(ms) – pozastaví na ms milisekund. Argument musí být celé číslo.

  • time.sleep_us(us) – pozastaví na us mikrosekund.

import time

print("now")
time.sleep_ms(500)
print("half a second later")

Pro typické potřeby „chvíli počkat“ použijte time.sleep_ms() a time.sleep_us() jen tehdy, když časování musí být přesné. Obyčejná funkce time.sleep() je také v pořádku, ale varianty s celočíselným argumentem se vyhýbají převodu na pohyblivou řádovou čárku a pro krátké intervaly se čtou přirozeněji.

Spánek je blokující volání. Zatímco skript spí, nedělá nic jiného – nečte pin, neobsluhuje UART, neaktualizuje LED. Sáhněte po sleep, když je blokování to, co chcete; pokud ne, použijte neblokující vzorec níže.

3.2.2. Čtení hodin#

Chcete-li změřit, jak dlouho trvá nějaký úsek kódu, přečtěte hodiny před ním a po něm:

import time

start = time.ticks_ms()
do_work()
elapsed = time.ticks_ms() - start
print("took", elapsed, "ms")

Toto funguje většinu času. Čítač tiků přeteče (vrátí se zpět na nulu) po velkém, ale konečném počtu tiků a naivní odečtení přes tento přechod produkuje divoce nesprávné záporné nebo kladné číslo.

Number line from 0 to MAX with a start tick near MAX and an end tick near 0; a dashed wrap-around arrow shows that after MAX the counter returns to 0.

Čítač tiků se po dosažení celočíselného limitu vrátí zpět na nulu. Obyčejné odečtení přes tento přechod je nesprávné.#

3.2.3. ticks_diff#

Pro správné získání uplynulých tiků, i přes přetečení, použijte time.ticks_diff():

import time

start = time.ticks_ms()
do_work()
elapsed = time.ticks_diff(time.ticks_ms(), start)
print("took", elapsed, "ms")

Pořadí argumentů je ticks_diff(later, earlier) – výraz se čte jako „jak daleko je later po earlier“. Výsledkem je celé číslo se znaménkem; kladné znamená, že later je skutečně později, záporné znamená, že je v minulosti. Funkce přetečení zpracovává interně.

Tip

time.ticks_ms() / time.ticks_us() vždy párujte s time.ticks_diff(). Surové odečtení je správné většinu času; kdy je nesprávné, je tehdy, když skript běží dlouhou dobu – obvykle ve chvíli, kdy je ladění chyby časování nejhorší.

3.2.4. Neblokující časování#

Skript pro mikrokontrolér obvykle musí dělat více než jednu věc „současně“: číst tlačítko, blikat LED, dotazovat senzor, obsluhovat UART. sleep k tomu není dobrý – zatímco běží jeden sleep, ostatní úlohy jsou zastavené.

Standardním vzorcem je udržovat termín (deadline) pro každou úlohu a v každé smyčce kontrolovat, zda termín uplynul. Rozhodnutí jednat je postaveno na time.ticks_diff(), nikdy 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)

LED se přepíná každých 500 ms, senzor se dotazuje každých 100 ms a žádná úloha neblokuje druhou. time.ticks_add() posune termín o známý přírůstek, aniž by narazila na problém s přetečením.

Tento tvar – jediná smyčka, několik časovaných úloh, žádný sleep v těle – se objevuje všude, kde se vyskytuje hardwarový kód: softwarové potlačení zákmitů spínačů (debouncing), sekvencování motorů, časové limity čtení z UART. Stejné tři funkce (time.ticks_ms(), time.ticks_diff(), time.ticks_add()) pokrývají každý případ.