Aca un manejador de I2C MASTER por interrupciones:
//Valores para I2C
#define SSPMode(val) SSPCON1 &=0xF0; SSPCON1|=(val & 0xf)
#define MASTER_FIRM 0B1011
#define MASTER_CLK 0B1000
//Definiciones de tareas
#define T_IDLE 0
#define T_WRITE 1
#define T_READ 2
#define T_WRITE_R 3
#define T_READ_R 4
//Definiciones de pasos
#define S_IDLE 0
#define S_START 1
#define S_RESTART 2
#define S_STOP 3
#define S_WRITE_ADRESS_W 4
#define S_WRITE_ADRESS_R 5
#define S_WRITE_DEV_ADRE 6
#define S_WRITE_DATA 7
#define S_READ_DATA 8
#define S_ACK_MS 9
#define S_NACK_MS 10
#define S_DONE 11
#define S_ERROR 12
//Definiciones de errores
#define E_NO_ERROR 0
#define E_W_ADRESS_NACK 1
#define E_R_ADRESS_NACK 2
#define E_DATA_NACK 3
#define E_DEV_ADRESS_NACK 4
#define E_DEV_TIMEOUT 5
//Definicion de la I2C
#define SCL RC3
#define SDA RC4
#define SCL_TRIS TRISC3
#define SDA_TRIS TRISC4
//Variables de I2C
char I2C_TASK_TIMEOUT;
char I2C_TASK,I2C_TASK_STEP,I2C_TASK_ERROR;
char I2C_TASK_ADRESS,I2C_TASK_DEV_ADRESS,*I2C_TASK_DATA,I2C_TASK_NUM;
#define I2C_TASK_TIMEOUT_MAX 30 //tiempo de espera para que conteste
/******************************************************************************************/
// Rutinas I2C MASTER para dispositivos con HW I2C manejado por interrupciones
/******************************************************************************************/
void I2C_init (void);
void I2C_Interrupt (void);
void I2C_WRITE (char I2C_ADRESS,char *I2C_DATA,char I2C_NUM);
void I2C_READ (char I2C_ADRESS,char *I2C_DATA,char I2C_NUM);
void I2C_WRITE_ROM (char I2C_ADRESS,char I2C_DEV_ADRESS,char *I2C_DATA,char I2C_NUM);
void I2C_READ_ROM (char I2C_ADRESS,char I2C_DEV_ADRESS,char *I2C_DATA,char I2C_NUM);
/******************************************************************************************/
void I2C_WRITE (char I2C_ADRESS,char *I2C_DATA,char I2C_NUM)
{I2C_TASK_STEP=S_START; /*Posiciono en paso START*/;
I2C_TASK_TIMEOUT=I2C_TASK_TIMEOUT_MAX; /*Inicializo el TIMEOUT*/
I2C_TASK=T_WRITE;I2C_TASK_ADRESS=I2C_ADRESS;I2C_TASK_DATA=I2C_DATA;
I2C_TASK_NUM=I2C_NUM;SEN=1;/*Genero un Start*/;}
/******************************************************************************************/
void I2C_READ (char I2C_ADRESS,char *I2C_DATA,char I2C_NUM)
{I2C_TASK_STEP=S_START;/*Posiciono en paso START*/;
I2C_TASK_TIMEOUT=I2C_TASK_TIMEOUT_MAX; /*Inicializo el TIMEOUT*/
I2C_TASK=T_READ;I2C_TASK_ADRESS=I2C_ADRESS;I2C_TASK_DATA=I2C_DATA;
I2C_TASK_NUM=I2C_NUM;SEN=1;/*Genero un Start*/;}
/******************************************************************************************/
void I2C_WRITE_ROM (char I2C_ADRESS,char I2C_DEV_ADRESS,char *I2C_DATA,char I2C_NUM)
{I2C_TASK_STEP=S_START;/*Posiciono en paso START*/;
I2C_TASK_TIMEOUT=I2C_TASK_TIMEOUT_MAX; /*Inicializo el TIMEOUT*/
I2C_TASK=T_WRITE_R;I2C_TASK_ADRESS=I2C_ADRESS;I2C_TASK_DEV_ADRESS=I2C_DEV_ADRESS;
I2C_TASK_DATA=I2C_DATA;I2C_TASK_NUM=I2C_NUM;SEN=1;/*Genero un Start*/;}
/******************************************************************************************/
void I2C_READ_ROM (char I2C_ADRESS,char I2C_DEV_ADRESS,char *I2C_DATA,char I2C_NUM)
{I2C_TASK_STEP=S_START;/*Posiciono en paso START*/;
I2C_TASK_TIMEOUT=I2C_TASK_TIMEOUT_MAX; /*Inicializo el TIMEOUT*/
I2C_TASK=T_READ_R;I2C_TASK_ADRESS=I2C_ADRESS;I2C_TASK_DEV_ADRESS=I2C_DEV_ADRESS;
I2C_TASK_DATA=I2C_DATA;I2C_TASK_NUM=I2C_NUM;SEN=1;/*Genero un Start*/;}
/******************************************************************************************/
void I2C_Interrupt (void)
{I2C_TASK_TIMEOUT=I2C_TASK_TIMEOUT_MAX; /*Inicializo el TIMEOUT*/
switch(I2C_TASK_STEP)
{case S_START :I2C_TASK_ERROR=E_NO_ERROR;
if (I2C_TASK==T_READ)
{I2C_TASK_STEP=S_WRITE_ADRESS_R;SSPBUF=(I2C_TASK_ADRESS | 0x01);}
else{I2C_TASK_STEP=S_WRITE_ADRESS_W;SSPBUF=(I2C_TASK_ADRESS & 0xFE);};break;
case S_RESTART :I2C_TASK_STEP=S_WRITE_ADRESS_R;SSPBUF=(I2C_TASK_ADRESS | 0x01);break;
case S_WRITE_ADRESS_W:if (ACKSTAT) {I2C_TASK_ERROR=E_W_ADRESS_NACK;I2C_TASK_STEP=S_STOP;PEN=1;}
else {switch(I2C_TASK){case T_WRITE :I2C_TASK_STEP=S_WRITE_DATA;SSPBUF=*I2C_TASK_DATA;break;
case T_WRITE_R :I2C_TASK_STEP=S_WRITE_DEV_ADRE;SSPBUF=I2C_TASK_DEV_ADRESS;break;
case T_READ_R :I2C_TASK_STEP=S_WRITE_DEV_ADRE;SSPBUF=I2C_TASK_DEV_ADRESS;break;
};};break;
case S_WRITE_ADRESS_R:if (ACKSTAT) {I2C_TASK_ERROR=E_R_ADRESS_NACK;I2C_TASK_STEP=S_STOP;PEN=1;}
else{I2C_TASK_STEP=S_READ_DATA;RCEN=1;};break;
case S_WRITE_DEV_ADRE:if (ACKSTAT) {I2C_TASK_ERROR=E_DEV_ADRESS_NACK;I2C_TASK_STEP=S_STOP;PEN=1;}
else{switch(I2C_TASK){case T_WRITE_R:I2C_TASK_STEP=S_WRITE_DATA;SSPBUF=*I2C_TASK_DATA;break;
case T_READ_R :I2C_TASK_STEP=S_RESTART;RSEN=1;break;};}
break;
case S_WRITE_DATA :if (ACKSTAT) {I2C_TASK_ERROR=E_DATA_NACK;I2C_TASK_STEP=S_STOP;PEN=1;}
else if (I2C_TASK_NUM)
{I2C_TASK_NUM--;I2C_TASK_DATA++;I2C_TASK_STEP=S_WRITE_DATA;SSPBUF=*I2C_TASK_DATA;}
else {I2C_TASK_STEP=S_STOP;PEN=1;};
break;
case S_READ_DATA :*I2C_TASK_DATA=SSPBUF;
if (I2C_TASK_NUM) {ACKDT=0;I2C_TASK_NUM--;I2C_TASK_DATA++;I2C_TASK_STEP=S_ACK_MS;}
else {ACKDT=1;I2C_TASK_STEP=S_NACK_MS;};
ACKEN=1;break;
case S_ACK_MS :I2C_TASK_STEP=S_READ_DATA;RCEN=1;break;
case S_NACK_MS :I2C_TASK_STEP=S_STOP;PEN=1;break;
case S_STOP :if (I2C_TASK_ERROR==E_NO_ERROR)
{I2C_TASK_STEP=S_DONE;}else{I2C_TASK_STEP=S_ERROR;};break;
default :I2C_TASK_STEP=S_ERROR;break;
}
}
/******************************************************************************************/
void I2C_init (void)
{SCL_TRIS=SDA_TRIS=1; //Seteo SCL y SDA como entradas
SSPMode(MASTER_CLK); //SSP modo maestro con seteo de frec de I2C
SSPCON2=0;
#if XTAL_CON_PLL==1 //Velocidad de transferencia=fosc/(4*(SSPADD+1))=100K
SSPADD = 99;
#else
SSPADD = 24;
#endif
SSPEN = 1; //Habilito modulo I2C
CKP=SMP=1; //Control de Slew rate
I2C_TASK=T_IDLE;I2C_TASK_STEP=S_IDLE; //Inicializo flags
I2C_TASK_ERROR=E_NO_ERROR;
SSPIF=0;SSPIE=1; //Limpio flags de interrupcion y la inicio
}
Este manejador permite trabajar tanto con un dispositivo I2C clasico como con memorias 24LCXX de manera directa.
Falta un pequeño DAEMON de Timeout de I2C que va en la parte de relojeria pero es facil darse cuenta que va.