# Programacion CCS y I2C



## jomocba (Jul 10, 2010)

Hola, soy nuevo en el foro.

Vereis tengo una duda, estoy trabajando en un proyecto con 2 PIC'S 18f2550, intento comunicarlos por I2C pero nose que hare mal por que no funciona. El primer pic realiza el control de unos motores mediante una señal PWM mientras que el otro se encarga de obtener medidas para indicar que accion debe realizar el otro pic.

El problema viene aqui nada mas empezar :S no consigo hacer que se comuniquen los dichosos chips, podeis hecharme una ayudita? 

estos son los codigos:

MAESTRO

```
#include <18F2550.h>
#fuses HS,USBDIV,PLL5
#use delay(clock=20000000)
#use I2C(MASTER, sda=PIN_B0, scl=PIN_B1, FAST,  FORCE_HW)

//////////////////////////////////////////////////////////////////////
////////////////////////////DEFINES///////////////////////////////////
//////////////////////////////////////////////////////////////////////

#define ENCENDER output_high
#define APAGAR output_low
#define LED_PIC PIN_C6

//////////////////////////////////////////////////////////////////////
///////////////////////////VARIABLES//////////////////////////////////
//////////////////////////////////////////////////////////////////////

int posicion_servo1 = 0;
int posicion_servo2 = 0;

/////////////////////////////////////////////////////////////////////
///////////////////////////INTERRUPCIONES////////////////////////////
/////////////////////////////////////////////////////////////////////
#INT_TIMER2
void PWM_movimiento_servos(void)
{
	//mirar de mejorar el movimiento de los servos
}


/////////////////////////////////////////////////////////////////////
/////////////////////////////FUNCIONES///////////////////////////////
/////////////////////////////////////////////////////////////////////
void escribir_i2c(BYTE direccion, int dato)
{
	i2c_start();
	i2c_write(direccion);
	i2c_write(dato);
	i2c_stop();
	delay_us(50);
}

int leer_i2c(BYTE direccion, int opcion)
{
	int dato = 0;
	i2c_start();
	i2c_write(direccion);
	i2c_write(opcion);
	
	i2c_start();
	i2c_write(direccion+1);
	dato = i2c_read();
	i2c_stop();
	
	return(dato);
}

void parpadeo()
{
		ENCENDER(LED_PIC);
		delay_ms(500);
		APAGAR(LED_PIC);
		delay_ms(500);
}
/////////////////////////////////////////////////////////////////////
///////////////////////////////MAIN//////////////////////////////////
/////////////////////////////////////////////////////////////////////
void main(void) 
{
	setup_timer_2(T2_DIV_BY_1,249,2); //inicializacion de las interrupciones del timer 2 a 0.1 ms cada una
	set_timer2(0); //inicializar a 0 el timer2

	delay_ms(1000); //esperamos 1 segundo que para asegurarnos que se inicialice toda la placa antes de empezar la fiesta
	
	enable_interrupts(INT_TIMER2); //interrupcion del timer 2
	enable_interrupts(GLOBAL); //habilitamos las interrupciones
	
	while(TRUE)
	{

				posicion_servo1 = leer_i2c(0xa0, 1); //leer posicion en la que debe situarse el servo 1
				posicion_servo2 = leer_i2c(0xa0, 2); //leer posicion en la que debe situarse el servo 1
	};
}
```

ESCLAVO

```
#include <18F2550.h>
#fuses HS,USBDIV,PLL5
#use delay(clock=20000000)
#use I2C(SLAVE, sda=PIN_B0, scl=PIN_B1, ADDRESS= 0xa0, FAST, FORCE_HW)

//////////////////////////////////////////////////////////////////////
////////////////////////////DEFINES///////////////////////////////////
//////////////////////////////////////////////////////////////////////

#define ENCENDER output_high
#define APAGAR output_low
#define LED_PIC PIN_C6

//////////////////////////////////////////////////////////////////////
///////////////////////////VARIABLES//////////////////////////////////
//////////////////////////////////////////////////////////////////////

BYTE i2c_dato_entrada; //dato que se escribe mediante I2C
BYTE direccion = 0x00;
int posicion_servo1 = 0;
int posicion_servo2 = 0;

/////////////////////////////////////////////////////////////////////
///////////////////////////INTERRUPCIONES////////////////////////////
/////////////////////////////////////////////////////////////////////
#INT_SSP
void ssp_interupt ()  //salta una interrupcion y nos disponemos a tratarla
{
	byte temp, i2c_estado, i2c_dato_funcion;

	i2c_estado = i2c_isr_state();  //leemos el valor del isr (el que nos indica que accion debemos tratar, o bien leer o bien escribir)
	if(i2c_estado <0x80)  //el master te esta enviando informacion por lo que debemos leer lo que nos dice
	{
		temp = i2c_read();
		if(i2c_estado == 1)
		{
			direccion = temp;
		}
		if(i2c_estado == 2)
		{
			//este dato puede ser o bien la funcion a realizar o bien el valor que queremos introducir al pic mediante I2C
			i2c_dato_entrada = i2c_read(); //leemos "dato" del master, si hubieran mas datos habria que hacer mas reads, en nuestro caso solo leemos un byte por cada funcion de lectura en el esclavo (el master es el que escribe)
		}
	}
	if(i2c_estado >= 0x80) //el master esta pidiendonos un dato con lo que debemos responderle con un write
	{
		i2c_dato_funcion = i2c_dato_entrada;
		if(i2c_dato_funcion == 1) //el master nos pide la posicion del servo1
		{
			i2c_write(posicion_servo1);
		}
		if(i2c_dato_funcion == 2) //el master nos pide la posicion del servo2
		{
			i2c_write(posicion_servo2);
		}
	}
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////FUNCIONES///////////////////////////////
/////////////////////////////////////////////////////////////////////

int posicion_servo1_mediante_triangulacion(int receptor1,int receptor3, int posicion_servo1)
{
	if(receptor1 < receptor3)
	{
		posicion_servo1 = posicion_servo1 + 1;
	}
	else if(receptor1 > receptor3)
		{
			posicion_servo1 = posicion_servo1 - 1;
		}

	return(posicion_servo1);
}

int posicion_servo2_mediante_triangulacion(int receptor2,int receptor3, int posicion_servo2)
{
	if(receptor3 < receptor2)
	{
			posicion_servo2 = posicion_servo2 - 1;
	}
	else if(receptor3 > receptor2)
		{
			posicion_servo2 = posicion_servo2 + 1;
		}

	return(posicion_servo2);
}
/////////////////////////////////////////////////////////////////////
///////////////////////////////MAIN//////////////////////////////////
/////////////////////////////////////////////////////////////////////
void main(void) 
{
   enable_interrupts(INT_SSP);
   enable_interrupts(GLOBAL);

   while(TRUE)
   {
		
   };
}
```

Como podeis ver faltan las funciones de obtener el valor de los receptores, pero estoy inicializando los valores de la posicion_servo1 y 2 a un valor y este valor no se trasmite al otro pic. el maestro si que parece que emita la transmision I2C pero el esclavo no hace nada, me e equivocado con la #int_ssp? alguno sabe por que no funciona?

Gracias de antemano.


----------



## ednacarolina (Jul 19, 2010)

Hola, yo he tenido problemas en ccs el compilador no muestra errores pero a la hora de probar en protoboard no va como se supone q da en la simulacion. Estoy simulando con proteus 7.6 sp4. Pero como 
te digo compila bien pero no me funciona he provado varias alternativas q he escontrado en el foro iepero sigue sin funcionarme.
Has tenido algun avance en tu proyecto?


----------



## jomocba (Jul 20, 2010)

Que va en cuanto a simulacion no e avanzado nada, estoy terminando de desarrollar el integrado y ire a base de pruebas sobre el hardware.


----------



## CEROVOLT (Mar 24, 2012)

Hola amigo , soy nuevo en el foro pero me parece interesante esto del i2c , y la forma como se comunican los pics , espero que se te haya solucionado tu problema.

Ademas tengo algunas dudas con el codigo del esclavo.


----------



## gdtxz (Abr 10, 2012)

para los que pasen, revisen que tienen las resistencias de Pull-UP en SDA y SCL


----------



## chrisck87 (Abr 30, 2012)

Bueno tu publicacion fue del 2010, no se si para hoy aun necesitaras esa respuesta, pero para otros colegas que necesiten esa info aqui va la respuesta.

cuando trabajas con los pics para comunicarse con i2c, no es recomendable utilizar la condicion if(state>=0x80) para determinar si el maestro te esta pidiendo datos, ya que cuando el pic maestro envia una trama i2c luego de enviar un request que es con un state mayor a 0x80 envia 2 o 3 tramas mas y eso hará que tus pics se "cuelguen" ya que el maestro seguira esperando los ack.

Te doy una parte de un código que hice y creo k te ayudará.

EN EL ESCLAVO

```
#use i2c(SLAVE,fast,sda=PIN_C4,scl=PIN_C3,address=0x10)
#define Pide_Dato1 2
#define Pide_Dato2 3

int8 state, Direc_master, iic_dato;

#int_SSP
void  SSP_isr(void) 
{
   state = i2c_isr_state();
   if(state==0)
   {
      Direc_Master=i2c_read();
   }
   
   if(state < 0x80)
   {
      iic_dato=i2c_read();
      comando=iic_dato;
      //Aqui colocas lo que necesites hacer con el dato enviado por el maestro
   }
   if(state == 0x80)
   {
      switch(comando)
      {
         Case Pide_Dato1:
            i2c_write(Dato1);
            break;
         Case Pide_Dato2:
            i2c_write(Dato2);
            break;
      }
   }
}
```

En el caso del código del esclavo te darás cuenta que solo pregunto por el state=0x80, además en el caso del maestro lo primero que se debe hacer es enviar el comando de peticion y después de eso recien se hara la lectura.

EL CODIGO DEL MAESTRO


```
#use i2c(MASTER,fast,sda=PIN_B0,scl=PIN_B1)
#define Pide_Dato1 2
#define Pide_Dato2 3
int8 Dato_rcv_1;

   i2c_start();
   delay_us(100);
   i2c_write(0x10);
   delay_us(100);
   i2c_write(Pide_Dato1);
   delay_us(100);
   i2c_stop();

   i2c_start();
   delay_us(100);
   i2c_write(0x10|1);
   delay_us(100);
   Dato_rcv_1=i2c_read(0);
   delay_us(100);
   i2c_stop();
```

En el caso del maestro notarás que cuando leo el dato coloco i2c_read(0), ese cero me ayuda a evitar retrasos por lectura del ack.

No he tenido problemas usando ese código, pruebalo y pues exitos.


----------



## boadacr (May 1, 2012)

Muy buenas a todo, activo este foro para preguntar sobre que errores podria tener en la programación de mi comunicacion i2c que trato de realizar. Os situo:
Trato de hacer la comunicación de un master/slave por i2c. El Slave recoge datos de 8 pulsadores que le entran por el puertoB. A la vez que va recogiendo los datos los transmite directamente al puerto D del mismo Slave (por confirmar que funciona la comunicación correctamente). El maestro solicita la información del slave sobre el estado de los pulsadores para sacarlo por el puerto B suyo, con lo cual lo que quiero es ver lo mismo en el puerto D del slave y en el puerto B del master. Hasta ahi lo he conseguido, ahora lo que quiero para probar la bidireccionalidad de la comunicación es mediante un simple pulsador en el master, lograr que se enciendan todos los leds del puerto D del slave. Os cuelgo aqui el programa como lo tengo en la actualidad:

MAESTRO:

```
#include "C:\Users\Cristian\Desktop\Prueba CCS\I2c Puertas\MAESTRO MANOLO\MAESTRO\maestro manolo.h"
#byte trisb=0x86
#byte portb=0x06
#byte trisd=0x88
#byte portd=0x08

BYTE luces;
int8 dato=255;

void leer_esclavo()
   {
   i2c_start();
   i2c_write(0xA0);
   i2c_write(0x02);
   i2c_stop();
   
   i2c_start();  
   i2c_write(0xA0+1);
   luces = i2c_read(0);
   i2c_stop();
   }

void escribir_esclavo(void)
{
   i2c_start();
   i2c_write(0xA0); // como son 2 cifras hexadecimales 
   i2c_write(dato); // ponemos XXXXYYYY donde XXXX es el 
   i2c_stop();      // dispositivo y YYYY es el codigo 
                    // de lectura o escritura.
}

void main()
{
set_tris_b(0x00);
set_tris_d(0xFF);

   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_psp(PSP_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);

   // TODO: USER CODE!!
while(1)
{
 leer_esclavo();
 delay_ms(20);
 portb=luces;
 if(input_state(PIN_D1)==1)
   {
   escribir_esclavo();
   delay_ms(20);
   }
 
}
}
```


ESCLAVO:


```
#include "C:\Users\Cristian\Desktop\Prueba CCS\I2c Puertas\MAESTRO MANOLO\ESCLAVO\esclavo manolo.h"

#byte trisb=0x86
#byte portb=0x06
#byte trisd=0x88
#byte portd=0x08
#byte trisa=0x85
#byte porta=0x05

BYTE luces,estado,rdato,dato,direccion;
#int_SSP
void  SSP_isr(void) 
{
   estado = i2c_isr_state();
   if(estado<0x80)        //Maestro escribiendo en el esclavo
   {
       dato=i2c_read();
       if(estado==1)
       {
         direccion=dato;
       }
       if(estado==2)
       {
         rdato=dato;
         portd=rdato;
       }
       
   }
   else if(estado>=0x80)        //Maestro solicitando datos del esclavo
       i2c_write(luces);
       
}



void main()
{
set_tris_b(0xFF);
set_tris_d(0x00);
set_tris_a(0x00);
   port_b_pullups(TRUE);
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_psp(PSP_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   enable_interrupts(INT_SSP);
   enable_interrupts(GLOBAL);

   // TODO: USER CODE!!
while(1)
 {
 luces=portb;
 portd=luces;
 }
}
```


Os adjunto el archivo del esquema proteus. Con estos codigos que os he pegado, lo que le ocurre es que se encienden todos los leds del puerto B del master le de o no al button, es decir estan encendidos continuamente. Agradeceria una ayuda a la hora de detectar los errores que esten escritos, ya que mi conocimiento con este tipo de comunicación no es nada extenso.


Muchas gracias.


----------



## picolandia (May 6, 2012)

buenas!
estoy intentando realizar lo mismo pero en vez de dos pics tengo una brujula electronica como esclavo y un dspic 30f4013 como maestro; lamentablemente el unico lenguaje que se me facilita es el lenguaje ensamblador, he tenido ganas de pasarme a C pero ya estoy un poco avanzado con este tipo de lenguaje.
el problema q tengo es el siguiente: ya tengo conocimiento de los comandos a implementar para realizar la comunicacion maestro esclavo pero no se que delays sean necesarios implementar para cada funcion que realice el maestro y el esclavo; alguien me podria ayudar con un ejemplo muy sencillo? 
Gracias


----------



## Palvulito (May 13, 2012)

Hola a todos, espero que me puedan ayudar a correjir mi error. Estoy haciendo un programa en ccs c, este programa trata de sensar voltaje de tal manera que se toman dos valores ( valor1 y valor2 ) separados cada 1mseg los cuales se comparan y si es mayor el valor1 que el valor2, mediante 12c pide la hora al ds1307 y guarda la hora en una memoria eeprom externa, si el valor1 es menor al valor2 hace lo mismo, pide y guarda la hora, si es igual el valor1 a 1023 del adc el lcd imprime un mensaje de encendido, si el valor1 es igual 0 el lcd imprime el mensaje de apagado y es aqui el problema porque no lo hace y aparte se salta las dos comparaciones anteriores, pero cuando quito la parte en que el lcd mande algun mensaje funciona bien, no se si la parte del lcd afecte la comunicacion 12c, otro problema que tengo es que hise un 'menu' con un switch pero no funciona, lo hise aumentando una variable, no se si este haciendo bien la parte de los case dejo mi codigo para que me puedan ayudar a encontrar mi error.

#include <18F4550.H>
#device ADC = 10
#fuses XT, MCLR, NOWDT, NOPROTECT, NOPUT, NOLVP
#use delay( clock = 4M )
#use I2C( Master, SCL = PIN_C0, SDA = PIN_C1, SLOW )
#include <LCD.C>

int Horas, Minutos, Segundos;
int16 Vadc, Vax, Dir;

void WEEPROM( long int Dir, BYTE Dato )
   {
      short int h;

      i2c_start();      //Inicia la comunicacion.
      i2c_write( 0xA0 );   //Direccion de memoria para escribir.
      i2c_write( Dir >> 8 );  //Inicia en la parte de arriba de la memoria.
      i2c_write( Dir );    //Y Despues en la parte de abajo de la memoria.
      i2c_write( Dato );   //Dato que se quiere escribir.
      i2c_stop();          //Finaliza la comunicacion. 
      i2c_start();      //Inicia la escritura.

      h = i2c_write( 0xA0 );

      while( h == 1 )
         {
            i2c_start();   //Inicia la escritura.
            h = i2c_write( 0xA0 );
         }
   }

BYTE REEPROM( long int Dir )
   {
      BYTE Date;

      i2c_start();      //Inicia la comunicacion.
      i2c_write( 0xA0 );   //Direccion de memoria para escribir.
      i2c_write( Dir >> 8 );  //Inicia en la parte de arriba de la memoria.
      i2c_write( Dir );    //Y Despues en la parte de abajo de la memoria.
      i2c_start();         //Inicia la lectura.
      i2c_write( 0xA1 );   //Direccion de memoria para leer.
      Date = i2c_read( 0 );
      i2c_stop();          //Finaliza la comunicacion.

      return ( Date );
   }

int BCD2BIN( int BCD )
   {
      int Temp;
      Temp = BCD;
      Temp >>= 1;
      Temp &= 0x78;
      return( Temp + ( Temp >> 2 ) + ( BCD & 0x0F ) );
   }

void Obtim( byte &Hrs, byte &Min, byte &Seg )
   {
      i2c_start();      //Inicia la comunicacion.
      i2c_write( 00 );   //Direccion de DS1307 para escribir.
      i2c_write( 0x00 );   //Inicia en la direccion 0.
      i2c_start();      //Inicia la lectura.
      i2c_write( 01 );//Direccion de DS1307 para leer.
      Seg = BCD2BIN( i2c_read() & 0x7F );    //Lee los segundos.
      Min = BCD2BIN( i2c_read() & 0x7F );    //Lee los minutos.
      Hrs = BCD2BIN( i2c_read( 0 ) & 0x3F ); //Lee las horas.
      i2c_stop();       //Finaliza la comunicacion.
   }

void Guardar( void )
   {
      Obtim( Horas, Minutos, Segundos );
      delay_us( 5 );

      WEEPROM( Dir++, Horas );
      WEEPROM( Dir++, Minutos );
      WEEPROM( Dir++, Segundos );

      if( Dir == 0xFFFF )
         {
            Dir = 0;
         }         
   }

void Leer( void )
   {
      Horas  = REEPROM( Dir++ );
      Minutos = REEPROM( Dir++ );
      Segundos = REEPROM( Dir++ );
   }

void main( void )
   {
      int Band = 0;

      lcd_init();

      Dir = 0;

      setup_adc_ports( AN0 );
      setup_adc( ADC_CLOCK_INTERNAL );
      set_adc_channel( 0 );

      while( 1 )
         {
            if( INPUT( PIN_C2 ) == 0 )
               {
                  Band  = Band + 1;
               }
            switch( Band )
               {
                  case 1:
                  Vax = read_adc();
                  delay_ms( 1 );
                  Vadc = read_adc();

                  output_high( PIN_D0 );
                  output_low( PIN_D1 );

                  if( Vax < Vadc  )
                     {
                        delay_us( 5 );
                        Guardar();
                        output_high( PIN_C5 );
                        output_low( PIN_C6 );
                     }
                  if( Vax > Vadc )        //esta el la parte que no hace
                     {
                        delay_us( 5 );
                        Guardar();
                        output_low( PIN_C5 );
                        output_high( PIN_C6 );
                     }
                  if( Vax == Vadc )    //esta el la parte que no hace
                     {
                        output_low( PIN_C5 );
                        output_low( PIN_C6 );

                        if( Vax == 0 )
                           {
                              lcd_gotoxy( 1, 1 );
                              printf( lcd_putc, "\fApagado" );
                              output_low( PIN_C7 );
                           }
                        else
                           {
                              lcd_gotoxy( 1, 1 );
                              printf( lcd_putc, "\fEncendido" );
                              output_high( PIN_C7 );
                           }
                     }
                  break;

                  case 2:
                  output_low( PIN_D0 );
                  output_high( PIN_D1 );
                  output_low( PIN_C5 );
                  output_low( PIN_C6 );
                  output_low( PIN_C7 );
                  Band = 0;
                  break;
               }   
         }   
   }


----------



## ID619 (Jun 7, 2012)

chrisck87 dijo:


> que tal chrisck87 buen aporte, solo me quedo la duda de ver tus conexiones en una simulación, para ver como se ve y si físicamente lo pueda conectar, ha y tocando el tema de las conexiones, me pregunto si sabes como se calcula el valor de las resistencias de pull-up, si dependen del numero de elementos que estén conectados en las lineas SDA y SCL?


----------



## ID619 (Jun 7, 2012)

hola de nuevo chisck87 oye analizando tu codigo, me queda la duda si *0x80* se refiere a un registro del pic que utilizaste para implementarlo ó acaso es el numero 80 (ocheta) en binario???


----------

