3.22. SPI en código

machine.SPI envuelve un controlador hardware de SPI; las líneas CS son salidas Pin ordinarias gestionadas por el script. Construye una instancia de SPI con el id del bus, la velocidad de reloj deseada y (si es necesario) el modo:

from machine import SPI, Pin

spi = SPI(0, baudrate=1_000_000, polarity=0, phase=0)
cs = Pin("P3", Pin.OUT, value=1)         # CS idle high

El id selecciona qué bloque hardware de SPI usar; los números disponibles y los pines SCK/MOSI/MISO a los que se asignan dependen de la placa (consulta la Placas OpenMV). baudrate es la frecuencia de SCK en hercios; la velocidad real que alcanza el hardware puede ser ligeramente inferior debido a la división del reloj, lo cual mostrará el valor impreso del objeto SPI.

El pin CS se construye con value=1 para que repose desactivado. Cada transacción activa CS (lo pone a nivel bajo), mueve los bytes y vuelve a desactivar CS (lo pone a nivel alto).

3.22.1. Lectura, escritura, intercambio

Tres métodos cubren los casos habituales:

cs.value(0)
spi.write(b"\x10\x20\x30")              # send 3 bytes, ignore what comes back
cs.value(1)

cs.value(0)
data = spi.read(4)                      # read 4 bytes; sends 0x00 while reading
cs.value(1)

rx = bytearray(2)
cs.value(0)
spi.write_readinto(b"\x9F\x00", rx)     # send 0x9F, 0x00; receive 2 bytes
cs.value(1)

write() es la vía rápida de solo escritura; el controlador envía los bytes y descarta lo que el periférico haya devuelto por MISO. read() es la imagen reflejada: genera N pulsos de SCK mientras envía un byte ficticio fijo (0 de forma predeterminada) por MOSI y almacena los bytes de MISO. write_readinto() es la forma full-duplex: envía los bytes de un búfer y almacena los bytes simultáneos de MISO en otro. Muchos periféricos usan este patrón – «enviar un byte de comando y luego leer la respuesta en la siguiente transferencia» – de modo que las dos operaciones encajan de forma natural en una sola llamada a write_readinto.

La mayoría de los periféricos esperan que la línea CS permanezca activada durante toda la transacción (desde los bytes de comando hasta los bytes de respuesta), así que mantén el par cs.value(0) / cs.value(1) alrededor de toda la secuencia, no alrededor de cada llamada a un método.

3.22.2. Una lectura típica de un sensor

Muchos sensores SPI organizan su estado como un conjunto de registros internos y siguen la misma forma de intercambio: enviar la dirección del registro (con un bit de lectura/escritura en el bit superior) y luego leer o escribir los bytes del registro. Una lectura del registro 0x0F en un dispositivo de este tipo:

rx = bytearray(2)
cs.value(0)
spi.write_readinto(b"\x8F\x00", rx)     # 0x80 = "read" flag, then reg 0x0F
cs.value(1)
register_value = rx[1]

El primer byte de MISO es basura (en ese momento el dispositivo todavía estaba recibiendo el comando); el segundo byte de MISO contiene el contenido del registro. El formato exacto del byte de comando – qué bit es el indicador de lectura/escritura, si la dirección se autoincrementa en lecturas de varios bytes – está en la hoja de datos del dispositivo.

3.22.3. Bit-banging

La instancia de SPI anterior usa un bloque hardware de SPI: un periférico dedicado dentro del MCU con su propio registro de desplazamiento y generador de reloj que produce en silicio las formas de onda SCK / MOSI / MISO. El software solo le entrega un byte; los bits se mueven por el cable sin más ayuda de la CPU, dejándola libre para realizar otras tareas en paralelo.

La alternativa es el bit-banging: el software recorre cada bit en un bucle y conmuta directamente los pines GPIO para producir la misma forma de onda. No interviene ningún periférico hardware: la CPU pone SCK a nivel bajo, fija MOSI, pone SCK a nivel alto, muestrea MISO, y así con cada bit de cada byte. Eso mantiene ocupada la CPU durante toda la transacción y funciona más lento de lo que puede el bloque hardware, pero sirve en cualquier pin y no necesita que haya un bloque hardware libre.

machine.SoftSPI es la implementación por bit-banging de la misma API de SPI:

from machine import SoftSPI, Pin

spi = SoftSPI(baudrate=500_000, polarity=0, phase=0,
              sck=Pin("P2"), mosi=Pin("P0"), miso=Pin("P1"))

Úsalo cuando el dispositivo deba estar en pines que no están conectados a un bloque hardware de SPI, o cuando todos los bloques hardware estén en uso. 500 kHz es un techo cómodo en la mayoría de las cámaras; la CPU permanece ocupada durante toda la transacción.