3.2. Temporización

El módulo time agrupa funciones para dormir (pausar el script durante una duración conocida) y para medir cuánto tarda algo. En un microcontrolador estas no son funciones opcionales; son la forma en que el script regula sus interacciones con el mundo exterior – cuánto tiempo mantener un pin en alto, cuánto esperar entre muestras, cuánto tiempo ha pasado desde la última vez que se pulsó un botón.

3.2.1. Dormir

Tres funciones de dormir bloquean el script durante la duración solicitada:

  • time.sleep(s) – pausa durante s segundos. Acepta un valor de coma flotante, así que time.sleep(0.5) espera medio segundo.

  • time.sleep_ms(ms) – pausa durante ms milisegundos. El argumento debe ser un entero.

  • time.sleep_us(us) – pausa durante us microsegundos.

import time

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

Usa time.sleep_ms() para las necesidades típicas de «esperar un poco» y time.sleep_us() solo cuando la temporización deba ser ajustada. La time.sleep() simple también es válida, pero las variantes con argumento entero evitan la conversión de coma flotante y se leen de forma más natural para intervalos cortos.

Dormir es una llamada bloqueante. Mientras el script está durmiendo, no está haciendo nada más – ni leyendo un pin, ni atendiendo un UART, ni actualizando un LED. Recurre a sleep cuando bloquear sea lo que deseas; usa el patrón no bloqueante de más abajo cuando no lo sea.

3.2.2. Leer el reloj

Para medir cuánto tarda una pieza de código, lee el reloj antes y después:

import time

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

Esto funciona la mayor parte del tiempo. El contador de ticks se desborda (vuelve a cero) tras un número grande pero finito de ticks, y una resta ingenua a través de ese desbordamiento produce un número negativo o positivo tremendamente erróneo.

Una recta numérica de 0 a MAX con un tick inicial cerca de MAX y un tick final cerca de 0; una flecha de desbordamiento punteada muestra que tras MAX el contador vuelve a 0.

El contador de ticks vuelve a cero cuando alcanza el límite entero. Una resta simple a través de ese desbordamiento es incorrecta.

3.2.3. ticks_diff

Para obtener correctamente los ticks transcurridos, incluso a través de un desbordamiento, usa time.ticks_diff():

import time

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

El orden de los argumentos es ticks_diff(later, earlier) – la expresión se lee «cuánto después está later respecto de earlier». El resultado es un entero con signo; positivo significa que later es efectivamente posterior, negativo significa que está en el pasado. La función gestiona el desbordamiento internamente.

Truco

Empareja siempre time.ticks_ms() / time.ticks_us() con time.ticks_diff(). La resta cruda es correcta la mayor parte del tiempo; el momento en que falla es cuando un script lleva mucho tiempo en ejecución – normalmente el peor momento para depurar un fallo de temporización.

3.2.4. Temporización no bloqueante

Un script de microcontrolador suele tener más de una cosa que hacer «al mismo tiempo»: leer un botón, parpadear un LED, sondear un sensor, atender un UART. sleep no sirve para eso – mientras un sleep se está ejecutando, las demás tareas quedan paradas.

El patrón estándar es mantener un plazo límite por tarea y comprobar en cada iteración del bucle si el plazo ha pasado. La decisión de actuar se construye sobre time.ticks_diff(), nunca sobre 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)

El LED conmuta cada 500 ms, el sensor se sondea cada 100 ms y ninguna tarea bloquea a la otra. time.ticks_add() adelanta un plazo límite en un incremento conocido sin caer en el problema del desbordamiento.

Esta forma – un único bucle, varias tareas temporizadas, sin sleep en el cuerpo – aparece en todas partes donde va el código de hardware: antirrebote por software de interruptores, secuenciación de motores, tiempos de espera de lectura de UART. Las mismas tres funciones (time.ticks_ms(), time.ticks_diff(), time.ticks_add()) cubren todos los casos.