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.