3.2. Temporização

O módulo time agrupa funções para dormir (pausar o script por uma duração conhecida) e para medir quanto tempo algo demora. Num microcontrolador, estas não são conveniências; são a forma como o script cadencia as interações com o mundo exterior – quanto tempo manter um pino a alto, quanto tempo esperar entre amostras, quanto tempo desde que um botão foi premido pela última vez.

3.2.1. Dormir

Três funções de sleep bloqueiam o script pela duração solicitada:

  • time.sleep(s) – pausar por s segundos. Aceita um float, por isso time.sleep(0.5) aguarda meio segundo.

  • time.sleep_ms(ms) – pausar por ms milissegundos. O argumento tem de ser um inteiro.

  • time.sleep_us(us) – pausar por us microssegundos.

import time

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

Use time.sleep_ms() para necessidades típicas de «aguardar um pouco» e time.sleep_us() apenas quando a temporização tem de ser precisa. O time.sleep() simples também funciona, mas as variantes com argumento inteiro evitam a conversão de vírgula flutuante e ficam mais naturais para intervalos curtos.

Sleep é uma chamada bloqueante. Enquanto o script está a dormir, não faz mais nada – não lê um pino, não serve um UART, não atualiza um LED. Recorra a sleep quando o bloqueio é o comportamento desejado; use o padrão não bloqueante abaixo quando não é.

3.2.2. Ler o relógio

Para medir quanto tempo um pedaço de código demora, leia o relógio antes e depois:

import time

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

Isto funciona na maior parte das vezes. O contador de ticks faz rollover (volta a zero) após um número grande mas finito de ticks, e a subtração simples ao atravessar esse rollover produz um número negativo ou positivo completamente errado.

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.

O contador de ticks volta a zero quando atinge o limite do inteiro. Uma subtração simples ao atravessar esse rollover é incorreta.

3.2.3. ticks_diff

Para obter os ticks decorridos corretamente, mesmo num rollover, use time.ticks_diff():

import time

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

A ordem dos argumentos é ticks_diff(later, earlier) – a expressão lê-se «quão longe está later depois de earlier«. O resultado é um inteiro com sinal; positivo significa que later é de facto posterior, negativo significa que está no passado. A função trata o rollover internamente.

Dica

Associe sempre time.ticks_ms() / time.ticks_us() com time.ticks_diff(). A subtração simples está correta na maior parte das vezes; a vez em que está errada é quando um script está a funcionar há muito tempo – geralmente o pior momento para depurar um problema de temporização.

3.2.4. Temporização não bloqueante

Um script de microcontrolador normalmente tem mais de uma coisa a fazer «ao mesmo tempo»: ler um botão, piscar um LED, fazer polling a um sensor, servir um UART. O sleep não serve para isso – enquanto um sleep está a funcionar, as outras tarefas estão paradas.

O padrão standard é manter um prazo por tarefa e verificar a cada iteração do ciclo se o prazo passou. A decisão de agir é baseada em time.ticks_diff(), nunca em 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)

O LED alterna a cada 500 ms, o sensor é amostrado a cada 100 ms e nenhuma tarefa bloqueia a outra. time.ticks_add() avança um prazo por um incremento conhecido sem problemas com o rollover.

Esta estrutura – um único ciclo, várias tarefas temporizadas, sem sleep no corpo – aparece em todo o lado no código de hardware: remoção de bouncing de interruptores por software, sequenciamento de motores, timeouts de leitura UART. As mesmas três funções (time.ticks_ms(), time.ticks_diff(), time.ticks_add()) cobrem todos os casos.