3.1. Microcontroladores

La OpenMV Cam funciona sobre un microcontrolador (MCU): un único chip que combina una CPU, memoria de trabajo (RAM), almacenamiento de programa (memoria flash) y un conjunto de periféricos – bloques de hardware para interactuar con el mundo exterior.

Los periféricos son la parte interesante. Cada uno es una pieza de silicio dedicada a una sola tarea: poner un pin en alto o en bajo, medir una tensión analógica, sacar bytes en serie por un bus. La CPU configura y lee cada periférico a través de registros – direcciones de memoria fijas que el hardware vigila y actualiza.

MicroPython envuelve esos registros en clases dentro del módulo machine. machine.Pin(...) devuelve un objeto que controla un pin de entrada/salida de propósito general (GPIO) – un cable que el chip puede mantener en alto (alrededor de 3,3 V) o en bajo (alrededor de 0 V), o leer como uno de esos dos estados cuando algo externo lo impulsa. machine.ADC(...) expone el conversor analógico-digital, que mide la tensión en un pin y la reporta como un número. machine.UART(...) ejecuta un transmisor/receptor asíncrono universal (UART) – un periférico que envía y recibe bytes bit a bit a través de un par de cables, TX (transmisión) y RX (recepción). Otras clases cubren el resto de los periféricos. El script lee y escribe objetos de Python; MicroPython traduce cada acceso en las correspondientes lecturas y escrituras de registros, y estas mueven bits en cables físicos.

El contorno de un único chip que contiene bloques etiquetados -- CPU, RAM, flash y una fila de bloques de periféricos (GPIO, ADC, Timer/PWM, UART/SPI/I2C, CAN) conectados por un bus interno, con flechas que salen de cada periférico fuera del chip hacia las etiquetas de los pines físicos.

Un MCU empaqueta la CPU, la memoria y los periféricos en un único chip. Cada periférico se expone a Python mediante una clase del módulo machine.

3.1.1. El bucle principal

Casi todos los programas de microcontrolador comparten la misma forma: una configuración inicial única en la parte superior del script (importar módulos, configurar pines, abrir buses) y luego un bucle infinito while True: en la parte inferior. Dentro del bucle, el programa lee entradas, toma decisiones y actualiza salidas una y otra vez. El bucle es el programa; cuando el script termina, el dispositivo deja de hacer nada.

# setup, runs once
from machine import Pin
led = Pin("P0", Pin.OUT)

# main loop, runs forever
while True:
    led.value(1)
    # ... do work ...
    led.value(0)
    # ... do other work ...

Esta forma – configurar una vez y luego repetir indefinidamente – es el patrón del bucle principal. Todo lo que sigue trata sobre lo que va dentro de él.

3.1.2. Control en tiempo real

Un programa de escritorio se ejecuta junto a muchos otros. El sistema operativo planifica su trabajo entre uno o más hilos – flujos de ejecución independientes entre los que va alternando milisegundo a milisegundo. Cuando un hilo espera una operación de E/S (disco, red, el usuario moviendo el ratón), el SO cede la CPU a otro. El programa es en su mayoría dirigido por eventos: el gestor de ventanas llama a tu código cuando llega una entrada, la biblioteca HTTP reanuda tu código cuando llegan bytes al socket. Algo más grande te está llamando.

Un programa de microcontrolador es lo contrario. Por defecto no hay sistema operativo, ni planificador, ni otro hilo. El bucle principal que se acaba de mostrar es el único bucle. Los periféricos disparan interrupciones o exponen indicadores de estado; el bucle los sondea o gestiona las interrupciones directamente. Si el bucle se detiene en un time.sleep_ms(1000), el dispositivo no hace nada durante ese segundo; no hay otro hilo que llene el hueco.

De esto se derivan dos consecuencias que se aplican en todas partes:

  • El tiempo es real. Leer un pin dos veces en un bucle ajustado lleva microsegundos; dormir diez milisegundos significa diez milisegundos en los que no ocurre nada más. El patrón de temporización no bloqueante es la respuesta.

  • El hardware es real. Asignar 1 a machine.Pin.value pone aproximadamente 3,3 V en un cable físico; asignarle 0 pone aproximadamente 0 V allí. Otras partes del circuito ven esa tensión de inmediato – incluidos los componentes que el pin puede dañar si se impulsa incorrectamente.