En esta sección aprenderás a realizar una comunicación serial SPI con dos microcontroladores PIC. El objetivos es enviar comandos por el puerto SPI a un microcontrolador PIC para que encienda diferentes LED´s.
Comunicación serial SPI
SPI (Serial Peripheral Interface) es un protocolo de comunicación serial sincrónica que se utiliza para la transferencia de datos entre dispositivos electrónicos. Se compone de una sola línea de reloj de sincronización (SCLK) y dos líneas de datos: MOSI (Master Output Slave Input) y MISO (Master Input Slave Output).
En una comunicación SPI, un dispositivo maestro envía comandos y datos a uno o varios dispositivos esclavos, y los dispositivos esclavos responden con los datos solicitados. El maestro controla la comunicación estableciendo la frecuencia de reloj y eligiendo el dispositivo esclavo con el que quiere comunicarse mediante una línea de selección de esclavos (SS).
Los dispositivos que se comunican a través de SPI deben estar configurados para trabajar en la misma velocidad de reloj y tener los mismos ajustes de formato de datos, como la longitud de la palabra de datos y la polaridad y fase del reloj.
SPI se utiliza comúnmente en sistemas embebidos, especialmente en aplicaciones que requieren alta velocidad de transferencia de datos y baja complejidad de hardware. Algunos ejemplos de dispositivos que utilizan SPI son sensores, controladores de pantalla, tarjetas de memoria, entre otros.
Circuito de conexión
Programación PIC de Maestro
Inicialmente se establecen los parámetros de la comunicación SPI.
- CLK = pin de señal de reloj
- DI = Pin de recepción de datos
- D0= Pin de transmisión de datos
- BAUD = Velocidad de transmisión
- MODE = Modo de comunicación SPI
- BITS = Datos de transmisión
#use spi (MASTER, CLK=PIN_B1, DI=PIN_B0, DO=PIN_C7, BAUD=9600, MODE=0, BITS=8)
Se establece el pin para enviar el pulso para habilitar la recepción de datos en el PIC esclavo.
#define enviar PIN_C2
Se definen los pines de entrada conectados a los botones.
#define boton1 input(pin_d0)
#define boton2 input(pin_d1)
#define boton3 input(pin_d2)
Se crean las variables para registrar el cambio de estado de los botones.
int1 regboton1 = 0;
int1 regboton2 = 0;
int1 regboton3 = 0;
Se crean las variables que contendrán los comandos que se enviaran al PIC esclavo.
int8 led1_on = 0x01;
int8 led1_off = 0x02;
int8 led2_on = 0x03;
int8 led2_off = 0x04;
int8 led3_on = 0x05;
int8 led3_off = 0x06;
Para enviar un comando el PIC esclavo inicialmente se envía un pulso de bajada, seguido del comando y para finalizar la transmisión se envía un pulso de subida.
output_bit(enviar,0);
spi_write(led1_on);
output_bit(enviar,1);
Para enviar el comando correspondiente solo cuando cambie de estado el botón, es decir cuando esta pulsado y cuando no esta pulsado se utilizan las variable para registrar el estado anterior del botón y solo cuando el estado actual sea diferente del estado anterior se enviara un nuevo comando.
if (regboton1 != boton1)
{
delay_us(10);
if(regboton1==0)
{
output_bit(enviar,0);
spi_write(led1_on);
output_bit(enviar,1);
}else
{
output_bit(enviar,0);
spi_write(led1_off);
output_bit(enviar,1);
}
regboton1 = !regboton1;
}
Finalmente se realiza el mismo procedimiento para los botones restantes.
Código completo PIC Maestro
#FUSES NOMCLR
#use spi (MASTER, CLK=PIN_B1, DI=PIN_B0, DO=PIN_C7, BAUD=9600, MODE=0, BITS=8)
#define enviar PIN_C2
#define boton1 input(pin_d0)
#define boton2 input(pin_d1)
#define boton3 input(pin_d2)
int1 regboton1 = 0;
int1 regboton2 = 0;
int1 regboton3 = 0;
void main()
{
int8 led1_on = 0x01;
int8 led1_off = 0x02;
int8 led2_on = 0x03;
int8 led2_off = 0x04;
int8 led3_on = 0x05;
int8 led3_off = 0x06;
while(TRUE)
{
if (regboton1 != boton1)
{
delay_us(10);
if(regboton1==0)
{
output_bit(enviar,0);
spi_write(led1_on);
output_bit(enviar,1);
}else
{
output_bit(enviar,0);
spi_write(led1_off);
output_bit(enviar,1);
}
regboton1 = !regboton1;
}
if (regboton2 != boton2)
{
delay_us(10);
if(regboton2==0)
{
output_bit(enviar,0);
spi_write(led2_on);
output_bit(enviar,1);
}else
{
output_bit(enviar,0);
spi_write(led2_off);
output_bit(enviar,1);
}
regboton2 = !regboton2;
}
if (regboton3 != boton3)
{
delay_us(10);
if(regboton3==0)
{
output_bit(enviar,0);
spi_write(led3_on);
output_bit(enviar,1);
}else
{
output_bit(enviar,0);
spi_write(led3_off);
output_bit(enviar,1);
}
regboton3 = !regboton3;
}
delay_ms(100);
//TODO: User Code
}
}
Programación PIC esclavo
Se establecen los parámetros para la comunicación SPI.
- CLK = pin de señal de reloj
- DI = Pin de recepción de datos
- D0= Pin de transmisión de datos
- ENABLE = Pin de pulso de señal de recepción de datos
- MODE = Modo de comunicación SPI
- BITS = Datos de transmisión
#use spi (SLAVE, CLK=PIN_B1, DI=PIN_B0, DO=PIN_C7, ENABLE=PIN_A5, MODE=0, BITS=8)
Se definen los pines de salida conectados a los LED´s.
#define LED1 PIN_D0
#define LED2 PIN_D1
#define LED3 PIN_D2
Se habilita la interrupción para la recepción de datos por el puerto SPI.
enable_interrupts(INT_SSP);
enable_interrupts(global);
Cuando se recibe un comando se habilita la interrupción y se aloja en la sub-rutina «recibe()», donde se realiza la lectura del comando recibido con «spi_read()» y finalmente el comando recibido se guarda en la variable «dato».
#INT_SSP
void recibe()
{
dato = spi_read();
return;
}
Se utiliza una sentencia switch para realizar una instrucción dependiendo del comando recibido.
void led()
{
switch (dato)
{
case 0x01:
OUTPUT_BIT(LED1,1);
break;
case 0x02:
OUTPUT_BIT(LED1,0);
break;
case 0x03:
OUTPUT_BIT(LED2,1);
break;
case 0x04:
OUTPUT_BIT(LED2,0);
break;
case 0x05:
OUTPUT_BIT(LED3,1);
break;
case 0x06:
OUTPUT_BIT(LED3,0);
break;
default:
break;
}
return;
}
Una vez recibido el comando y realizado la instrucción correspondiente el programa regresa a la función «main()» donde espera a recibir un nuevo comando por el puerto SPI.
Código completo PIC Esclavo
#FUSES NOMCLR
#use spi (SLAVE, CLK=PIN_B1, DI=PIN_B0, DO=PIN_C7, ENABLE=PIN_A5, MODE=0, BITS=8)
#define LED1 PIN_D0
#define LED2 PIN_D1
#define LED3 PIN_D2
int8 dato=0;
void led();
#INT_SSP
void recibe()
{
dato = spi_read();
led();
return;
}
void led()
{
switch (dato)
{
case 0x01:
OUTPUT_BIT(LED1,1);
break;
case 0x02:
OUTPUT_BIT(LED1,0);
break;
case 0x03:
OUTPUT_BIT(LED2,1);
break;
case 0x04:
OUTPUT_BIT(LED2,0);
break;
case 0x05:
OUTPUT_BIT(LED3,1);
break;
case 0x06:
OUTPUT_BIT(LED3,0);
break;
default:
break;
}
return;
}
void main()
{
enable_interrupts(INT_SSP);
enable_interrupts(global);
while(TRUE)
{
//TODO: User Code
}
}