Múltiples esclavos en i2c 18f4550

buen día.

acudo a ustedes con esta duda:

estoy empezando con i2c y he logrado la comunicación uno a uno sin problemas con estos pics, ahora bien mi problema se sale de control cuando intento hacer una simple petición de lectura a 2 esclavos desde un maestro.

el problema es que no envían datos los esclavos, lo he estado probando mediante un osciloscopio en proteus y resulta que los esclavos jamas contestan D:, tienen ustedes alguna idea del por q?

adjuntando aquí el código del maestro:
Código:
#include <18F4550.h>
#use delay (clock = 4M) 

#use i2c (MASTER,SDA = pin_c0, SCL = pin_c2) 
#use RS232 (baud = 9600, bits = 8, parity = N, xmit = pin_b0, rcv = pin_b1)


void main () {
  unsigned int x,y=0,r;
    while (true)
    {
            
     i2c_start();
     i2c_write(0xA1);//direccion del esclavo "a" con bit r/w "1" de lectura.
     x = i2c_read(); //lee sclavo 1
     i2c_stop();
     
     //delay_ms(10);
     /*
     i2c_start();
     i2c_write(0xB1);//direccion del esclavo "b" con bit r/w "1" de lectura.
     y = i2c_read(); //lee esclavo 2
     i2c_stop();*/
     
     printf("\rSlave A = %d ",x);
     printf("\rSlave B = %d ",y);
     printf("\rVuelta ============> %d ",r);
     delay_ms(1000);
     r++;
    }
}
y aquí el del primer esclavo: (NOTA: los esclavos solo tienen de diferencia el address y el dato
que envían.)
Código:
#include <18F4550.h>
#use delay (clock = 4M) 
#use i2c (SLAVE,SDA = pin_b0, SCL = pin_b1, ADDRESS = 0XA0,force_hw)
unsigned int c = 0,state = 0;

#INT_SSP                      
void sspinterupt()
{
  
   state = i2c_isr_state();   
   if(state == 0x80)
      {
      output_high(PIN_d5);
   
       delay_ms (500);
   
        output_low(PIN_d5);

      delay_ms(500);
         i2c_write(c);
         //delay_ms(10);
         //i2c_write(data1);   
        // delay_ms(10);
      }
}


void main() 
{
  enable_interrupts(INT_SSP);
  enable_interrupts(GLOBAL);
   
    while (true)
    {
    


      delay_ms(1000);
      c++;
    }
    
        
 }
a continuación el segundo esclavo:
Código:
#include <18F4550.h>
#use delay (clock = 4M) 
#use i2c (SLAVE,SDA = pin_b0, SCL = pin_b1, ADDRESS = 0XA0,force_hw)

unsigned int c = 0,state = 0;



/// Envio de 3 datos por I2C
#INT_SSP                      
void sspinterupt()
{
  
   state = i2c_isr_state();   
   if(state == 0x80)
      {
         i2c_write(c);
         // delay_ms(10);
          
      }
}

void main() 
{
    //enable_interrupts(INT_SSP);
    //enable_interrupts(GLOBAL);
   
    while (true)
    {
        
        output_high(PIN_d3);
   
        delay_ms (500);
   
        output_low(PIN_d3);
        
        delay_ms (500);
        c++;
    }

 }
cualquier pregunta o atención al código o tema queda agradecida desde antemano.
 
Última edición por un moderador:
Sin mirarlo mucho, en el código de los esclavos los dos tienen asignada la misma dirección: 0xA0.

#use i2c (SLAVE,SDA = pin_b0, SCL = pin_b1, ADDRESS = 0XA0,force_hw)

Y en el código de los maestros estás usando direcciones 0xA1 y 0xB1.

Hay que hacer que los 2 esclavos tengan direcciones distintas, y que sean iguales a las utilizadas en el código del maestro.
 
Ardogan, mucha razón, cambiando y actualizando :p... gracias... a-veces me meto tanto en el código que no veo errores así de simples, te aviso que paso : )

gracias
 
Última edición por un moderador:
Tras una mega demora por problemas de SW, adjunto los codigos de maestro y esclavos corregidos y aun causandome el mismo problema.

estoy empezando con i2c y he logrado la comunicación uno a uno sin problemas con estos PICS. Mi problema se sale de control cuando intento hacer una simple petición de lectura a 2 esclavos desde un maestro.

el problema es que no envían datos los esclavos, lo he estado probando mediante un osciloscopio en proteus y resulta que los esclavos jamas contestan D:, tienen ustedes alguna idea del por qué sucede?


adjuntando aquí el código del maestro:
Código:
#include <18F4550.h>
#use delay (clock = 4M) 

#use i2c (MASTER,SDA = pin_c0, SCL = pin_c2) 
#use RS232 (baud = 9600, bits = 8, parity = N, xmit = pin_b0, rcv = pin_b1)


void main () {
  unsigned int x,y=0,r;
    while (true)
    {
            
     i2c_start();
     i2c_write(0xA0+1); //direccion del esclavo "a" con bit r/w "1" de lectura.
     x = i2c_read(0);    //lee sclavo 1
      
     i2c_start();
     i2c_write(0xB0+1); //direccion del esclavo "b" con bit r/w "1" de lectura.
     y = i2c_read(0);    //lee esclavo 2
     i2c_stop();
     
     printf("\rSlave A = %d ",x);
     printf("\rSlave B = %d ",y);
     printf("\rVuelta ============> %d ",r);
     
     r++;
         output_high(PIN_b7);
         delay_ms (500);
         output_low(PIN_b7);
         delay_ms (500);
    }
}
y aquí el del primer esclavo: (NOTA: los esclavos solo tienen de diferencia el Address y el dato que envían.)
Código:
#include <18F4550.h>
#use delay (clock = 4M) 
#use i2c (SLAVE,SDA = pin_b0, SCL = pin_b1, ADDRESS = 0XA0,force_hw)
unsigned int c = 2,state = 0;

#INT_SSP                      
void sspinterupt()
{
   state = i2c_isr_state();   
   if(state == 0x80)
      {
         output_high(PIN_c0); // este LED es para verificar la entrada a la interrupción
         delay_ms (500);
         output_low(PIN_c0);
        delay_ms(500);

        i2c_write(c);
      }
}
void main() 
{
  enable_interrupts(INT_SSP);
  enable_interrupts(GLOBAL);
  while (true)
    {
      c++;
    }
 }
A continuación el segundo esclavo:
Código:
#include <18F4550.h>
#use delay (clock = 4M) 
#use i2c (SLAVE,SDA = pin_b0, SCL = pin_b1, ADDRESS = 0XB0,force_hw)

unsigned int c = 0,state = 0;

#INT_SSP                      
void sspinterupt()
{
   state = i2c_isr_state();   
   if(state == 0x80)
      {
         
         output_high(PIN_d3);  // este LED es para verificar la entrada a la interrupción
         delay_ms (500);
         output_low(PIN_d3);
         delay_ms (500);
         
         i2c_write(c);    
      }
}
void main() 
{
    enable_interrupts(INT_SSP);
    enable_interrupts(GLOBAL);
    while (true)
    {   
        c++;
    }
 }

De nuevo, agradezco cualquier pregunta o atención al código o tema similar.
 
Nunca use el compilador CCS, pero mi breve experiencia con el bus I2C me dice que:

  1. En el código del maestro habria que poner un i2c_stop al terminar la comunicación con un esclavo y empezar la comunicación con el otro esclavo.
  2. En el código del esclavo se podría escribir al buffer (hacer un i2c_write) inicial antes de entrar al while(true). Por más que el maestro todavía no haya pedido la información el esclavo ya la pone en el buffer por adelantado, lo que puede ser útil para depurar. Luego si eso funciona ahí se puede probar por interrupciones, pero conviene empezar por lo sencillo y luego escalar.
  3. En el código de la interrupción del esclavo, hacer los retardos antes de enviar la información que pide el maestro es contraproducente. Primero hacer el i2c_write, luego los retardos.
Faltaría saber en que punto particular surge el problema. ¿Es posible depurar paso a paso el programa del maestro? -> ¿tenés un depurador?.
En tal caso sugiero ir paso a paso por el programa del maestro para ver si el programa se queda colgado, en donde, y verificar los valores de las variables.


De lo contrario se puede probar encendiendo/apagando uno o más leds para saber en donde el programa se queda congelado.


Usar leds puede ser útil del lado del esclavo, para ver si entró en la rutina de interrupción, si está ejecutando el while(true); para por ejemplo ver si los leds dejan de parpadear sabemos que el programa se quedó congelado.


Es importante poder depurar bien, al momento de escribir código ir muuuuy despacio, nunca más de 1 función a la vez, compilar, programar, y probar. De lo contrario se termina escribiendo una parva de código asumiendo cosas que pueden no ser ciertas, y al momento de probar todo se para y es un nudo muy difícil de desenredar.


Suerte!!!.



Otra cuestión importante!!!

En el código de interrupción del esclavo se está escribiendo la variable state.
Todas las variables que se escriba dentro de una rutina de interrupción debe ser declaradas como volatile, no se si el compilador CCS utilizará una palabra distinta.
En este caso quizás no afecte al funcionamiento del programa (porque se escribe y lee dentro de la rutina de interrupción, pero no se toca la variable fuera de ella), pero nunca se sabe... conviene tenerlo como una regla.
 
Última edición:
woooo... genial con tu respuesta me llevo un poco de tiempo incrementar mi nivel de depuración a lo cual surgieron algunas preguntas a tus sugerencias así que intentare llevar esto punto por punto

Nunca use el compilador CCS, pero mi breve experiencia con el bus I2C me dice que:
En el código del maestro habria que poner un i2c_stop al terminar la comunicación con un esclavo y empezar la comunicación con el otro esclavo.
Al intentar usar un STOP intermedio, no obtuve ninguna diferencia. bueno esto es porque fue de las ultimas sugerencias con las que provee. sin embargo, me di cuenta que si realizo las lecturas de forma independiente, es decir, comentando una y la otra no, y viceversa ambas funcionan correctamente. crei que al poner el stop como lo sugieres ambas funcionarían, sin embargo no es así. el logro principal es:
Que en la primera vuelta si realiza la primera lectura del esclavo 1, pero no la del esclavo 2.

En el código del esclavo se podría escribir al buffer (hacer un i2c_write) inicial antes de entrar al while(true). Por más que el maestro todavía no haya pedido la información el esclavo ya la pone en el buffer por adelantado, lo que puede ser útil para depurar. Luego si eso funciona ahí se puede probar por interrupciones, pero conviene empezar por lo sencillo y luego escalar.
Con respecto a esta sugerencia T_T, lo siento comprendo la idea pero no como realizarla D:.
En el código de la interrupción del esclavo, hacer los retardos antes de enviar la información que pide el maestro es contraproducente. Primero hacer el i2c_write, luego los retardos.
en este aspecto tenias toda la razon. y gracias a esto funciono la primera lectura del slave uno, lo malo es que sigo sin descubrir por que razon no vuelve a leer : (.

Faltaría saber en que punto particular surge el problema. ¿Es posible depurar paso a paso el programa del maestro? -> ¿tenés un depurador?.
En tal caso sugiero ir paso a paso por el programa del maestro para ver si el programa se queda colgado, en donde, y verificar los valores de las variables.
desgraciadamente no tengo un depurador, pero con la ayuda del osciloscopio virtual de proteus, he logrado averiguar unos detalles un tanto inquietantes. a lo que mi duda principal, es que yo no puedo controlar los tiempos que tienen START y STOP de bajada y subida en SDA y SCL vdd? la razon es porque noté que SDA y SCL se mueven simultáneamente, es decir, no deberían estar desfasadas? D:


Usar leds puede ser útil del lado del esclavo, para ver si entró en la rutina de interrupción, si está ejecutando el while(true); para por ejemplo ver si los leds dejan de parpadear sabemos que el programa se quedó congelado.
seep estos foquitos geniales fueron y parece que seguirán siendo mis amigos en la depuración. sin embargo, en que programas tu? hay alguna otra clase de depurador que pueda usar?
Otra cuestión importante!!!
En el código de interrupción del esclavo se está escribiendo la variable state.
Todas las variables que se escriba dentro de una rutina de interrupción debe ser declaradas como volatile, no se si el compilador CCS utilizará una palabra distinta.
En este caso quizás no afecte al funcionamiento del programa (porque se escribe y lee dentro de la rutina de interrupción, pero no se toca la variable fuera de ella), pero nunca se sabe... conviene tenerlo como una regla.
realizado, así es también agregue este detalle a los códigos, sin embargo, aun no consigo dar con el detalle.

en conclusion:
las peticiones de lectura desde el maestro son perfectamente realizadas si son individuales (comentando la otra) pero en forma conjunta y incluso siendo intercaladas por señales de STOP aun no me han dado resultado.

me he sorprendido un poco, porque me parece extraño no encontrar algún ejemplo de consulta similar. en un medio con múltiples esclavos.

sin mas que decir, y agradeciendo de antemano a Ardogan por las ideas y comentarios. solo me queda seguir probando. >.<!



también hay algo que no me queda claro, el sistema de ACK es automático? o tengo que manejarlo, es posible que algo como esto pueda estarme causando el error?
 
Última edición por un moderador:
Al intentar usar un STOP intermedio, no obtuve ninguna diferencia. bueno esto es porque fue de las ultimas sugerencias con las que provee. sin embargo, me di cuenta que si realizo las lecturas de forma independiente, es decir, comentando una y la otra no, y viceversa ambas funcionan correctamente. crei que al poner el stop como lo sugieres ambas funcionarían, sin embargo no es así. el logro principal es:
Que en la primera vuelta si realiza la primera lectura del esclavo 1, pero no la del esclavo 2.

Interesante, hay un problema con la condición de stop... ¿estás usando pull-ups en las líneas de datos y reloj?.
No sé como estará tu código fuente ahora, pero mi propuesta era:
Maestro: stop, start, escribir dir esclavo 1, leer, stop, start, escribir dir esclavo 2, leer, stop
¿Que pasa si intentás leer 2 veces del mismo esclavo?. ¿Entra más de 1 vez a la rutina de interrupción o solo 1?.
Otra cuestión: puede ser necesario en el código de esclavo limpiar el flag de interrupción manualmente del puerto serie SSP. Quizás ya se hace automáticamente, no lo sé.
Parece que con CCS la función es clear_interrupt(INT_SSP); fuente:
http://www.ccsinfo.com/forum/viewtopic.php?p=86924

desgraciadamente no tengo un depurador, pero con la ayuda del osciloscopio virtual de proteus, he logrado averiguar unos detalles un tanto inquietantes. a lo que mi duda principal, es que yo no puedo controlar los tiempos que tienen START y STOP de bajada y subida en SDA y SCL vdd? la razon es porque noté que SDA y SCL se mueven simultáneamente, es decir, no deberían estar desfasadas? D:
Mmmm... no te preocupes por eso, es cosa que ya maneja automáticamente el hardware; además si pudiste leer 1 byte no creo que sea problema de formas de onda.

seep estos foquitos geniales fueron y parece que seguirán siendo mis amigos en la depuración. sin embargo, en que programas tu? hay alguna otra clase de depurador que pueda usar?

Hace un tiempo que no uso pics. Pero usaba el ICD2 con el mplabC18 (con pic18f4550/2550). Creo que ahora la herramienta más efectiva es el PicKit3.

también hay algo que no me queda claro, el sistema de ACK es automático? o tengo que manejarlo, es posible que algo como esto pueda estarme causando el error?

Cierto... pero supongo que las rutinas de escritura/lectura ya lo harán automáticamente. No he visto que sean utilizadas en forma directa hasta ahora.

Ahora que vuelvo a ver el código, creo que el problema es el retardo dentro de la rutina de interrupción de los esclavos. Normalmente no es recomendable hacer retardos dentro de una rutina de interrupción. Te diría que en vez de eso (mantener un led prendido por cierto tiempo y luego apagarlo), conmutes el pin del led (¿función output_toggle ?), si el led cambia de estado entonces quiere decir que entra a la rutina de interrupción.
 
wooooo ^^! como siempre muy acertadas las ideas... probando... grax Ardogan. Publico una vez intente todas estas nuevas ideas.

(lamento todas las faltas de ortografía que tengo, sres. Administradores T_T, cuando me emociono escribiendo pierdo la noción de esos detalles, tratare de no poner tantos )
 
Atrás
Arriba