3.2. Temporizzazione#
Il modulo time raggruppa le funzioni per mettere in pausa (sospendere lo script per una durata nota) e per misurare quanto tempo richiede qualcosa. Su un microcontrollore queste non sono comodità accessorie; sono il modo in cui lo script regola il ritmo delle interazioni con il mondo esterno – per quanto tempo tenere un pin alto, quanto attendere tra un campione e l’altro, quanto tempo è passato dall’ultima pressione di un pulsante.
3.2.1. Mettere in pausa#
Tre funzioni di pausa bloccano lo script per la durata richiesta:
time.sleep(s)– mette in pausa perssecondi. Accetta un float, quinditime.sleep(0.5)attende mezzo secondo.time.sleep_ms(ms)– mette in pausa permsmillisecondi. L’argomento deve essere un intero.time.sleep_us(us)– mette in pausa perusmicrosecondi.
import time
print("now")
time.sleep_ms(500)
print("half a second later")
Usa time.sleep_ms() per le tipiche esigenze di «attendere un po”» e time.sleep_us() solo quando la temporizzazione deve essere precisa. Anche la semplice time.sleep() va bene, ma le varianti con argomento intero evitano la conversione in virgola mobile e risultano più naturali per intervalli brevi.
La pausa è una chiamata bloccante. Mentre lo script è in pausa, non sta facendo nient’altro – non legge un pin, non serve una UART, non aggiorna un LED. Ricorri a sleep quando il blocco è ciò che desideri; usa il pattern non bloccante più avanti quando non lo è.
3.2.2. Leggere l’orologio#
Per misurare quanto tempo richiede un pezzo di codice, leggi l’orologio prima e dopo:
time.ticks_ms()– il valore di tick corrente in millisecondi.time.ticks_us()– lo stesso in microsecondi.
import time
start = time.ticks_ms()
do_work()
elapsed = time.ticks_ms() - start
print("took", elapsed, "ms")
Questo funziona quasi sempre. Il contatore di tick va in overflow (torna a zero) dopo un numero grande ma finito di tick, e una sottrazione ingenua a cavallo di quell’overflow produce un numero negativo o positivo del tutto errato.
Il contatore di tick torna a zero quando raggiunge il limite intero. Una semplice sottrazione a cavallo di quell’overflow è errata.#
3.2.3. ticks_diff#
Per ottenere correttamente i tick trascorsi, anche a cavallo di un overflow, usa time.ticks_diff():
import time
start = time.ticks_ms()
do_work()
elapsed = time.ticks_diff(time.ticks_ms(), start)
print("took", elapsed, "ms")
L’ordine degli argomenti è ticks_diff(later, earlier) – l’espressione si legge «quanto è distante later dopo earlier«. Il risultato è un intero con segno; positivo significa che later è effettivamente successivo, negativo che è nel passato. La funzione gestisce internamente l’overflow.
Suggerimento
Abbina sempre time.ticks_ms() / time.ticks_us() a time.ticks_diff(). La sottrazione grezza è corretta quasi sempre; la volta in cui è errata è quando uno script è in esecuzione da molto tempo – di solito il momento peggiore per fare il debug di un problema di temporizzazione.
3.2.4. Temporizzazione non bloccante#
Uno script per microcontrollore di solito ha più di una cosa da fare «contemporaneamente»: leggere un pulsante, far lampeggiare un LED, interrogare un sensore, servire una UART. sleep non va bene per questo – mentre un sleep è in esecuzione, gli altri compiti sono fermi.
Il pattern standard consiste nel mantenere una scadenza per ogni compito e nel controllare a ogni ciclo se la scadenza è passata. La decisione di agire si basa su time.ticks_diff(), mai su 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)
Il LED commuta ogni 500 ms, il sensore viene interrogato ogni 100 ms e nessuno dei due compiti blocca l’altro. time.ticks_add() fa avanzare una scadenza di un incremento noto senza incappare nell’overflow.
Questa struttura – un singolo ciclo, diversi compiti temporizzati, nessuno sleep nel corpo – compare ovunque vada il codice hardware: debouncing software degli interruttori, sequenziamento dei motori, timeout di lettura della UART. Le stesse tre funzioni (time.ticks_ms(), time.ticks_diff(), time.ticks_add()) coprono ogni caso.