En este tutorial profundizamos en la Interfaz Periférica en Serie, abreviada SPI.
Aprenderás las siguientes partes:
- Configuración del protocolo
- Transmisión de datos
- Ventajas y desventajas
- Al final de este tutorial encontrará dos ejemplos de la comunicación SPI entre dos microcontroladores Arduino y un Arduino y un microcontrolador basado en ESP8266.
Comparación de protocolos de comunicación de microcontroladores desde el punto de vista de SPI
SPI fue inventado por Motorola en 1970. La siguiente tabla compara los protocolos de comunicación I2C, SPI y UART según diferentes categorías.
Descripción | I2C | SPI | UART | |
---|---|---|---|---|
Inventado por | 1982 por Philips Semiconductor | 1970 por Motorola | 1960 por Gordon Bell en Digital Equipment Corporation | |
Transferencia de datos síncrona | Se necesita una línea de reloj para sincronizar la comunicación | Verdadero | Verdadero | Falso |
Transferencia de datos asíncrona | En lugar de una señal de reloj, el propio vapor de datos contiene señales de inicio y de parada | False | False | True |
Rendimiento | De 10.000 a 1.000.000 de bits/s | Hasta 10.000.000 de bits/s | Hasta 115.200 bits/s | |
El esclavo necesita una dirección única | Verdadero | Falso | Falso | |
Número de pines necesarios | 2 | 4 | 2 | |
Protocolo de comprobación de errores | Verdadero | Falso | Verdadero | |
Multi-maestro | Puede tener varios maestros que controlen uno o varios esclavos | Verdadero | Falso | Falso |
Multi-esclavo | Puede conectar varias salves a un solo maestro | Verdadero | Verdadero | Falso |
Conmutación de paquetes | Los datos transferidos se agrupan en paquetes/mensajes, compuestos por una cabecera y una carga útil | Verdadero | Falso | Falso |
Single-ended | Los datos se transfieren por un solo cable | True Datos en serie (SDA) | Falso Master in Slave Out (MISO) Master Out Slave In (MOSI) | False |
Conexión serie | Los datos se transfieren bit a bit a lo largo de un solo cable | Verdadero | Verdadero | Verdadero |
Al igual que I2C, SPI es un protocolo de datos en serie síncrono y, por lo tanto, necesita un reloj para sincronizar la comunicación entre los dispositivos maestro y esclavo.
Se recomienda SPI cuando la velocidad de comunicación necesita apostar muy rápido. El rendimiento máximo es de hasta 10,000,000 bps y mucho más rápido que I2C y UART. La velocidad que puede utilizar el microcontrolador se basa en la frecuencia del chip (frecuencia de reloj). La mayoría de los microcontroladores Arduino tienen una velocidad de chip de 16 MHz, pero se recomienda que establezca la velocidad de comunicación a la mitad de la velocidad de chip de 8 MHz (8.000.000 bps). ESP8266 tiene una velocidad de chip más alta que el microcontrolador Arduino 80MHz y también se recomienda reducir la velocidad de comunicación para el microcontrolador ESP8266.
Para configurar el reloj SPI en relación con el reloj del sistema, utilice la función setClockDivider (). Por ejemplo, si desea una velocidad SPI de 8 Mbps y su velocidad de chip es de 16 MHz, utilice la siguiente función con el parámetro setClockDivider (SPI_CLOCK_DIV2). Están disponibles los siguientes divisores: 2, 4, 8, 16, 32, 64 o 128.
Pero en la práctica, depende de usted decidir si desea seleccionar manualmente la velocidad de comunicación a través de la función, porque el Arduino selecciona automáticamente la velocidad de comunicación igual o menor que el parámetro según el dispositivo con la velocidad de comunicación más débil.
En un sistema SPI, siempre tiene un solo dispositivo maestro. Por lo tanto, SPI no puede construir un sistema multimaestro. Si desea comunicarse entre, por ejemplo, un Arduino Uno y un NodeMCU, un dispositivo debe ser el maestro y el otro el esclavo. Pero SPI puede administrar varios dispositivos esclavos, cada uno de los cuales está conectado al dispositivo maestro a través de la línea llamada Slave Select (SS). Como la línea de selección de esclavos conecta cada esclavo al maestro, no hay una dirección única para cada esclavo como para la comunicación I2C.
Hay 3 líneas que conectan el dispositivo maestro a todos los dispositivos esclavos
- MISO (Master in Slave Out): la línea esclava para enviar datos al maestro
- MOSI (Master Out Slave In): la línea maestra para enviar datos a periféricos
- SCK / SCLK (reloj serial): pulso desde el dispositivo maestro para sincronizar la transmisión de datos
Y 1 línea maestra para cada dispositivo
- SS (Selección de esclavo) Una línea específica para cada dispositivo que el maestro puede activar o desactivar el esclavo específico. Esto hace posible compartir la línea MISO, MOSI y SCK entre varios dispositivos. El pin SS puede ser cualquier pin digital de su microcontrolador
- El pin SS es BAJO → La comunicación del esclavo con el maestro está habilitada
- El pin SS es ALTO → La comunicación del esclavo con el maestro está deshabilitada
Si quieres saber cuáles son los pines MISO, MOSI y SCK para diferentes microcontroladores, puedes encontrar esta información en los artículos relacionados con pinout: Arduino Mega, Arduino Uno, Arduino Nano, ESP8266 NodeMCU o mejor si descargas la hoja de datos eBook del microcontrolador.
Configuración de SPI para habilitar la comunicación SPI
Si queremos iniciar una comunicación, primero debemos activar SPI con el siguiente código Arduino SPI.beginTransaction (SPISettings (8000000, MSBFIRST, SPI_MODE0));
Verá que al iniciar una comunicación SPI, hay en total 3 parámetros que se pueden configurar si queremos controlar los parámetros manualmente. Estos parámetros SPI no se eliminan cuando la comunicación SPI está deshabilitada con SPI.endTransaction (). En su lugar, puede cambiar la configuración de SPI anulando la configuración a través de la función SPISettings (). Siempre existe la opción de permitir que el microcontrolador establezca automáticamente los parámetros SPI, que vemos en los ejemplos de este tutorial.
Configuración de la frecuencia del reloj para SPI
El primer parámetro es la velocidad de reloj SPI que se establece en 8 Mbps en este ejemplo.
Configuración de transferencia de datos para SPI
El segundo parámetro es el desplazamiento de datos que define qué bit se transfiere primero. Hay 2 opciones:
- Bit más significativo (MSB) → MSBFIRST: el bit 8 es el primer bit que se transfiere a través de SPI
- Último bit significativo (LSB) → LSBFIRST: el bit 1 es el primer bit que se transfiere a través de SPI
Bit 8 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1
2⁷ | 2⁶ | 2⁵ | 2⁴ | 2³ | 2² | 2¹ | 2⁰ | ||
MSB> | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 1 |
La mayoría de los chips SPI utilizan el primer orden de datos MSB.
Modos de transmisión para SPI
Hay un total de 4 modos de transmisión diferentes dependiendo de la combinación de 2 parámetros de transmisión:
- Fase de reloj (ACSP)
- CPHA = 1: muestras del flanco ascendente del pulso de reloj
- CPHA = 0: muestras del flanco descendente del pulso de reloj
- Polaridad de reloj (CPOL)
- Reloj no activo en estado alto (CPOL = 1): Cada ciclo consta de un pulso de 0. El flanco ascendente es un flanco descendente y el flanco de salida es un flanco ascendente.
- Reloj inactivo en estado bajo (CPOL = 0): cada ciclo consta de un pulso de 1. El flanco ascendente es un flanco ascendente y el flanco posterior es un flanco descendente.
La siguiente tabla muestra los 4 modos de transmisión diferentes.
Modo de polaridad de reloj (CPOL) Fase de reloj (CPHA) Captura de datos de borde de salida
SPI_MODE0 | 0 | 0 | Caída | Creciente |
SPI_MODE1 | 0 | 1 | Creciente | Caída |
SPI_MODE2 | 1 | 0 | Creciente | Caída |
SPI_MODE3 | 1 | 1 | Caída | Creciente |
Para la mayoría de los dispositivos, SPI_MODE0 es el modo de transmisión predeterminado. Las siguientes imágenes muestran los cuatro modos diferentes. Ves la línea SCK y cuando el reloj está muestreando.
Transmisión de datos SPI
Los datos que se transfieren a través de la comunicación SPI se almacenan en registros de desplazamiento de 8 o 16 bits que tienen la misma longitud para el dispositivo maestro y esclavo. Los datos generalmente se desplazan con el bit más significativo primero, definido por los parámetros SPI, y se almacenan como el nuevo último bit significativo del registro de desplazamiento. El módulo SPI no tiene búfer en la dirección de transmisión y es único en la dirección de recepción. Los siguientes 6 pasos describen la transmisión de datos SPI:
- Primero, el dispositivo maestro configura los parámetros SPI (frecuencia de reloj, compensación de datos, modo de transmisión).
- El maestro selecciona el dispositivo esclavo tirando de la línea SS LOW (del 1 lógico al 0 lógico).
- Los siguientes pasos se realizan todos al mismo tiempo durante un ciclo de reloj en una transmisión de datos full duplex:
- En el borde del reloj, el maestro envía el primer bit al esclavo a través de la línea MOSI. El esclavo lee el mensaje entrante.
- En el borde del reloj, el esclavo envía el primer bit al maestro a través de la línea MISO. El maestro lee el mensaje entrante.
- El intercambio de datos se realiza cuando se transmite el registro de desplazamiento completo.
- A continuación, se copia el registro de desplazamiento completo en el búfer de recepción y se vuelven a cargar los registros de desplazamiento. El proceso de transmisión de datos comienza de nuevo.
- Si la transmisión está completa, el maestro detiene la señal del reloj y deja de tirar de la línea SS LOW.
Ventajas y desventajas de SPI
Ventajas
- Comunicación full duplex (I2C no es comunicación síncrona)
- Flujo más alto que I2C
- No limitado a palabras de 8 bits
- Menor consumo de energía que I2C porque no se requiere resistencia pull-up
- Los esclavos no necesitan una dirección única con respecto a I2C
Desventajas
- Requiere más pines que I2C
- Sin protocolo de comprobación de errores
- Muchas variaciones diferentes ya que no se define ningún estándar formal
Ejemplos de comunicación SPI
La siguiente tabla le brinda una descripción general de todos los componentes y partes que utilicé para este tutorial. Recibo comisiones por compras realizadas a través de los enlaces de esta tabla.
Arduino Uno | Amazon | AliExpress |
Arduino Mega | Amazon | AliExpress |
ESP8266 NodeMCU | Amazon | AliExpress |
Osciloscopio USB | Amazon | Aliexpress |
También les doy dos ejemplos de comunicación SPI porque es bueno conocer la teoría y cómo funciona algo. Pero al final quieres llevar a cabo proyectos y necesitas ejemplos para ver cómo se hace la comunicación SPI. La primera es la comunicación entre el Arduino Uno como maestro y un Arduino Mega como esclavo. En el segundo ejemplo, estamos estableciendo una comunicación SPI entre un ESP8266 NodeMCU como maestro y un Arduino Uno como esclavo.
Comunicación SPI: Arduino Uno a Arduino Mega
Queremos comunicarnos a través de SPI entre un Arduino Uno como maestro y un Arduino Mega como esclavo. La siguiente imagen muestra el cableado. También encontrará los pines utilizados en la tabla de descripción general de pines en los artículos especiales para Arduino Uno y Arduino Mega.
En la siguiente parte, avanzamos paso a paso por el guión del programa.
Script para dispositivo maestro: Arduino Uno
#include "SPI.h" void setup() { digitalWrite(SS, HIGH); // disable Slave Select SPI.begin(); SPI.setClockDivider(SPI_CLOCK_DIV4);//divide the clock by 4 } void loop() { char c; digitalWrite(SS, LOW); // enable Slave Select for (const char * p = "Hello Megar" ; c = *p; p++) { SPI.transfer(c); } digitalWrite(SS, HIGH); // disable Slave Select delay(2000); }
Primero necesitamos incluir la biblioteca SPI para nuestro ejemplo. En la función de configuración, primero deshabilitamos el pin de selección de esclavo tirando de la línea SS HIGH. A continuación, inicializamos la comunicación SPI con la función SPI.begin. La última parte de la función de configuración es opcional. Dividimos el reloj por 4 para reducir la tasa SPI de 16 MHz a 16/4 = 4 MHz.
La función de bucle comienza definiendo una variable de carácter cy activa la línea de selección esclava arrastrando la línea LOW. Queremos enviar la cadena «Hello Mega r» y usar r como bandera para indicar al dispositivo esclavo que la transferencia de caracteres se ha completado. Por lo tanto, la cadena se divide en sus letras en el bucle for y se transfiere a través de SPI. Cuando se completa la transmisión, el pin selector de esclavo se desactiva y esperamos 2 segundos hasta que volvamos a enviar el mensaje del maestro al esclavo.
Ahora puede descargar el script Arduino Uno como maestro en el microcontrolador.
Script para dispositivo esclavo: Arduino Mega
#include "SPI.h" char buff [50]; volatile byte indx; volatile boolean process; void setup (void) { Serial.begin (9600); pinMode(MISO, OUTPUT); // have to send on master in so it set as output SPCR |= _BV(SPE); // turn on SPI in slave mode indx = 0; // buffer empty process = false; SPI.attachInterrupt(); // turn on interrupt } ISR (SPI_STC_vect) // SPI interrupt routine { byte c = SPDR; // read byte from SPI Data Register if (indx < sizeof buff) { buff [indx++] = c; // save data in the next index in the array buff if (c == 'r') //check for the end of the word process = true; } } void loop (void) { if (process) { process = false; //reset the process Serial.println (buff); //print the array on serial monitor indx= 0; //reset button to zero } }
El script esclavo también comienza con la inclusión de la biblioteca SPI. También necesitamos definir 3 variables. Las variables de mejora almacenan los valores entrantes a través de SPI. La variable indx almacena el índice de 8 bits y la variable de proceso almacena el estado actual de la transmisión.
En la función de configuración, la velocidad en baudios se establece en 9600 para mostrar la salida SPI en el monitor en serie. El pin MISO se define como una salida para recibir datos del maestro. El SPI se configura en modo esclavo y ambas variables se configuran con sus valores predeterminados. Las interrupciones también están habilitadas para la comunicación SPI.
Después de la función de configuración, se define la rutina de interrupción SPI. Primero, los datos SPI se leen del registro de datos interno y se almacenan en forma binaria. Hasta que el búfer esté completamente lleno con 50 caracteres, bit a bit se almacena en el búfer desde el registro de datos SPI hasta que se encuentre la bandera » r» para el final del carácter. Si el mensaje se transmite por completo, la variable de proceso se establece en VERDADERO.
La función de bucle espera a que la variable de proceso sea VERDADERA, luego primero restablece la variable e imprime el búfer completo en el monitor en serie. La última parte es restablecer el índice del búfer para llenar el búfer desde el principio.
La siguiente imagen muestra la salida serial del Arduino Mega como esclavo. La palabra «Hello Mega» se transfiere a través de SPI.
Comunicación SPI: ESP8266 NodeMCU a Arduino Uno
Este segundo ejemplo usa el ESP8266 como maestro porque para el script esclavo, vimos en el primer ejemplo que la función del IRS (spi_stc_vect) se usa porque SPI.attachInterrupt (); La instrucción activa la interrupción. Desafortunadamente, la biblioteca SPI ESP8266 no tiene un método attachInterrupt () y, por lo tanto, no es fácil acceder al registro de datos SPI para NodeMCU. Por lo tanto, no estoy usando ESP8266 NodeMCU como dispositivo esclavo.
La siguiente imagen muestra la conexión entre el ESP8266 NodeMCU y el Arduino Uno.
No hay ningún problema para conectar Arduino y NodeMCU directamente aunque el voltaje de funcionamiento para Arduino es de 5V y para NodeMCU es de 3.3V porque los pines digitales de NodeMCU son tolerantes a 5V y están protegidos contra sobretensiones.
El código del programa no depende del microcontrolador específico, por lo que puede usar el script maestro y esclavo del ejemplo anterior. Pero quiero mostrarles un ejemplo muy breve del script maestro del microcontrolador ESP8266.
#include "SPI.h" char buff[]="Hello Slaven"; void setup() { SPI.begin(); } void loop() { for(int i=0; i<sizeof buff; i++) { SPI.transfer(buff[i]); } delay(1000); }
Al principio incluimos la biblioteca SPI y la matriz de caracteres que contiene los caracteres que queremos transferir al dispositivo esclavo. En la función de configuración, solo inicializamos la comunicación SPI. En la función de bucle, transferimos la matriz de caracteres letra por letra a través de SPI, luego esperamos 1 segundo. Este es el script maestro más corto que puede crear y usa solo la configuración predeterminada para la comunicación. SPI.
Para el script esclavo estamos usando lo mismo que para Arduino Mega.
La siguiente imagen muestra la salida en serie para el ejemplo de comunicación SPI entre NodeMCU y Arduino Uno.
Visualice y decodifique la comunicación SPI con un osciloscopio
También es posible que falle la comunicación SPI. En este caso, es muy útil saber si la comunicación SPI en la línea MOSI que es la salida del maestro es correcta o no. La mayoría de los osciloscopios tienen la función de decodificar protocolos de comunicación como SPI. Tengo un osciloscopio USB de 2 canales que está vinculado en la parte superior de este artículo en la lista de piezas.
Decodifiqué el primer ejemplo en el que enviamos «Hello Mega» del Arduino Uno al Arduino Mega. Solo conecto la señal MISO y Clock a un canal de mi osciloscopio cada uno. En la siguiente imagen se ve la señal decodificada mostrando que la salida de Arduino Uno es correcta, enviando «Hola Mega».
Haga clic en la imagen para ampliar.
Conclusión
En este tutorial, primero comparamos el protocolo de comunicación SPI con I2C y UART. También discutimos la configuración de SPI y cómo se transmiten los datos entre diferentes dispositivos. Al final de este artículo, analizamos dos ejemplos y cómo se puede decodificar y depurar la comunicación SPI con un osciloscopio.
Si tiene alguna pregunta sobre la comunicación SPI, utilice la sección de comentarios a continuación para hacer sus preguntas. Les responderé lo antes posible.