Hola a tod@s:
LLevo varia semanas intentando comunicar dos PIC mediante I2C, y por fin lo he conseguido.
He descubierto que hay un par de "trucos" para que funcione correctamente.
Os pongo unos ejemplos simplificados al maximo:
El master:
///////////////////////////////////////////////////////////////////////////////////////////////////////
// INT_SSP_MASTER //
// //
// Comunicacion I2C entre dos PIC 16F876 //
// //
// Este es el que va a actuar como master. Enviara secuencialmente una serie //
// de numeros que iran incrementandose de uno en uno. //
// //
//////////////////////////////////////////////////////////////////////////////////////////////////////
#INCLUDE <16F876.H>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#ORG 0x1F00,0x1FFF {} //Necesario para bootloader
#use delay(clock=20000000)
#use I2C(master,sda=PIN_C4,scl=PIN_C3)
//Leds que se iluminaran
#DEFINE LED1 PIN_A1 //PIN 3
#DEFINE PIC2 0x02 //Direccion dispositivo PIC 2 (esclavo)
void main()
{
int8 dato=0;
delay_ms(1000); //Hay que dejar tiempo que se inicialize el esclavo
while(1)
{
output_high(PIN_A1);
delay_ms(1000);
i2c_start(); // Condicion Inicio
i2c_write(PIC2);// Direccion dispositivo
delay_ms(3); // *** Necesario ***
i2c_write(dato);// dato
delay_ms(3); // *** Necesario ***
i2c_write(++dato);// dato
i2c_stop(); // Stop
output_low(PIN_A1);
delay_ms(1000);
}
}
Y el Esclavo:
///////////////////////////////////////////////////////////////////////////////////////////////////////
// INT_SSP_SLAVE //
// //
// Comunicacion I2C entre dos PIC 16F876 //
// //
// Este es el que va a actuar como ESCLAVO. Recibira secuencialmente una serie//
// de numeros que iran incrementandose de uno en uno y los mostrara en //
// pantalla //
//////////////////////////////////////////////////////////////////////////////////////////////////////
#INCLUDE <16F876.H>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#ORG 0x1F00,0x1FFF {} // Necesario para bootloader
#use delay(clock=20000000)
#DEFINE PIC2 0x02
#use I2C(slave,sda=PIN_C4,scl=PIN_C3,address=PIC2)
#define use_portb_lcd TRUE //Necesario si usamos el portB para el LCD, y que sea compatible con LCD.C
#include <LCD.C>
//VARIABLES GLOBALES
int8 buffer[2];
int1 recibido=0;
#int_ssp //Rutina Interrupción I2C
void int_sspi2c()
{
disable_interrupts(int_ssp); // *** Necesario ***
buffer[0]=i2c_read();
buffer[1]=i2c_read();
recibido=1;
}
void main()
{
delay_ms(50);
lcd_init();
lcd_putc("fPreparado...
");
delay_ms(300);
enable_interrupts(int_ssp);
enable_interrupts(global);
while(1)
{
if(recibido==1)
{
printf(lcd_putc,"fb[0]=%U b[2]=%u",buffer[0],buffer[1]);
recibido=0;
enable_interrupts(int_ssp); // *** Necesario ***
}
}
}
Los "trucos" a los que me referia antes son los siguientes:
Es fundamental poner un pequeño retardo despues de enviar la direccion del dispositivo:
i2c_write(PIC2); // Direccion dispositivo
delay_ms(3); // *** Necesario ***
Y luego tambien poner un retardo despues de enviar cada dato:
i2c_write(dato);// dato
delay_ms(3); // *** Necesario ***
El otro "truco" es que, en el esclavo, justo cuando se entra en la interrupcion SSP hay que desactivarla antes de hacer nada:
disable_interrupts(int_ssp); // *** Necesario ***
y luego cuando ya se ha terminado de procesar todos los datos volver a activarla:
enable_interrupts(int_ssp); // *** Necesario ***
Os puedo asegurar que estos ejemplos, asi, funcionan perfectamente, si se quitan los retardos o no se desactiva la int, no funcionan. Palabrita de "Niño Jesus".
Saludos