Saltar al contenido

Comunicación SPI con PIC

Hola controleros y controleras bienvenidos a otra entrada de nuestro curso de PIC donde vamos a aprender a usar la comunicación SPI con la cual en nuestra próxima entrada lo usaremos para conectaremos una termocupla tipo K al microcontrolador para que nos sirva de ejemplo en la utilización de este protocolo de comunicación.

Primero deseo hacerte la invitación a que revises el curso GRATIS de 👉Microcontroladores PIC

Comunicación SPI con PIC

A lo largo de nuestro curso de microcontroladores PIC hemos abordado los diferentes tipos de comunicación serial que posee nuestro sistema embebido tanto síncrona como asíncrona.

Tuvimos la oportunidad de ver el protocolo de comunicación asíncrono RS232 utilizando el UART (Universal Asynchronous Receiver Transmitter), como también la comunicación sincrona utilizando la comunicación I2C (Inter Integrated Circuit), pero ahora es tiempo de ver una segunda comunicación síncrona muy popular conocida como comunicación SPI (Serial Peripheral Interface).

Habíamos visto que en la comunicación serial síncrona definimos el concepto de Maestro-Esclavo. Normalmente, quien genera la señal de sincronismo se define como Maestro (Master) de la comunicación y los dispositivos que utilizan la señal de sincronismo generada los definimos como Esclavo (Slave).

La comunicación SPI utiliza dos señales de transmisión de datos lo que le da mayor velocidad de comunicación debido a que hay poca deformación de la señal.

Con la comunicación SPI el intercambio de datos ocurre siempre en ambas direcciones. En decir, cuando existe intercambio de un bit entre el maestro y algún esclavo seguidamente vendrá un intercambio de un bit del esclavo para el maestro. De esta forma, definimos que la comunicación es siempre full-duplex.

Los pines básicos de comunicación entre dispositivos SPI y el esquema de conexión estándar se muestra a continuación, esto sirve para CUALQUIER dispositovo que use SPI (PIC, ARDUINO, MOTOROLA,ETC):

Pin

Nombre Estandar

Significado

Nombres Alternativos

 

Del Master para el Slave

MOSI

Master Output Slave Input

SDO, DO, SO

 

Del Slave para el Master

MISO

Master Input Slave Output

SDI, DI, SI

 

Clock

SCLK

Serial Clock

SCK, CLK

 

Selección del Slave

SS

Slave Select

CS, nSS, nCS

 
Esquema comunicación SPI

Cuando se conectan varios esclavos a un puerto SPI, los esclavos se conectan en paralelo y las señales CS o SS del maestro se vinculan a cada SS de los esclavos. La salida de datos del esclavo seleccionado mediante la señal CS/SS se habilita y los dispositivos deseleccionados se desconectan de la línea MISO.

La señal de SS funciona como Selección de Esclavo (Slave Select). Es una señal activa a nivel bajo, lo que significa que el dispositivo se selecciona cuando este pin se encuentra a un nivel bajo.

Pines SPI 16F887 y 18F4550

A continuación en Rojo se muestra la ubicación de los pines que componen el hardware del protocolo SPI en los PIC 16F887 y 18F4550. Los 4 pines en cada uno corresponden a (SDO, SDI, SCK, SS) explicados en la tabla de arriba.

SPI pinout

Funcionamiento de la comunicación SPI

Como vimos en el esquema anterior, una comunicación SPI básica entre dos dispositivos tendrá 4 Lineas o en su defecto 3 lineas si uno de los dispositovos no necesita enviar nada.

Vemos que el MASTER colocará en bajo el pin SS del Slave con el que se quiere comunicar. Seguidamente, el master genera una señal de reloj para sincronizar el envío y recepción de datos. Note que los datos aparecen en el flanco de subida de la señal de reloj (SCK), este es el modo de comunicación más común, sin embargo existen otros modos de comunicarse que se detallarán a continuación.

Modos de Transferencia en comunicación SPI

En la comunicación SPI, el cambio de datos y el bloqueo de datos se realizan en los bordes de la señal de reloj (flancos de subida o bajada), respectivamente. Existe la ventaja de que cuando se separan las operaciones de cambio de polaridad del reloj (latch) y de fase del reloj (shift), se puede evitar la sincronización crítica entre dos operaciones. Por lo tanto, la consideración de tiempo para el maestro y el esclavo se puede aliviar. Pero, por otro lado, hay cuatro modos de operación debido a la combinación de polaridad de reloj y fase de reloj, el maestro debe configurar su interfaz SPI con un modo SPI que el esclavo sea capaz de interpretar.

Tiempos y modos del SPI

Funciones de CCS para SPI

setup_spi (modo), setup_spi2 (modo): configura el SPI de hardware en el modo especificado.
El modo configura si es maestro o esclavo, la velocidad del reloj y la configuración cambios de reloj / datos.
Nota: para dispositivos con interfaces SPI dobles se proporciona una segunda función, setup_spi2 (), para configurar la segunda interfaz.

spi_data_is_in (), spi_data_is_in2 () – Devuelve TRUE si el búfer de recepción SPI tiene un byte de datos.
spi_write (valor), spi_write2 (valor): transmite el valor a través de la interfaz SPI. Esto hará que los datos se vacien en el pin SDO.
spi_read (valor), spi_read2 (valor): realiza una transacción SPI, donde el valor se registra en el pin SDO y se devuelven los datos sincronizados en el pin SDI. Si solo desea registrar los datos, puede usar spi_read () sin un parámetro.

Interrupciones por SPI

int_ssp, # int_ssp2 – directiva para definir la interrupción dentro del código

[PCD] # int_spi1 – Interrumpe la actividad del primer módulo SPI.
[PCD] # int_spi2 – Interrumpe la actividad del segundo módulo SPI.

Modos SPI dentro de CCS

#define SPI_MODE_0  (SPI_L_TO_H | SPI_XMIT_L_TO_H) 
#define SPI_MODE_1  (SPI_L_TO_H) 
#define SPI_MODE_2  (SPI_H_TO_L) 
#define SPI_MODE_3  (SPI_H_TO_L | SPI_XMIT_L_TO_H) 

Buffer SPI

Dentro de los registros del PIC, se encuentra una directiva llamada SSBUF, la cual es el buffer donde se almacenan los datos recibidos a través de la comunicación SPI, será importante definir la dirección de memoria de esta libreria en el programa, la cual nos la suministra el datasheet.

#byte SSPBUF = 0x13  // Dirección del Registro para 16F877 
#byte SSPBUF = 0xFC9  // Dirección del Registro para 18F4550

Otros protocolos de comunicación

El protocolo SPI es altamente eficiente para la transmisión de datos a alta velocidad en proyectos con microcontroladores PIC, especialmente cuando necesitas conectar múltiples dispositivos con un control preciso. Sin embargo, cuando se trata de aplicaciones donde la simplicidad y la facilidad de integración son más importantes que la velocidad, el protocolo I2C es una excelente alternativa. Tanto en proyectos con PIC como con Arduino, el I2C te permite conectar varios dispositivos usando solo dos hilos, lo que reduce la complejidad del cableado y libera pines en tu microcontrolador. Si estás interesado en aprender más sobre cómo implementar I2C, te invito a consultar nuestras guías completas sobre I2C en PIC y I2C en Arduino.

Ejemplo de Comunicación SPI con PIC en CCS

Realizar una comunicación SPI entre dos Microcontroladores, donde el PIC maestro tendrá una comunicación Serial RS232 para que el usuario pueda decidir las acciones a ejecutar.

Las acciones a ejecutar consisten en que el Maestro debe mostrarle al usuario 3 lecturas diferentes:

  1. Leer el voltaje de un potenciometro
  2. Leer el estado logico de un interruptor
  3. Leer la cuenta de un contador

Solo que esas tres lecturas las tiene un PIC esclavo (potenciomentro en AN0, interruptor en B4 y contador en la memoria interna del esclavo).

Por lo tanto el Maestro deberá pedir esas informaciones al esclavo a través de una comunicación SPI.

Circuito SPI 16F887

Circuito SPI 18F4550

Código en CCS de Comunicación SPI

Para descargar el código basta solo con que compartas el contenido de este post con cualquiera de los siguientes tres botones, de esa forma ayudas a que este sitio web pueda llegar a más personas y pueda brindar estos conocimientos

[sociallocker id=948]

>> DESCARGAR CÓDIGO Y ARCHIVO PROTEUS 8.7 <<

Código del Maestro

// MAESTRO en SPI

#include <18F4550.H> 
//#include <16F887.H>

#device ADC=10
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP 
#use delay(crystal=4000000) 
#use rs232(baud=9600,xmit=PIN_D0,rcv=PIN_D1,stream=PC, parity=N, bits=8) //18F4550
//#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7,stream=PC, parity=N, bits=8)//16F887

#define SPI_SS   PIN_C2 

// Modos de comunicación SPI 
#define SPI_MODE_0  (SPI_L_TO_H | SPI_XMIT_L_TO_H) 
#define SPI_MODE_1  (SPI_L_TO_H) 
#define SPI_MODE_2  (SPI_H_TO_L) 
#define SPI_MODE_3  (SPI_H_TO_L | SPI_XMIT_L_TO_H) 


// Comandos SPI que acepta el esclavo 
#define LEER_ADC          1 
#define LEER_CONTADOR     2 
#define LEER_INTERRUPTOR  3 

//====================================== 
void main() 
{ 
char c; 
int8 resultado; 

output_high(SPI_SS);  // Inicializa el Pin SS en alto para deshabilitar el esclavo

//Inicializa el hardware SSP para ser un SPI Maestro en Modo 0
setup_spi(SPI_MASTER | SPI_MODE_0 | SPI_CLK_DIV_4); 

printf("Comandos para el Esclavo:\n\r"); 
printf("A: Leer el ADC\n\r"); 
printf("B: Leer el interruptor\n\r"); 
printf("C: Leer el contador\n\r"); 
printf("Presione la tecla comando\n\r"); 

// Si presiona la tecla correcta manda la orden al esclavo
while(1) 
  { 
   c = getc(); //Captura el caracter del puerto serial RS232
   c = toupper(c); //Convierte el caracter en mayusculo
  
   switch(c) 
    { 
     case 'A':     // Leer ADC en el Esclavo 
       output_low(SPI_SS); 
       spi_write(LEER_ADC);  // Envia el comando 
       output_high(SPI_SS); 

       delay_us(100);  // Tiempo para que el esclavo responda

       output_low(SPI_SS); 
       resultado = spi_read(0);     // Lee la respuesta del esclavo 
       output_high(SPI_SS); 
    
       printf("Valor ADC = %X \n\r", resultado); 
       break; 

     case 'B':     // Lee el interruptor en el esclavo 
       output_low(SPI_SS); 
       spi_write(LEER_INTERRUPTOR); // Envia el comando 
       output_high(SPI_SS); 

       delay_us(100); 

       output_low(SPI_SS); 
       resultado = spi_read(0);     //  Lee la respuesta del esclavo 
       output_high(SPI_SS); 
    
       printf("Interruptor = %X \n\r", resultado); 
       break; 


     case 'C':     // Lee el contador en el esclavo 
       output_low(SPI_SS); 
       spi_write(LEER_CONTADOR); // Envia el comando  
       output_high(SPI_SS); 

       delay_us(100); 

       output_low(SPI_SS); 
       resultado = spi_read(0);     //  Lee la respuesta del esclavo  
       output_high(SPI_SS); 
    
       printf("Countador = %X \n\r", resultado); 
       break; 

      
     default: 
       printf("%c es un caracter invalido.\n\r", c); 
       break; 
    } 

  } 

}

Código del Esclavo

// ESCLAVO Comunicación SPI 

#include <18F4550.H> 
//#include <16F887.H>
#device adc=8 
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP 
#use delay(crystal=4000000) 
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7) 


//#byte SSPBUF = 0x13  // Dirección del Registro para 16F877 
#byte SSPBUF = 0xFC9  // Dirección del Registro para 18F4550


// Modos de comunicación SPI 
#define SPI_MODE_0  (SPI_L_TO_H | SPI_XMIT_L_TO_H) 
#define SPI_MODE_1  (SPI_L_TO_H) 
#define SPI_MODE_2  (SPI_H_TO_L) 
#define SPI_MODE_3  (SPI_H_TO_L | SPI_XMIT_L_TO_H) 

// Comandos SPI de este esclavo 
#define LEER_ADC          1 
#define LEER_CONTADOR     2 
#define LEER_INTERRUPTOR  3 

// Definición del PIN donde se conecta el interruptor. 
#define SWITCH_PIN  PIN_B4 

// Variable global para almacenar el ADC 
int8 pot; 

// Interrupción por SPI donde se analiza las solicitudes del Maestro
#int_ssp 
void ssp_isr(void) 
{ 
int8 comando; 
static int8 contador = 0; 

comando = SSPBUF; //Toma el valor del buffer SPI y lo almacena en comando

switch(comando)   // Verifica la instrucción del maestro
  { 
   case LEER_ADC: 
     SSPBUF = pot; 
     break; 

   case LEER_CONTADOR: 
     SSPBUF = contador; 
     contador++;          
     break;  

   case LEER_INTERRUPTOR: 
     SSPBUF = input(SWITCH_PIN); 
     break; 
  } 

} 


//====================================== 
void main() 
{ 
// Configura AN0 como analoga
setup_adc_ports(AN0);    //18F4550
//setup_adc_ports(sAN0); //16F887
setup_adc(ADC_CLOCK_DIV_8); 

set_adc_channel(0); 
delay_us(20); 
pot = read_adc(); 


//Inicializa el hardware SSP para ser un SPI esclavo en Modo 0
setup_spi(SPI_SLAVE | SPI_MODE_0); 

// Habilita las interrupciones
clear_interrupt(INT_SSP); 
enable_interrupts(INT_SSP); 
enable_interrupts(GLOBAL); 

// Actualiza la lectura analoga cada 100ms 
while(1) 
  { 
   pot = read_adc(); 
   delay_ms(100); 
  } 

}

[/sociallocker]

Bueno controleros y controleras, eso es todo por la entrada del día de hoy, ya por lo menos nos hemos familiarizado un poco con la comunicación SPI, y en la próxima entrada podremos hacer una lectura de una termocupla tipo K usando este tipo de protocolo de comunicación.

Espero se encuentren muy bien y nos vemos en la próxima.

Entradas relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Comentarios (16)

Hola buenas tardes ,Estoy tratando de calibrar el termopar tipo k en proteus pero no me sale ,me gustaría saber las configuraciones del spi y el computador virtual , y si el spi lo necesito para una calibración física o con la pura programación queda calibrado mi termopar

Responder

Edgar para una calibración y ajuste real, debes directamente usar otro sensor de temperatura que te indique la temperatura real, puedes tomar varios puntos de medida y despues generar un polinomio con una regresión para que lo programes dentro del microcontrolador, algo parecido a la entrada que hicimos con el Termopar tipo K con PIC.

Responder

Muy Buena la explicación, clara y concisa. ah y muchas gracias por divulgarla

Responder

De nada Miguel, que bueno que te ha gustado. Saludos!!

Responder

gracias profesor por seguir enseñandonos,
podria hacer un video explicando el uso del modulo wifi esp8266 (o cualquier otro)
en pic c compiler? se lo agradeceria mucho

Responder

Hola buen día, una duda, para hacer el envío de datos flotantes esclavo-maestro, cual sería la metodología, gracias, saludos.

Responder

Disculpa ya simule lo que explicas en el video y no me sale

Responder

Hola Carlos, debes observar bien cual código deseas ejecutar si es para un 16F o para un 18F. De igual forma coloqué los archivos de simulación para descarga junto con los códigos.

Responder

Hola una pregunta.
Estoy tratando de hacer comunicación por SPI, pero mi microcontrolador no cuenta con MOSI, solo con MISO, y no se como configurar el código para forzar otro pin para utilizarlo como MOSI.
¿Existe la manera de hacerlo? o Cambio de microcontrolador.
El PIC que estoy usando es: PIC16F18445

Responder

Hola Juan, desconozco un método para forzar eso, pues nunca lo he hecho. Cuando se realizan proyectos con microcontroladores, la primera etapa es seleccionar el dispositivo que tenga todas las características que requiere el proyecto, pues no todos los PICs tienen las mismas capacidades. Yo creería que en este caso vas a tener que cambiar de PIC o buscar en el foro del CCS para ver si alguien ya consiguió forzar el MOSI. Saludos y suerte con tu proyecto.

Responder

Hola. Gracias por este buen contenido. Tengo dos preguntas. 1. No estas utilizando un cristal de cuarzo en cada PIC, creí que era obligatorio colocar uno. 2. Y como se hace para conectado a una Computadora que no posee puerto RS232, si se usa con un convertidos de RS232 a USB hay que modificar el código?

Responder

Hola Carlos, gracias por la observación. El cristal no es siempre necesario porque puedes usar el cristal interno. Sin embargo en este ejemplo SI es necesario. Se debe colocar. En simulación, no hay problema, que es el caso que mostré aquí, pero para implementarlo fisicamente hay que colocar los dos cristales. Para comunicar el computador com el conversor, no hay necesidad de cambiar nada en el código, tal como lo vimos en la entrada de comunicación serial. Saludos.

Responder

Hola Sergio,

Te felicito por tu página.
Nos ha sido muy útil.
Nosotros trabajamos con PIC y no sabemos como lograr que un ADC se estabilice, por ejemplo para sistemas muy dinámicos (en movimiento).
Puedes tratar el tema o recomendar como filtrar o amortiguar por código esta toma de datos?
Por ejemplo si tomas un ADC para una báscula de pesaje dinámico los datos suben y bajan y no se como estabilizarlos.
Usamos algunos CI de ADC y nos pasa lo mismo, no sabemos como estabilizar el pesaje.
Ojalá nos puedas guiar o recomendar algo.
Saludos desde Chile.

Responder

Hola Francisco, una opción de estabilizar las lecturas ADC es a través de un filtro de promedio movil, el cual se hace por código. En un FOR lees digamos unas 100 veces, guardas los 100 valores en un vector, los sumas todos y los divides por 100, para obtener el promeio. Igual puedes hacerlo con menos datos.

Responder

Probamos eso y no nos da resultados positivos, ya que no elimina los extremos que generan vibraciones de los motores o bandas transportadoras.
Tambien intentamos simular un filtro RC, pero tampoco logro los resultados en iguales condiciones con una bascula industrial.

Responder

Es importante tener una medida lo más limpia posible, pues si hay mucha varianza en la lectura, va a ser complicado. Trata de filtrar la señal con amplificadores operacionales, en modo seguidor y con filtro pasa bajos. En el pic coloca un condensador de 10u lo más cerca posible del pin de 5v y gnd. Si la señal es muy ruidosa es complicado arreglar un problema de hardware por software. Lo otro que podrías hacer es tomar la medida cuando se estabilice, es decir al colocar el objeto en la balanza, dicha balanza le tomara un tiempo en entregar el peso, una vez que lo cuantifique, en ese momento sería el ideal para leerlo con el PIC, y talvez con el filtro de promedio movil.

Responder