Protocolo SPI emulado por software (Bit Banging).

Protocolo SPI


Por ahí leí algo así: El protocolo SPI es aquel que uno diseñaría si le dieran solo 10 minutos para ello. Y sinceramente, sin desmerecer el protocolo, concuerdo en que es bastante sencillo.

Es un protocolo serial y sincrónico en el que un maestro puede comunicarse con uno o mas esclavos. Posee 3 lineas para la comunicación (MOSI, MISO, CLK) más 1 para la selección de la tarjeta con la que se comunicará (CS). Por último existen 4 modos en los que cada uno especifica solamente en que polaridad y flanco se leen o envían los datos.

Detalle de las lineas:

MOSI: Siglas que significan Salida del Maestro - Entrada del esclavo (Master Output - Slave Input). Por esta linea el Maestro (en mi caso el microcontrolador) envía los datos hacia el esclavo (tarjeta SD).

MISO: Entrada del Maestro - Salida del Esclavo (Master Input - Slave Output). Es la linea por donde el maestro recibe los datos que le envía el esclavo.

CLK: Linea de reloj. Es la encargada de generar los pulsos de reloj que indicaran cuando un dato (bit) es valido. Osea cuando tiene que ser leído un bit o cuando hay que preparar otro para su lectura. Esta linea es manejada exclusivamente por el maestro.

CS: Selección del Esclavo (Chip Select). Por medio de esta linea el maestro habilita uno de los esclavos presentes (Si es que hay mas de 1). Es esencial para evitar que dos esclavos, por ejemplo, transmitan los datos al mismo tiempo creando colisiones (errores en la linea) con lo que se corromperían los datos.

Tanto las lineas MISO y MOSI son llamadas también DO (Data Out) y DI(Data In). En lo personal prefiero MISO y MOSI ya que a mi me crea menos confusiones.

La comunicación se establece cuando la linea CS se hace baja (!CS). A partir de acá, como dijimos, hay 4 modos que son los siguientes:

Modo 0: (Y el usado por las tarjetas SD).
Modo 0.jpg

Datos se leen en el flanco de subida del pulso de reloj.
El micro mantiene el CLK en bajo. Pone el bit a transmitir, sube la linea CLK en alto y luego lee los datos.


Modo 1:
Modo 1.jpg

Datos se leen en el flanco de bajada del pulso de reloj.
El micro mantiene el CLK en bajo. Sube la linea de CLK, pone el bit a transmitir, baja la linea CLK y luego lee los datos.


Modo 2:
Modo 2.jpg

Datos se leen en el flanco de bajada del pulso de reloj.
El micro mantiene el CLK en alto. Pone el bit a transmitir, baja la linea CLK y luego lee los datos.


Modo 3:
Modo 3.jpg

Datos se leen en el flanco de subida del pulso de reloj.
El micro mantiene el CLK en alto. Baja la linea de CLK, pone el bit a transmitir, sube la linea CLK y luego lee los datos.

Básicamente el modo 0 y el 2, como así también el 1 y el 3, son similares. La única diferencia es en que nivel está la linea de CLK al mandar el primer bit para hacer el desplazamiento de los datos. Por ejemplo, en el modo 0, el primer bit a transmitir se carga antes de subir la linea de CLK.
Como se puede apreciar en las imágenes los datos se envían empezando por el bit mas significativo (MSB).

La función en C seria así (para el Modo 0):

C:
unsigned char spi_write(unsigned char byte)
{
      unsigned char i, SPI_Valor = 0;
 
      for (i = 0; i < 8; i++) { // 8 bit a transmitir
                                                               SCK = 0;
                                         MOSI = (byte & 128) >> 7;
                                                               SCK = 1;
                                                               SPI_Valor = SPI_Valor << 1;
                                                                   SPI_Valor = SPI_Valor | MISO;
                                                                   byte = byte << 1;
                                      }
     MOSI = 1;
      return SPI_Valor;
}


1) Linea de CLK en bajo. Recuerden que el primer bit se lee en el pulso de subida.

Código:
SCK = 0;

2) Preparo el bit MSB a ser transmitido y lo pongo en la linea de salida.

Código:
MOSI = (byte & 128) >> 7;

3) Subo la linea de CLK para validar los datos.

Código:
SCK = 1;

4) Las dos siguientes lineas leen el bit transmitido por la tarjeta SD y lo desplazan.

Código:
SPI_Valor = SPI_Valor << 1;
 SPI_Valor = SPI_Valor | MISO;

5) Desplazo el byte a transmitir para comenzar otro ciclo en el paso 1.

Código:
byte = byte << 1;

Ojala que con las imágenes quede mas claro.

Como se aprecia, la función de escritura también lee el dato enviado por la SD. Con lo que la función debería llamarse spi_RW(). A mi me quedo mas claro llamarla como spi_write por lo que explicare a continuación.

Una cosa importante es que la linea MOSI nunca debe quedar en bajo cuando se lee los datos de la tarjeta. Cuando se leen datos de la tarjeta se le envía continuamente 1´s.

Código:
dato = spi_write(0xFF);

Para no confundirse, en C uno puede decirle que la función spi_read() sea igual que spi_write(0xFF) de la siguiente forma:

Código:
#define spi_read()  spi_write(0xFF)

Con lo que el compilador al encontrarse con spi_read() lo reemplazara con spi_write(0xFF).

Otro punto a aclarar es que si bien parece que el protocolo SPI es full duplex (se transmite y recibe al mismo tiempo) es mas bien half duplex (los datos van en solo sentido a la vez, osea o se transmite o se recibe, pero no los dos a la vez). Por eso hay que enviarle el valor 0xFF para que la memoria nos devuelva el dato que necesitamos.

Espero que con esto me haya podido explicar bien como es la transmisión entre el micro y la SD en el protocolo SPI emulado en software. Tengan presente que si el micro a usar tiene ya un modulo SPI integrado la cosa se simplifica bastante.



Nota: Todo esto lo había escrito hace tiempo cuando estaba jugando con las tarjetas SD y usaba un microcontrolador (PIC12F6XX) que no posee SPI por hardware. Hay que tener presente que no todos los dispositivos SPI funcionaran igual pudiendo haber algunas variaciones en los datos que devuelve cuando se le envía uno como así también hay, por ejemplo, memorias que usan datos de 16 bits y no de 8. Por lo tanto deberán adecuar al programa a sus necesidades.

Saludos.
 

D@rkbytes

Moderador
Buen aporte.
El protocolo SPI es aquel que uno diseñaría si le dieran solo 10 minutos para ello. Y sinceramente, sin desmerecer el protocolo, concuerdo en que es bastante sencillo.
El protocolo SPI (Serial Peripheral Interface) es tan sencillo que, es de los más fáciles de entender y emular por software.
Ni siquiera se requieren librerías, ni que el microcontrolador disponga del hardware.
Quiero suponer que todos los entornos de programación de alto nivel cuentan con soporte por software.
Por ejemplo: PIC C Compiler de CCS cuenta con esto: #use SPI (Options)
Y toda esa información se encuentra en los documentos de ayuda, que muchos ni siquiera saben que existen.
Los "programadores" de ahora, si no encuentran ejemplos, los piden, porque todo se les complica.
Piensan que su mejor maestro es youtube y ya no quieren estudiar ni terminar carreras.
Las cosas ya no son como antes. 😕
 
Buenísimo...hace unos días pregunté por este tema y nadie me respondió...también encontré algunos documentos de ayuda, como los que menciona d@rkbytes, pero referido a otros temas.

Donde marcan las reacciones o el me gusta o me entristece, etc....???
No tengo ninguna de las redes de espionaje americano (fb, ig, tw), así que no estoy al día con eso...único like que puedo dar a algo, es al helado de chocolate y al café con leche y medialunas.
Ya lo ví...no me respondan...!!!
 
Eso es mio. :LOL:.

Lo mas probable es que sea de acá o del, ya fallecido, foro de Mario Sacco (Aunque no recuerdo si lo había subido ahí).

Pero ojo que el 12F6XX no tiene memoria para manejar la memoria SD en todo su esplendor ya que los bloques son de 512 bytes y ese microcontrolador solo tiene 64 bytes.

Aun así y todo es una linda práctica que te lleva a aprender muchísimo.
 

D@rkbytes

Moderador
Eso es mio. :LOL:.

Lo mas probable es que sea de acá o del, ya fallecido, foro de Mario Sacco (Aunque no recuerdo si lo había subido ahí).
Me llamó la atención la rutina, ya que yo sabía que por algún lado la había visto.
Busqué entre mis archivos lo relacionado a SPI y salió ese ejemplo.
No contiene información del autor por ningún lado y me quedó la duda.
La publiqué con la intención de esperar la aclaración, que justamente era la esperada.
Ahora que la tiene, reitero nuevamente el agradecimiento. :aplauso:
 
Hay que tener cuidado, tanto con SPI como con I2C, y leer bien el datasheet del componente con el que uno se conecta. El problema con esos dos enlaces seriales es que hay una cierta base común pero después cada fabricante hace "lo que se le da la gana". Yo por ejemplo estoy trabajando con un sensor de línea para cámara que usa SPI. Las escrituras son de 32 bits. Las lecturas son de 64. En los primeros 32 bits de una lectura, uno le avisa al sensor qué registro quiere leer. En los siguientes 32 bits, el sensor devuelve el valor del registro pedido. Y como este caso me he encontrado unos cuantos, tanto para SPI como para I2C
 
Arriba