     /********************************************************************
      ***     Programa de test de comunicación I2C con una EEPROM      ***
      ***                24LC256 y un dsPIC30F6014                     ***
      ***                                                              ***
      ***  Adaptación: Nocturno-2008             www.micropic.es       ***
      ***                 Basado en código original de CREMMEL Marcel  ***
      ********************************************************************/

#include <p30f6014.h>
#include <stdio.h>
//--------------------------Configuración del dispositivo------------------------       
_FOSC(CSW_FSCM_OFF & XT_PLL16);//Turn off clock switching and
                               //fail-safe clock monitoring and
                               //use the XT Clock x 16 as the system clock
_FWDT(WDT_OFF);                //Turn off Watchdog Timer
_FBORPOR(PBOR_ON & BORV_27 & PWRT_16 & MCLR_EN);
                               //Set Brown-out Reset voltage and
                               //and set Power-up Timer to 16msecs
_FGS(CODE_PROT_OFF);           //Set Code Protection Off for the
                               //General Segment

#define MIPS 32

/*********************************************/
/**  Función para hacer un retardo de x us  **/
/**  generada automáticamente por asistente **/
/**             www.micropic.es           **/
/**              Nocturno - 2006            **/
/**       Válida para MIPS>=16 (>32MHz)     **/
/**        Valores de x: de 0 a 65535       **/
/*********************************************/

void delay_us(unsigned int x) {
  asm ("REPEAT #%0\n NOP" :: "i" (MIPS-16));
  while ((x--)!=1) {
    asm ("REPEAT #%0\n NOP" :: "i" (MIPS-7));
  };
};

/*********************************************/
/**  Función para hacer un retardo de x ms  **/
/**  generada automáticamente por asistente **/
/**             www.micropic.es           **/
/**              Nocturno - 2006            **/
/**      Válida para MIPS>=0.24 (>48KHz)    **/
/**        Valores de x: de 0 a 65535       **/
/*********************************************/

void delay_ms(unsigned int x) {
  asm ("REPEAT #%0\n NOP" :: "i" (MIPS*250-5));
  asm ("REPEAT #%0\n NOP" :: "i" (MIPS*250-5));
  asm ("REPEAT #%0\n NOP" :: "i" (MIPS*250-6));
  asm ("REPEAT #%0\n NOP" :: "i" (MIPS*250-6));
  while ((x--)!=1) {
    asm ("REPEAT #%0\n NOP" :: "i" (MIPS*250-4));
    asm ("REPEAT #%0\n NOP" :: "i" (MIPS*250-3));
    asm ("REPEAT #%0\n NOP" :: "i" (MIPS*250-3));
    asm ("REPEAT #%0\n NOP" :: "i" (MIPS*250-3));
  };
}
                        /*****************************
                        *    Definiciones de tipos   *
                        ******************************/

typedef struct
 {
  unsigned char Adr_I2C;    // Dirección del dispositivo I2C
  unsigned int  Direccion; 	// Posición de memoria a la que apuntamos. 
  unsigned int  Longitud;	// Nº de bytes que se van a transmitir
  unsigned char *Datos;		// Puntero a los datos que se transmitirán
  unsigned char Sentido; 	// 0 = Escritura; 1 = Lectura
 } tCOM_I2C;

typedef enum
{
  Start    = 0,
  Envia    = 1,
  Recibe   = 2,
  Restart  = 3,
  MAck     = 4,
  Stop     = 5
}tSecuenciaI2C; // Tipo de secuencia de I2C

typedef enum
{
  Escritura    = 0,
  Lectura      = 1
}tR_W; // Determina tipo de acceso al I2C

                        /*******************************
                        *       Variables              *
                        ********************************/

tCOM_I2C COM_I2C;			// Estructura de soporte del I2C
tSecuenciaI2C EstadoI2C; 	// Estado actual de la comunicación I2C
tR_W R_W_I2C; 				// Tipo de acceso al I2C

unsigned int BytesTransmitidos;// Contador de bytes transmitidos

unsigned char BufferI2C[256];	// Buffer donde se almacena la trama enviada / recibida
								// se puede dimensionar con la longitud que convenga según la aplicación

// Inicialización de la UART
void InitUART(void)
{
 U1MODE=0x8000; // 8 bits, sin paridad, 1 bit de stop
 U1STA =0x8400; 
 U1BRG =207; // 9600 baudios = (MIPS*1000000)/(16*9600)-1
}


/*******************************************************************
Inicializa I2C
********************************************************************/
#define FSCL 100000 // bus a 100KHz

void InitI2C(void)
{
 I2CBRG=MIPS/FSCL-MIPS/1111111-1; // FSCL=100kHz
 I2CCON=0x8000; // I2CEN=1 
                // I2CSIDL=0: módulo activo en modo "Idle"
                // SCLREL=0 : sin efecto
                // IPMIEN=0 : modo IPMI deshabilitado
                // A10M=0 : direcciones de 7 bits
                // DISSLW=0 : Slew Rate control habilitado
                // SMEN=0 : SMBus deshabilitado
                // GCEN=0 : no aplica
                // STREN=0 : Clock Stretching deshabilitado
                // ACKDT=0 : Transmite ACK durante la comunicación
                // ACKEN=RCEN=PEN=RSEN=SEN=0 : estados iniciales de la comunicación
}


void ComunicacionI2C(void)
{

 EstadoI2C=Start; 		// Estado inicial de la comunicación
 BytesTransmitidos=0; 	// Inicializamos el contador de bytes
 I2CCONbits.SEN=1; 		// Lanzamos la comunicación. A partir de ahora se encarga la interrupción
} 


// Interrupción I2C
void _ISR __attribute__((interrupt, auto_psv)) _MI2CInterrupt (void)
{
 IFS0bits.MI2CIF=0; // Borramos flag de interrupción
 if (COM_I2C.Sentido==Escritura)
 {
  // Acceso de escritura
  // *****************
  switch (EstadoI2C)
  {
   case Start : // Fin de secuencia START
   {
    I2CTRN=COM_I2C.Adr_I2C; 			// El primer byte de pDatas_I2C contiene la ADDRESS (A0 en eeprom)
    EstadoI2C=Envia;					// El siguiente paso será Envia
    break;
   }
   case Envia : 						// Ya se ha enviado otro byte
   {
    BytesTransmitidos++; 				// Incremento de nº de bytes transmitidos
    // los bytes 1 y 2 son la posición de memoria
    // Enviamos el byte de dirección que corresponda (alto y bajo)   
    if (BytesTransmitidos == 1) 
    {
     I2CTRN=COM_I2C.Direccion>>8;  		// enviamos la parte alta de la dirección
     break;
    }
    if (BytesTransmitidos == 2) 
    {
     I2CTRN=COM_I2C.Direccion & 0xFF;  	// enviamos la parte baja de la dirección
     break;
    }
    if (I2CSTATbits.ACKSTAT) 			// ¿Ha llegado un NACK?
    {
     EstadoI2C=Stop; 					// Activamos la secuencia de Stop
     I2CCONbits.PEN=1;     				// y finalizamos la comunicación
     break; 
    }
    if (BytesTransmitidos < (COM_I2C.Longitud + 3)) // ¿Todavía no hemos llegado al final?
    {
     I2CTRN=*COM_I2C.Datos; 			// Enviamos otro byte
     COM_I2C.Datos++;					// e incrementamos el puntero
     break;
    }
    if (BytesTransmitidos == (COM_I2C.Longitud + 3)) // ¿Hemos llegado al final?
    {
     EstadoI2C=Stop; 					// Activamos la secuencia de Stop
     I2CCONbits.PEN=1;     				// y finalizamos la comunicación
     break; 
    }
   }
   case Stop : // Final de secuencia
   {
    break;
   }
  }
 }
 else
 {
  // Acceso de lectura
  // ****************
  switch (EstadoI2C)
  {
   case Start : 						// Fin de secuencia START
   {
    I2CTRN=COM_I2C.Adr_I2C; 			// El primer byte de pDatas_I2C contiene la ADDRESS (A0 en eeprom)
    EstadoI2C=Envia;					// El siguiente paso será Envia
    break;
   }
   case Envia : 						// Ya se ha enviado otro byte
   {
    if (I2CSTATbits.ACKSTAT) 			// ¿Fin de la comunicación?
    {
     EstadoI2C=Stop; 					// NACK : El siguiente paso será STOP
     I2CCONbits.PEN=1;     				// Activar la secuencia de STOP
     break; 
    }
    BytesTransmitidos++; 				// Incremento de nº de bytes transmitidos
    // los bytes 1 y 2 son la posición de memoria
    // Enviamos el byte de dirección que corresponda (alto y bajo)   
    if (BytesTransmitidos == 1) 
    {
     I2CTRN=COM_I2C.Direccion>>8;  		// enviamos la parte alta de la dirección
     break;
    }
    if (BytesTransmitidos == 2) 
    {
     I2CTRN=COM_I2C.Direccion & 0xFF;  	// enviamos la parte baja de la dirección
     break;
    }
    if (BytesTransmitidos==3)  			// Ya hemos enviado la dirección completa, ahora toca RESTART
    {
     EstadoI2C=Restart; 				// Siguiente paso: RESTART
     I2CCONbits.RSEN=1;       			// Activamos
     break; 
    }
    if (BytesTransmitidos==4)  			// Se ha enviado ya la ID + Dir alto + Dir bajo. Ahora a recibir
    {
     EstadoI2C=Recibe;					// Siguiente paso: RECIBIR
     BytesTransmitidos=0;  				// Empezamos de nuevo el recuento de bytes recibidos
     I2CCONbits.RCEN=1; 				// Activamos secuencia de recepción
     break; 
    }
   }
   case Restart :						// Fin de la secuencia Restart
   {
    I2CCONbits.ACKDT=0; 				// A partir de ahora siempre contestaremos ACK 0
    I2CTRN=COM_I2C.Adr_I2C+1;			// Enviamos la dirección del dispositivo con el bit de lectura marcado
    EstadoI2C=Envia;     				// Siguiente paso: ENVIAR
    break;
   }
   case Recibe : 						// Se ha recibido un BYTE
   {
    BytesTransmitidos++;    			// Incrementamos el contador de bytes
    *COM_I2C.Datos=I2CRCV;				// Almacenamos el dato
 	COM_I2C.Datos++;    				// e incrementamos el array
   
    if (BytesTransmitidos==COM_I2C.Longitud) 
    	I2CCONbits.ACKDT=1; 			// En la siguiente devolvemos un NACK
    I2CCONbits.ACKEN=1;  				// Activamos la respuesta del ACK
    EstadoI2C=MAck;						// Y ese será el siguiente estado
    break;
   }
   case MAck : 							// Finalizamos la secuencia
   {
    if (BytesTransmitidos==COM_I2C.Longitud)
     {
      EstadoI2C=Stop; 					// NACK : El siguiente paso será STOP
      I2CCONbits.PEN=1;     			// Activar la secuencia de STOP
     }
    else 
     {
      EstadoI2C=Recibe;					// Nueva secuencia de recepción
      I2CCONbits.RCEN=1; 				// activamos la secuencia
     }
    break;
   }
   case Stop : // Final de secuencia
   {
    break;
   }
  }
 }
}



int main (void) {
	unsigned char i;
	
	InitUART(); // Inicializamos la USART
	InitI2C();  // Inicializamos el I2C 
	
	IFS0bits.MI2CIF=0; // Flag de interrupción Master
	IFS0bits.SI2CIF=0; // Flag de interrupción Slave
	IEC0bits.MI2CIE=1; // Activamos la interrupción "Master I2C"
	IEC0bits.SI2CIE=0; // No activamos la interrupción "Slave I2C"
	
	// Inicializamos el buffer con un valor determinado
	for (i=0;i<50;i++)
		BufferI2C[i]=i;
	
	// Vamos a iniciar una secuencia de escritura en la EEPROM
	COM_I2C.Adr_I2C=0xA0;			// Identificación de la EEPROM
	COM_I2C.Direccion=0x0000;		// Vamos a escribir en la dirección 0x0123
	COM_I2C.Datos=&BufferI2C;		// Puntero a nuestro Buffer
	COM_I2C.Longitud=50;			// Número de bytes a transmitir
	COM_I2C.Sentido=Escritura;		// Vamos a escribir
	ComunicacionI2C();				// Lanzamos la comunicación
	
	printf ("\n\rEscribiendo 50 bytes");
	delay_ms(100);
	
	// Ahora vamos a leer de la EEPROM lo que hemos escrito
	COM_I2C.Adr_I2C=0xA0;			// Identificación de la EEPROM
	COM_I2C.Direccion=0x0000;		// Vamos a leer desde la dirección 0x0123
	COM_I2C.Datos=&BufferI2C;		// Puntero a nuestro Buffer
	COM_I2C.Longitud=50;			// Número de bytes a transmitir
	COM_I2C.Sentido=Lectura;		// Vamos a leer
	ComunicacionI2C();				// Lanzamos la comunicación
	delay_ms(100);
	
	printf ("\n\rLeido: ");  
	
	for (i=0;i<50;i++)
		printf ("%u ",BufferI2C[i]);
	
	while (1) 
		{};
}
