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.

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.

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.