spi-bus-nanoframework

Bus SPI en nanoFramework: comunicación rápida

  • 4 min

El bus SPI es un protocolo de comunicación síncrono pensado para transferir datos a alta velocidad.

Si comparamos el I2C con una carretera comarcal (fiable, dos carriles, velocidad moderada), el SPI es una autopista alemana sin límite de velocidad.

Mientras que I2C suele funcionar a 100kHz o 400kHz, el SPI puede alcanzar fácilmente los 10MHz, 40MHz o incluso 80MHz. Por eso es el estándar elegido para dispositivos que manejan grandes volúmenes de datos, como:

  • Pantallas LCD/TFT/OLED a color.
  • Lectores de tarjetas SD.
  • Módulos Ethernet (como el ENC28J60).
  • Lectores RFID/NFC (como el RC522).

En esta entrada aprenderemos a domar esta velocidad usando la clase SpiDevice.

Para este tutorial necesitas instalar el paquete NuGet: nanoFramework.System.Device.Spi

Las líneas del bus SPI

La velocidad tiene un precio: cables. El SPI necesita 4 líneas para funcionar (frente a las 2 del I2C).

  • SCK (Serial Clock): El reloj que marca el ritmo (generado por el Maestro).
  • MOSI (Master Out Slave In): Datos que salen del microcontrolador hacia el sensor.
  • MISO (Master In Slave Out): Datos que vienen del sensor hacia el microcontrolador.
  • CS / SS (Chip Select): ¡El más importante! Es un pin individual para cada dispositivo. Si conectas 3 sensores SPI, compartirán SCK, MOSI y MISO, pero necesitarás 3 pines CS distintos.

Comunicación full-duplex

Una característica única del SPI es que es Full-Duplex. Imagina dos personas pasándose tarjetas de visita al mismo tiempo. En el mismo ciclo de reloj, el Maestro envía un bit al Esclavo Y el Esclavo envía un bit al Maestro. Siempre se intercambian datos, aunque a veces uno de los dos envíe “basura” (dummy bytes) solo para empujar los datos del otro.

Configuración en nanoFramework

En el ESP32 tenemos dos buses SPI hardware disponibles para el usuario: VSPI (Bus 1) y HSPI (Bus 2).

Por defecto, en la mayoría de ESP32 DevKits, el VSPI (que solemos llamar SPI1) está en:

  • MOSI: GPIO 23
  • MISO: GPIO 19
  • SCK: GPIO 18
  • CS: un GPIO disponible, habitualmente el 5

La clase protagonista es SpiDevice. Primero configuramos la conexión con SpiConnectionSettings.

using System.Device.Spi;

// Configuración
// 1. Bus ID: consulta el mapeo del target
// 2. Chip Select Pin: GPIO 5
SpiConnectionSettings settings = new SpiConnectionSettings(1, 5);

// Parámetros avanzados (Cruciales leerlos del Datasheet del sensor)
settings.ClockFrequency = 1000000; // 1 MHz (Velocidad)
settings.Mode = SpiMode.Mode0;     // Polaridad y Fase del reloj (Mode0 es el más común)

// Crear el dispositivo
SpiDevice dispositivo = SpiDevice.Create(settings);
Copied!

El Modo SPI (0, 1, 2, 3) Esto define cuándo se leen los datos (si cuando el reloj sube o baja). Si configuras mal el modo, leerás basura. El 90% de dispositivos usan Mode0, pero siempre verifica el datasheet.

Transferir datos: Write, Read y TransferFullDuplex

Tenemos tres formas de comunicarnos:

Solo escribir con Write

Usado para enviar comandos o configurar registros (ej. enviar píxeles a una pantalla).

byte[] comando = new byte[] { 0x01, 0xFF };
dispositivo.Write(comando);
Copied!

Solo leer con Read

Usado para leer datos cuando no necesitamos enviar nada específico (aunque internamente el SPI siempre envía ceros para poder leer).

byte[] buffer = new byte[4];
dispositivo.Read(buffer);
Copied!

Intercambiar con TransferFullDuplex

La forma “pura” del SPI. Envías un array y recibes otro del mismo tamaño simultáneamente.

byte[] enviar = new byte[] { 0xAA, 0xBB };
byte[] recibir = new byte[enviar.Length];

dispositivo.TransferFullDuplex(enviar, recibir);

// Ahora 'recibir' contiene lo que el sensor nos contestó MIENTRAS enviábamos AA y BB.
Copied!

Ejemplo: un byte dummy

Supongamos que queremos leer un valor de un sensor RFID. Normalmente funciona así:

  1. Bajamos la línea CS (automático por SpiDevice).
  2. Enviamos la dirección del registro que queremos leer (ej. 0x15).
  3. El sensor necesita relojes para responder, así que enviamos un byte “vacío” (0x00) para “empujar” la respuesta hacia nosotros.
// Queremos leer el registro 0x15
// Byte 0: Dirección 0x15
// Byte 1: 0x00 (Dummy para dar tiempo a recibir el dato)
byte[] writeBuffer = new byte[] { 0x15, 0x00 };
byte[] readBuffer = new byte[2];

dispositivo.TransferFullDuplex(writeBuffer, readBuffer);

// readBuffer[0] -> Basura (lo que recibimos mientras enviábamos la dirección)
// readBuffer[1] -> ¡El dato real del registro 0x15!
byte datoValido = readBuffer[1];
Copied!

Este baile de bytes es lo que confunde al principio, pero es la base de la velocidad del SPI.

Drivers y dispositivos gráficos

Al igual que con I2C, la gran ventaja de nanoFramework es su ecosistema.

Si quieres usar una pantalla TFT ILI9341 o un lector RC522, no escribas el protocolo SPI a mano. Usa los drivers NuGet.

Por ejemplo, para un lector de tarjetas:

  1. Instala nanoFramework.Iot.Device.Mfrc522.
  2. Inicialízalo pasándole el SpiDevice ya configurado.

El driver se encargará de toda la gestión de registros, comandos y tiempos, aprovechando la velocidad del bus SPI que hemos configurado.