← Índice del curso

Comunicación entre Dispositivos · Tema 14 · SPI

Varios esclavos y dispositivos SPI

Un microcontrolador casi nunca habla con un solo periférico: lee un sensor, guarda en una memoria, muestra en una pantalla. Vamos a ver cómo se comparte un bus SPI entre varios esclavos con una línea de selección por cada uno, cómo se encadenan dispositivos para ahorrar pines, qué pasa cuando dos esclavos hablan a la vez y cómo funcionan en la práctica las tarjetas SD, las memorias flash y los conversores.

01 · Del enlace a la red

Un bus, varios dispositivos

En los temas anteriores unimos un maestro con un esclavo. Pero una placa real tiene un acelerómetro, una memoria, una pantalla y quizá una tarjeta SD, y no hay pines para un bus SPI completo por cada uno. Hay dos formas de conectar varios esclavos que difieren en cómo se reparten las cuatro líneas:

Bus con un CS por esclavo

SCLK, MOSI y MISO son compartidas por todos; cada esclavo tiene su propia línea CS. Es la conexión más común y la más flexible: cada dispositivo puede tener su modo y su velocidad. Cuesta un pin por esclavo.

Cadena en margarita (daisy chain)

Los esclavos se encadenan: la salida de uno entra al siguiente y el último vuelve al maestro. Comparten SCLK y CS, y el maestro usa solo cuatro pines sea cual sea el número de dispositivos. Solo sirve para dispositivos preparados para ello.

Esto responde a una necesidad que ya planteamos en el tema 6: en un esquema maestro–esclavo hace falta un mecanismo para decidir a quién se le habla. En I²C se resuelve con direcciones dentro del mensaje; en SPI, con una línea física.

02 · El esquema habitual

Un CS por esclavo

Bus SPI con tres esclavos Las líneas SCLK, MOSI y MISO recorren el bus y se derivan a tres esclavos. Cada esclavo tiene además su propia línea CS que sale del maestro hacia él y solo hacia él. Maestro Esclavo 1Esclavo 2Esclavo 3 SCLKMOSIMISO CS1CS2CS3

Los tres hilos compartidos se derivan a todos los esclavos; las líneas CS (en naranja) van una a una. La regla de funcionamiento es la que ya vimos: el maestro baja un solo CS, y el esclavo seleccionado escucha el bus y es el único que maneja MISO. Los demás ignoran los pulsos de SCLK y los datos de MOSI mientras su CS esté alto.

Hay un detalle eléctrico que hace posible esto y que es fuente de problemas: MISO es una salida push-pull (tema 5). Si dos esclavos la manejaran a la vez, uno intentaría poner un 1 y otro un 0, y circularía una corriente de cortocircuito. La solución está en el diseño de los esclavos: cuando CS está alto, MISO pasa a alta impedancia (tri-state), es decir, la salida se desconecta eléctricamente del bus. Solo el esclavo seleccionado la maneja.

Otras consideraciones de este esquema:

  • Cada esclavo puede tener su modo, velocidad y orden de bits (tema 13). El maestro los cambia antes de cada conversación.
  • Pull-up en cada CS. Mientras el microcontrolador arranca, sus pines están flotando; sin una resistencia de unos 10 kΩ a alimentación, un CS flotante puede interpretarse como bajo y el esclavo escuchar basura. Con el pull-up, todos arrancan deseleccionados.
  • Capacidad y longitud. Cada esclavo suma capacidad al bus y alarga el cable: con varios, la frecuencia máxima baja. El esclavo más lejano es el limitante.
  • Un esclavo mal configurado afecta a todos: un CS que queda siempre bajo por un error de programa deja ese dispositivo conectado y en conflicto con el siguiente.

03 · Menos pines, otra estructura

La cadena en margarita

Si los esclavos son registros de desplazamiento, el anillo de dos registros que vimos en el tema 12 se puede extender: la salida del primer esclavo no vuelve al maestro sino que entra al siguiente, y así hasta el último, cuya salida sí vuelve al maestro por MISO. Todos comparten el mismo reloj y el mismo CS.

Cadena en margarita con tres dispositivos MOSI entra al primer dispositivo, cuya salida va al segundo, cuya salida va al tercero. La salida del tercero regresa al maestro por MISO. SCLK y CS son comunes a los tres. Maestro Dispositivo 1Dispositivo 2Dispositivo 3 MOSIsalida 1 → entrada 2salida 2 → entrada 3 SCLK (común)MISO: la salida del dispositivo 3 vuelve al maestro CS (común)

El efecto es que los tres registros de 8 bits se comportan como un solo registro de 24 bits. Para actualizarlos, el maestro baja CS, envía tantos bytes como dispositivos (24 pulsos de reloj para tres) y sube CS. Dos reglas permiten no equivocarse:

  • El primer byte enviado termina en el último dispositivo de la cadena, porque es el que más lugares se desplazó. El último byte enviado queda en el primero.
  • Por MISO vuelve, byte a byte, el contenido anterior de la cadena, empezando por el último dispositivo. Para leer una cadena hay que enviar bytes de relleno mientras se recogen los que salen.

Los dispositivos típicos de una cadena son el 74HC595 (registro de desplazamiento con salidas que se actualizan al subir un pin de «latch», que se conecta a CS) y el MAX7219 (controlador de pantallas de LED, con una palabra de 16 bits por dispositivo y la actualización también al subir CS). Una tira de ocho de estos controla 64 dígitos con los mismos cuatro pines.

La cadena tiene límites claros. Solo sirve para dispositivos que aceptan datos en serie y pasan los que no les corresponden: una memoria flash, un conversor o una tarjeta SD no se conectan así. El tiempo de actualización crece con la cantidad (N bytes por transacción), y si un dispositivo falla o se desconecta, se rompe la cadena entera.

04 · Ahorrar líneas de selección

Menos pines: el decodificador

Cuando hay muchos esclavos que no se pueden encadenar, el costo es un pin de CS por cada uno. Con 8 esclavos se necesitan 3 + 8 = 11 pines. Un decodificador reduce esa cuenta: un circuito como el 74HC138 recibe un número binario de 3 bits (A0, A1, A2) y baja una sola de sus 8 salidas (Y0 a Y7), que sirven de CS para los esclavos.

Pines necesarios para N esclavos según el método (3 pines compartidos: SCLK, MOSI, MISO).
N esclavosUn CS por esclavoDecodificador (74HC138)Cadena en margarita
254 (1 línea de dirección)4
475 (2 líneas)4
8116 (3 líneas)4
16197 (4 líneas, con dos decodificadores)4

Con N esclavos hacen falta ⌈log2 N⌉ líneas de dirección. El decodificador tiene también entradas de habilitación: con ellas se puede deseleccionar todo (todas las salidas en alto), algo necesario para el reposo entre transacciones. Sin habilitación, hay que reservar una salida (la Y7, por ejemplo) como «ninguno».

Dos precauciones. Primero, al cambiar de una dirección a otra el decodificador pasa por direcciones intermedias durante unos nanosegundos y puede bajar por un instante un CS que no corresponde: conviene deshabilitarlo (o dejar todo en «ninguno») antes de cambiar la dirección. Segundo, los CS ahora pasan por un circuito más con su propio retardo, que no importa para una transacción de bytes pero sí para un reloj muy rápido.

05 · Qué hay del otro lado

Dispositivos típicos

Los periféricos SPI más usados tienen sus propios convenios. Conocerlos permite leer hojas de datos con rapidez.

Tarjeta SD

Toda tarjeta SD acepta el modo SPI (modo 0). Su arranque tiene un protocolo propio:

  1. Alimentar la tarjeta y enviar al menos 74 pulsos de reloj con CS alto y MOSI en alto (típicamente 10 bytes 0xFF), a una frecuencia de 100 a 400 kHz, el máximo permitido durante la identificación.
  2. Bajar CS y enviar CMD0 (reset): la tarjeta responde 0x01 («en estado de reposo»).
  3. Enviar CMD8 para ver si la tarjeta es de la versión 2, y repetir ACMD41 hasta que responda 0x00 (lista). Puede demorar cientos de milisegundos.
  4. Subir la frecuencia (hasta 25 MHz en las tarjetas comunes) y leer o escribir bloques de 512 bytes con comandos de 6 bytes (CMD17 lee un bloque; CMD24 escribe uno).

En la práctica nadie escribe esto desde cero: bibliotecas como SD o SdFat implementan el protocolo y, encima, el sistema de archivos FAT. Pero conviene saber que, cuando una tarjeta «no inicializa», casi siempre es un problema de la secuencia de arranque (frecuencia demasiado alta, falta del byte 0xFF inicial, alimentación insuficiente o CS mal conectado).

Memoria flash

Las memorias tipo W25Q usan comandos de un byte seguidos de una dirección de tres bytes:

Comandos habituales de una memoria flash SPI tipo W25Q.
ComandoCódigoQué envía el maestroQué hace
Leer identificación0x9Fcomando, y luego 3 bytes de rellenoDevuelve fabricante, tipo y capacidad (por ejemplo EF 40 18)
Leer datos0x03comando + 3 bytes de dirección + rellenoDevuelve bytes consecutivos desde la dirección (ráfaga ilimitada)
Habilitar escritura0x06solo el comandoPermite una escritura o borrado (se pierde tras cada operación)
Programar página0x02comando + 3 de dirección + hasta 256 bytesEscribe dentro de una página; solo puede pasar bits de 1 a 0
Borrar sector0x20comando + 3 de direcciónPone en 1 un sector de 4 KB
Leer estado0x05comando + rellenoEl bit 0 indica «ocupada» mientras se escribe o borra

Tres detalles que importan al programar: la escritura requiere 0x06 antes de cada operación; la operación recién empieza cuando CS sube al final de la instrucción, así que subirlo a mitad de camino la deja sin efecto; y después de programar o borrar hay que esperar (de décimas de milisegundo a cientos de milisegundos) leyendo el registro de estado hasta que el bit de ocupado vuelva a 0.

Conversor analógico-digital

Un MCP3008 (8 canales, 10 bits) no tiene registros ni comandos de lectura/escritura: cada transacción pide un canal y devuelve una muestra en tres bytes:

  • El maestro envía 0x01 (bit de inicio), luego 0x80 | (canal << 4) (modo single-ended y número de canal) y 0x00 de relleno.
  • Los 10 bits del resultado llegan en los dos últimos bytes: valor = ((r[1] & 3) << 8) | r[2].
  • Son tres bytes de datos con la respuesta mezclada con la última parte del comando: el dato no empieza en un límite de byte, y por eso se enmascara.

Es el ejemplo clásico de que un esclavo SPI puede responder mientras recibe el comando, algo que contradice la regla de «el primer byte no tiene significado» del tema 12. Cada dispositivo es distinto, y la hoja de datos manda.

Pantallas

Las pantallas gráficas (ST7735, ST7789, ILI9341, SSD1306 por SPI) suelen ser solo de escritura: el maestro envía comandos y datos de píxeles y no lee nada (a veces ni siquiera existe MISO). Necesitan una línea adicional, D/C (datos/comando), que dice si el byte que viene es un comando o un dato, y otra de reinicio. Por eso muchas veces aparecen con MOSI, SCLK, CS, D/C, RESET: más pines que los cuatro de SPI, pero con la velocidad de decenas de MHz que exige llenar un cuadro de pantalla.

06 · Convivir sin pisarse

Reglas para compartir el bus

  1. Un solo CS bajo a la vez. Es la regla fundamental. Una función que baja un CS debe subirlo antes de devolver el control.
  2. Todos los CS arrancan en alto. Configurarlos como salida y ponerlos en alto antes de iniciar el bus, con resistencias de pull-up para el instante previo.
  3. Los ajustes viajan con la conversación. Usar SPI.beginTransaction(ajustes) y SPI.endTransaction() en cada acceso, para que modo, velocidad y orden de bits sean los del dispositivo que se atiende.
  4. Las transacciones no se mezclan. Si una interrupción accede al SPI mientras el programa principal está a medio camino, las dos conversaciones se entremezclan. Se resuelve evitando el SPI en interrupciones (se marca una bandera y se atiende en loop()) o protegiendo las transacciones.
  5. El dispositivo más lento y más lejano manda. La frecuencia de cada conversación se ajusta al esclavo atendido, y el cableado se piensa para el peor caso.
  6. Esclavos de distinta tensión necesitan adaptación de nivel en cada línea hacia ellos (tema 5), y los que no sueltan MISO necesitan un buffer.
  7. Un registro de identificación para cada dispositivo en el arranque: si uno no responde, se sabe cuál y desde el primer momento.

07 · Con el microcontrolador

Con el microcontrolador

El primer programa lee el identificador de dos esclavos del mismo bus (un acelerómetro en modo 3 y una flash en modo 0) con una función que garantiza la regla de un solo CS bajo. El segundo escribe 16 salidas con dos 74HC595 en cadena:

#include <SPI.h>

const byte CS_ACEL = 9;
const byte CS_FLASH = 10;
const SPISettings AJ_ACEL(2000000, MSBFIRST, SPI_MODE3);
const SPISettings AJ_FLASH(8000000, MSBFIRST, SPI_MODE0);

// Toda conversación pasa por aquí: ajustes propios, un solo CS bajo, y CS alto al volver.
void hablar(byte pinCs, const SPISettings &ajustes, const byte *enviar, byte *recibir, byte n) {
    SPI.beginTransaction(ajustes);
    digitalWrite(pinCs, LOW);
    for (byte i = 0; i < n; i++) recibir[i] = SPI.transfer(enviar[i]);
    digitalWrite(pinCs, HIGH);
    SPI.endTransaction();
}

void setup() {
    Serial.begin(115200);
    pinMode(CS_ACEL, OUTPUT);  digitalWrite(CS_ACEL, HIGH);    // todos los CS en alto antes de empezar
    pinMode(CS_FLASH, OUTPUT); digitalWrite(CS_FLASH, HIGH);
    SPI.begin();
}

void loop() {
    byte r[4];
    const byte pedidoAcel[] = { 0x80, 0x00 };                  // lectura del registro 0x00 (DEVID)
    hablar(CS_ACEL, AJ_ACEL, pedidoAcel, r, 2);
    Serial.print("ADXL345: 0x"); Serial.println(r[1], HEX);    // 0xE5

    const byte pedidoFlash[] = { 0x9F, 0, 0, 0 };              // identificación JEDEC
    hablar(CS_FLASH, AJ_FLASH, pedidoFlash, r, 4);
    Serial.print("Flash: 0x");
    Serial.print(r[1], HEX); Serial.print(r[2], HEX); Serial.println(r[3], HEX);
    delay(1000);
}
// Dos 74HC595 en cadena: MOSI -> SER del primero; QH' del primero -> SER del segundo.
// El pin PIN_LATCH va a RCLK de los dos y hace de CS: las salidas cambian cuando sube.
const byte PIN_LATCH = 8;
const SPISettings AJ_595(4000000, MSBFIRST, SPI_MODE0);

void escribir16(uint16_t valor) {
    SPI.beginTransaction(AJ_595);
    digitalWrite(PIN_LATCH, LOW);
    SPI.transfer(valor >> 8);              // el primer byte enviado llega al último de la cadena (bits 15..8)
    SPI.transfer(valor & 0xFF);            // el último queda en el primero (bits 7..0)
    digitalWrite(PIN_LATCH, HIGH);         // recién ahora cambian las 16 salidas, todas a la vez
    SPI.endTransaction();
}

void setup() {
    pinMode(PIN_LATCH, OUTPUT);
    digitalWrite(PIN_LATCH, HIGH);
    SPI.begin();
}

void loop() {
    for (byte i = 0; i < 16; i++) {      // un LED que recorre las 16 salidas
        escribir16(1u << i);
        delay(100);
    }
}

En hablar() se concentran las tres reglas: ajustes propios, un único CS y devolución con CS alto. Si todo el código del proyecto pasa por una función así, la mayoría de los errores de bus compartido desaparecen. En la cadena, lo importante es el orden: el byte alto va primero porque tiene que recorrer los dos registros; si se invirtiera, los dos grupos de ocho LED cambiarían de lugar.

Una nota para el ESP32: tiene varios controladores SPI independientes. Dos esclavos con modos o velocidades muy distintos pueden ir cada uno a su propio bus (cada uno con su SPIClass y pines propios), lo que evita cambiar los ajustes en cada conversación y permite atenderlos en paralelo.

08 · Analogía con programación

La cadena y el bus en Python

Dos modelos pequeños. El primero es la cadena en margarita: una lista de registros por la que los bytes entran de un lado y salen del otro. El segundo es el bus compartido: una función que dice qué lee el maestro según qué CS estén en bajo, incluido el caso de conflicto.

Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.

class Cadena:
    """Registros de desplazamiento de 8 bits en serie: MOSI -> 0 -> 1 -> ... -> n-1 -> MISO."""
    def __init__(self, n):
        self.regs = [0] * n

    def enviar(self, datos):
        salieron = []
        for byte in datos:
            salieron.append(self.regs[-1])            # lo que sale del último llega al maestro por MISO
            self.regs = [byte] + self.regs[:-1]       # cada registro pasa su byte al siguiente
        return salieron


def bus(cs_bajos, destino, ids):
    """Qué lee el maestro cuando pide el identificador de `destino` con los CS indicados en bajo."""
    activos = [d for d in ids if d in cs_bajos]
    if not activos:
        return 0xFF, "nadie responde: MISO flota y se lee 0xFF"
    if len(activos) == 1:
        d = activos[0]
        return ids[d], ("respuesta correcta" if d == destino else f"respondió {d}, no {destino}")
    distintos = 0
    for d in activos[1:]:
        distintos |= ids[d] ^ ids[activos[0]]
    return None, f"conflicto: los bits {distintos:08b} se disputan entre {', '.join(activos)}"


print("Cadena de 3 registros, se envían 3 bytes (0x11, 0x22, 0x33)")
c = Cadena(3)
print("  salieron por MISO:", [f"{b:#04x}" for b in c.enviar([0x11, 0x22, 0x33])])
print("  registros 0..2:   ", [f"{b:#04x}" for b in c.regs])
print("Se envían 4 bytes más (0x44, 0x55, 0x66, 0x77): uno de más")
print("  salieron por MISO:", [f"{b:#04x}" for b in c.enviar([0x44, 0x55, 0x66, 0x77])])
print("  registros 0..2:   ", [f"{b:#04x}" for b in c.regs])
print("Se envían solo 2 bytes (0xAA, 0xBB): faltó uno")
print("  salieron por MISO:", [f"{b:#04x}" for b in c.enviar([0xAA, 0xBB])])
print("  registros 0..2:   ", [f"{b:#04x}" for b in c.regs])

ids = {"acelerómetro": 0xE5, "flash": 0xEF, "BME280": 0x60}
print()
for bajos, destino in (({"flash"}, "flash"), ({"BME280"}, "flash"), (set(), "flash"), ({"flash", "acelerómetro"}, "flash")):
    valor, nota = bus(bajos, destino, ids)
    texto = f"{valor:#04x}" if valor is not None else "  ?? "
    print(f"CS bajos {sorted(bajos) or '[]'!s:32} -> {texto}  {nota}")

Resultado: tras enviar 0x11, 0x22 y 0x33 a una cadena de tres, los registros quedan [0x33, 0x22, 0x11]: el primer byte enviado terminó en el último dispositivo, y por MISO salió el contenido anterior (tres ceros). Con cuatro bytes más (uno de más), salen por MISO 0x11 0x22 0x33 0x44: los registros anteriores y, al final, un byte que acabábamos de enviar, que cruzó la cadena sin ser guardado por nadie; quedan [0x77, 0x66, 0x55]. Con un byte de menos (0xAA, 0xBB), el tercer registro no se renueva: quedan [0xBB, 0xAA, 0x77] y el último conserva un dato viejo. En el bus: con solo el CS de la flash en bajo se lee 0xEF; con el del BME280 se lee 0x60 (otro dispositivo, pero un dato aparentemente válido); sin ningún CS, 0xFF; y con dos CS a la vez hay conflicto en los bits 00001010.

Observá el caso del BME280: el programa recibe un identificador completamente plausible, de un dispositivo que no era el pedido. Por eso se verifica el valor esperado, no solo que haya llegado «algo».

09 · Exploración interactiva

Laboratorio: cadena, bus compartido y costo

El laboratorio tiene tres herramientas. La primera es una cadena en margarita de hasta cuatro registros: elegís cuántos dispositivos hay y cuántos bytes enviás. La segunda es un bus con tres esclavos y tres líneas CS que podés subir o bajar. La tercera calcula el tiempo de las transacciones típicas y los pines que cuesta cada forma de conectar los esclavos.

1. Cadena en margarita

Dispositivo 1 (junto al maestro)—
Dispositivo 2—
Dispositivo 3—
Dispositivo 4—
Salieron por MISO (en orden)—
Pulsos de SCLK—
Tiempo a 4 MHz—

Elegí cuántos dispositivos hay y cuántos bytes enviás.

2. Bus compartido con tres esclavos

Lee el maestro—
Valor leído—
Quién respondió—
Veredicto—

Elegí el destino y qué CS están en bajo, y presioná «Pedir el identificador».

3. Pines y tiempo

Bytes de la operación—
Duración—
Pines del maestro—
Cuenta—

Elegí la operación, la frecuencia y la forma de conexión.

En la herramienta 1 la cadena conserva su contenido entre envíos (para ver cómo se desplaza); «Poner la cadena en cero» la reinicia. En la 2 se supone que cada esclavo seleccionado entiende el pedido y responde con su propio identificador, y que un MISO sin ningún esclavo conectado lee 0xFF por un pull-up. La 3 cuenta solo los bytes del protocolo, sin pausas ni tiempos internos del dispositivo.

Recorrido guiado

  1. Presioná «Poner la cadena en cero» y después «Enviar a la cadena» con los valores iniciales (3 dispositivos, 3 bytes: 17, 34, 51). Los dispositivos quedan con 0x33, 0x22, 0x11: el primer byte enviado (0x11) llegó al último dispositivo. Por MISO salieron tres ceros y el envío tardó 24 pulsos (6,00 µs).
  2. Subí a 4 bytes y enviá de nuevo (con 17, 34, 51, 68). Por MISO salen 0x11 0x22 0x33 0x11: los tres contenidos anteriores y, al final, el primer byte que se acaba de enviar, que cruzó los tres dispositivos sin quedarse en ninguno. Los dispositivos quedan con 0x44, 0x33 y 0x22: un byte de más desplazó todo.
  3. Poné la cadena en cero y enviá solo 2 bytes a 3 dispositivos: el dispositivo 3 sigue en 0x00 y los otros dos tienen 0x22 y 0x11. Un byte de menos deja a alguien sin actualizar.
  4. Cambiá a 1 dispositivo y a 4 para ver la regla: siempre el último byte enviado queda en el dispositivo 1 y el primero, en el último.
  5. En la herramienta 2, con los valores iniciales (destino flash, solo su CS en bajo), el maestro lee 0xEF: «Correcto».
  6. Bajá solo el CS del BME280 (subí el de la flash) y pedí de nuevo: lee 0x60. Veredicto «Dispositivo equivocado»: el valor parece válido, pero no es el que correspondía.
  7. Dejá todos los CS en alto: el maestro lee 0xFF («Sin respuesta»), el síntoma clásico de un CS que no baja.
  8. Bajá a la vez el CS del acelerómetro y el de la flash: 1110?1?1 y «Conflicto en MISO». Los bits donde 0xE5 y 0xEF coinciden son válidos; en los otros dos, las salidas se pelean. Con el acelerómetro y el BME280 el conflicto afecta a tres bits.
  9. En la herramienta 3, con 3 esclavos y un CS por cada uno, hacen falta 6 pines; con decodificador, 5; en cadena, 4. Subí a 8 esclavos: 11, 6 y 4 pines. Observá la nota sobre el decodificador cuando se ocupan todas las salidas.
  10. Elegí «Leer un bloque de 512 bytes de una SD»: son 522 bytes como mínimo. A 4 MHz toma 1,04 ms; a 25 MHz, 167 µs. Con 1 MHz, cercano a la velocidad de arranque, sube a 4,18 ms.
Desafío: cuánto tarda actualizar una cadena larga

Una tira de 8 controladores MAX7219 usa 16 bits por dispositivo y actualiza cuando CS sube. ¿Cuántos pulsos hacen falta para cambiar una fila en los ocho, y cuánto tarda a 10 MHz? Cada dispositivo recibe 16 bits, así que la cadena necesita 8 × 16 = 128 pulsos: a 10 MHz son 12,8 µs. El costo crece linealmente con la cantidad de dispositivos: con 64 el tiempo es 8 veces mayor. A cambio, el maestro sigue usando los mismos cuatro pines.

10 · Cuando algo no responde

Diagnóstico de un bus con varios esclavos

Los problemas de un bus compartido tienen su propia familia de síntomas. La estrategia general es la del tema anterior (bajar la frecuencia, leer un registro de identificación), con un paso previo: probar los dispositivos de a uno. Se desconectan los demás (o se mantienen sus CS en alto) y se prueba el primero; cuando anda, se agrega el segundo, y así.

Síntomas típicos de un bus SPI con varios esclavos.
SíntomaCausa probable
Cada dispositivo funciona solo; juntos, uno fallaUno no suelta MISO al estar deseleccionado, o dos CS están en bajo a la vez
Un dispositivo devuelve datos del otroEl CS equivocado, o dos CS que comparten pin por un error de cableado
Funciona al arrancar y falla después de usar la SDLa tarjeta no soltó MISO: enviar 0xFF con CS alto; o agregar un buffer triestado
Al agregar un dispositivo, los demás fallan a velocidad altaCapacidad y longitud del bus: bajar la frecuencia o acortar el cableado
Falla solo al encender o resetearCS flotantes durante el arranque: poner pull-ups y llevar los CS a alto antes de SPI.begin()
Dos dispositivos con modos distintos, uno se desordenaFalta beginTransaction con los ajustes del dispositivo en cada acceso
Una cadena muestra los datos corridos de a un dispositivoCantidad de bytes distinta de la cantidad de dispositivos, o orden invertido
La cadena entera falla de golpeUn dispositivo desconectado: se corta el anillo; revisar el cable entre salida y entrada

Una herramienta potente es el analizador lógico con todas las líneas CS más SCLK, MOSI y MISO. Ver qué CS baja y cuándo, y comprobar que nunca haya dos a la vez, resuelve en minutos lo que de otro modo toma horas de pruebas.

11 · Comprensión

Confusiones frecuentes

«Con SPI se puede conectar cualquier cantidad de esclavos sin costo»

Cada esclavo extra cuesta un pin de CS, suma capacidad al bus (lo que reduce la frecuencia máxima) y complica el programa. Para muchos dispositivos conviene un decodificador, una cadena (si el dispositivo lo permite) o pasar a I²C.

«Si el CS de un esclavo está alto, no puede afectar al bus»

Solo si el esclavo pone MISO en alta impedancia. Los que no lo hacen contaminan la línea aunque estén deseleccionados. Por eso un módulo barato puede funcionar solo y fallar cuando se le agrega otro.

«Una cadena en margarita sirve para cualquier dispositivo SPI»

Solo para los que pasan los bits que no son suyos al siguiente (registros de desplazamiento, controladores de LED). Una memoria flash o una tarjeta SD tienen que estar conectadas con su propio CS.

«En una cadena, el primer byte que envío va al primer dispositivo»

Es al revés: el primer byte enviado es el que más se desplaza y termina en el último dispositivo. El último byte enviado queda en el primero. Equivocarse en el orden intercambia las posiciones.

«Un dato válido viene del dispositivo correcto»

No necesariamente: si se seleccionó otro dispositivo, la respuesta puede ser plausible. Por eso se verifica el valor esperado (identificador de fabricante, de dispositivo) y no solo que haya llegado un byte.

«La tarjeta SD puede arrancar a la velocidad máxima»

Durante la identificación la especificación limita el reloj a 400 kHz. Recién después de que la tarjeta está lista se sube a 25 MHz o más. Muchos fallos de «no inicializa» son simplemente un reloj demasiado rápido al principio.

12 · Práctica guiada

Predecí antes de comprobar

1. Contar pines

Un proyecto tiene 6 esclavos SPI. ¿Cuántos pines necesita el maestro con un CS por esclavo y con un decodificador?

Ver solución

Un CS por esclavo: 3 compartidos + 6 = 9 pines. Con decodificador: ⌈log2 6⌉ = 3 líneas de dirección, así que 3 + 3 = 6 pines. Comprobalo en la herramienta 3.

2. Orden en una cadena

Tres registros en cadena deben quedar con los valores 0x10 (dispositivo 1), 0x20 (dispositivo 2) y 0x30 (dispositivo 3). ¿En qué orden se envían los bytes?

Ver solución

El primer byte enviado termina en el último dispositivo: se envía primero 0x30, luego 0x20 y al final 0x10. Es el orden inverso al de las posiciones.

3. Un byte de menos

Una cadena de 4 dispositivos recibe solo 3 bytes en una transacción. ¿Qué dispositivo no se actualiza y qué contiene?

Ver solución

Los tres bytes enviados llegan a los dispositivos 1, 2 y 3 (el último enviado, al 1). El dispositivo 4 (el último de la cadena) recibe lo que antes tenía el 3: se desplazó, pero no se renovó con un dato nuevo. Es un error silencioso: no hay aviso, solo una pantalla o salidas corridas.

4. Dos CS a la vez

Por un error de programa, el CS del acelerómetro (ID 0xE5) y el del BME280 (ID 0x60) están en bajo juntos. ¿Qué bits de la lectura se disputan?

Ver solución

0xE5 = 11100101 y 0x60 = 01100000; el XOR es 10000101. Se disputan tres bits (el 7, el 2 y el 0) y el resultado es «?1100?0?». En los demás coinciden los dos dispositivos. En hardware real, además de datos erróneos, hay corriente de cortocircuito en los bits conflictivos.

5. El tiempo de un bloque de SD

Leer un bloque son 522 bytes como mínimo. ¿Cuánto toma a 25 MHz? ¿Y a 400 kHz, la velocidad de arranque?

Ver solución

522 × 8 = 4176 pulsos. A 25 MHz: 4176 / 25 000 000 ≈ 167 µs. A 400 kHz: 4176 / 400 000 = 10,44 ms, unas 62 veces más. Por eso se sube la frecuencia apenas la tarjeta está inicializada.

6. Leer un MCP3008

El maestro envía 0x01, 0xA0 y 0x00 al MCP3008 y recibe como segundo y tercer byte 0x02 y 0x9C. ¿De qué canal es y cuál es el valor?

Ver solución

El segundo byte enviado, 0xA0 = 1010 0000, tiene el bit 7 en 1 (single-ended) y el canal en los bits 6 a 4: 010 = canal 2. El valor es ((0x02 & 3) << 8) | 0x9C = 512 + 156 = 668 de 1023. Con una referencia de 5 V, equivale a unos 3,27 V.

13 · Síntesis

Ideas para recordar

  • Varios esclavos comparten SCLK, MOSI y MISO; cada uno tiene su propio CS, y el maestro baja un único CS a la vez.
  • Un esclavo deseleccionado debe poner MISO en alta impedancia; si no lo hace, contamina las lecturas de los demás y hace falta un buffer triestado.
  • Los CS arrancan en alto (con pull-up), se configuran antes de iniciar el bus, y cada conversación aplica sus ajustes con beginTransaction.
  • En la cadena en margarita, los dispositivos se comportan como un único registro largo: se envían tantos bytes como dispositivos, y el primer byte enviado termina en el último.
  • La cadena ahorra pines (siempre cuatro) pero solo sirve para dispositivos que pasan los datos, aumenta el tiempo con cada integrante y se corta si uno falla.
  • Un decodificador (74HC138) reduce los pines de selección a ⌈log2 N⌉, a costa de un circuito más y de cuidar los estados intermedios.
  • Cada familia de dispositivos tiene su protocolo: la SD arranca a menos de 400 kHz con 74 pulsos en alto y bloques de 512 bytes; la flash necesita habilitar la escritura, esperar al bit de ocupado y subir CS al final; el conversor devuelve la muestra mezclada en dos bytes; la pantalla agrega la línea D/C.
  • Se verifica siempre el identificador esperado: un dato plausible puede venir del dispositivo equivocado.

¿Listo para el siguiente tema?

Antes de avanzar, calculá cuántos pines necesita un maestro para 5 esclavos con un CS por cada uno y con un decodificador, escribí el orden de los bytes para cargar 0xAA, 0xBB y 0xCC en tres registros en cadena (dispositivos 1 a 3), y explicá qué ocurre si se bajan a la vez dos CS en un bus compartido. Si podés justificar las tres respuestas, tenés dominado el uso de un bus SPI con varios dispositivos.

En el próximo tema cerramos el bloque de SPI con un taller: leer un conversor o una memoria, armando la transacción byte a byte y verificándola con un analizador lógico.