Pic Maestro y varios esclavos

Saludos.

Buscando en el foro siempre me tope con temas similares, pero no logre obtener información detallada para mi proyecto:

La idea es implementar un sistema de adquisición de datos.

PIC18F452 como maestro y dos PIC16F877A.

No utilizo I2C, SPI. Por cuestiones de diseño debo usar RS 232.
No es una comunicación convencional: Half-Duplex, Asincrono.

El maestro envía el identificador del pic esclavo que desea consultar mediante una cadena de caracteres. Esta cadena es recibida "simultaneamente" por los esclavos.

El pic esclavo que verifique su id. es el que toma la linea y envia sus datos al maestro.

Luego el maestro recibe los datos y pasa a consultar con el sgt pic esclavo, prepara la trama completa y envia al PC. Y luego vuelva a consultar...

Los problemas aparecen cuando trato de comunicar el maestro y los esclavos.
Utilizo un tri-state para tomar la linea y enviar datos al maestro.

La simulacion en proteus 7.8, entre una terminal virtual (q hace de maestro) y los pic esclavos funciona perfectamente, pero al colocar al maestro se arruina.

Subo los codigos fuente y los esquemas.

Código:
////////////////////////////////////////////////////////
MAESTRO .c

#include <main_MASTER.h>
#include <stdlib.h>

#define c_max 2                        //cantidad de esclavos a consultar por datos

void main()
{
   int id_cont = 0;
   char buffer_t[15];
   char buffer_p[5];
  
   while(true)
   {
      delay_ms(2000);                  //espera 2s
      strcpy(buffer_t, "x");           //caracter de inicio del buffer total
      
      while(id_cont < c_max)
      {
         strcpy(buffer_p, "");         //limpia el buffer parcial
         itoa(id_cont, 10, buffer_p);  //convierte int a string
         fputs(buffer_p, A_PICS);      //envia id a esclavos
         fputc(13, A_PICS);            //enter

         strcpy(buffer_p, "");         //limpia el buffer parcial
         fgets(buffer_p, A_PICS);      //lee datos de esclavo correspondiente
         delay_ms(100);        
         strcat(buffer_t, buffer_p);   //concatena al buffer total el parcial
         id_cont = id_cont + 1;        //prepara cont para sgt pic esclavo
      }
      id_cont = 0;
      
      fputs(buffer_t, A_COM);         //escribe la cadena hacia el computador
      strcpy(buffer_t, "x");          //limpia el buffer total 
   }
}

/////////////////////////////////////////

MAESTRO .h

#include <18C452.h>
#device adc=16

#FUSES NOWDT                 	//No Watch Dog Timer
#FUSES WDT128                	//Watch Dog Timer uses 1:128 Postscale
#FUSES NOPROTECT             	//Code not protected from reading
#FUSES XT                    	//Crystal osc <= 4mhz for PCM/PCH , 3mhz to 10 mhz for PCD
#FUSES NOOSCSEN              	//Oscillator switching is disabled, main oscillator is source
#FUSES NOPUT                 	//No Power Up Timer
#FUSES NOBROWNOUT            	//No brownout reset
#FUSES BORV20                	//Brownout reset at 2.0V
#FUSES STVREN                	//Stack full/underflow will cause reset

#use delay(clock=4000000)
#use rs232(baud=2400,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=A_PICS)
#use rs232(baud=2400,parity=N,xmit=PIN_D0,rcv=PIN_D1,bits=8,stream=A_COM)

////////////////////////////////////////////////

Esclavo 0 .c

#include <main_S0.h>
#INCLUDE <stdlib.h>

#define id_pic 0                    //identificador del pic esclavo 0

void main()
{
   char buffer_s[5];
   OUTPUT_LOW(PIN_C1);              //Deshabilita linea TX
   
   while(true)
   {
      strcpy(buffer_s, "");         //inicializo buffer
      fgets(buffer_s, A_MASTER);    //leo buffer del master
      if(atoi(buffer_s) == id_pic)  //verifica si se pide informacion a este pic
      {
         OUTPUT_HIGH(PIN_C1);       //habilita linea TX
         delay_ms(100);             
         strcpy(buffer_s, "p0|");   //cargo datos para enviar
         
         fputs(buffer_s, A_MASTER); //envio buffer cargado al master

         strcpy(buffer_s, "");      //limpio el buffer
         OUTPUT_LOW(PIN_C1);        //deshabilita la linea TX
      }
   }
}

//////////////////////////////////////////////////////
Esclavo 0 .h

#include <16F877A.h>
#device adc=16

#FUSES NOWDT                 	//No Watch Dog Timer
#FUSES XT                    	//Crystal osc <= 4mhz for PCM/PCH , 3mhz to 10 mhz for PCD
#FUSES NOPUT                 	//No Power Up Timer
#FUSES NOBROWNOUT            	//No brownout reset
#FUSES NOLVP                 	//No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                 	//No EE protection
#FUSES NOWRT                 	//Program memory not write protected
#FUSES NODEBUG               	//No Debug mode for ICD
#FUSES NOPROTECT             	//Code not protected from reading

#use delay(clock=4000000)
#use rs232(baud=2400,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=A_MASTER)

//////////////////////////////////

El codigo para el esclavo 1 es el mismo solo cambia el id_pic.

Esquema adjunto.
Les agradezco de antemano por la ayuda que me puedan dar.
 

Adjuntos

  • esquema.bmp
    88.9 KB · Visitas: 109
Gracias por el comentario, a pesar del tiempo... jajaja bueno si, utilizando compuertas es posible, diseñé una solución similar y logré la comunicación.
 
Amigo he seguido tu diseño y quisiera saber si puedes colocar la solución que obtuviste? si lo hiciste por compuertas lógicas con el RS232 o usaste el RS485?.

Estoy haciendo un diseño a distancias mayores de 250 metros y me interesaría saber como se haría en el caso del RS485 ya que no manejo bien C (estoy aprendiendo debido a que aprendí assembler) y me cuesta entender un poco más como se formula en tus programas la activación de este puerto serial. Gracias.
 
Estimado Cristhiancdbc, para la etapa de la comunicación que buscaba la solución fue simplemente colocar comparadores, uno en la salida del maestro y uno en la entrada de cada esclavo, esto para garantizar que los niveles lógicos sean los correctos y mantener los triestados en las salidas de los esclavos. Por cierto, he utilizado USART (entre pics). No se que tan efectiva sea esta solución para los 250 m. Con respecto a la programación en C, la explicación seria bastante extensa, en especial si recién empiezas a aprenderlo, yo traté de dejar (en el código expuesto arriba) lo mas comentado posible para su comprensión, de todos modos te recomendaría que te des una vuelta por los miles de tutoriales en Internet disponibles (mucho me han ayudado). Por cierto, me parece excelente que hayas estudiado ensamblador primero, en mi caso fue al revés. Aunque también lo puedes realizar en ensamblador, hay mucha info al respecto. Saludos.
 
Última edición:
No utilizo I2C, SPI. Por cuestiones de diseño debo usar RS 232.
No es una comunicación convencional: Half-Duplex, Asincrono.

Por las especificaciones que estás dando, lo mejor es utilizar RS485 (que no es un protocolo software).

Para que no te compliques demasiado, pensá que tenés que desarrollar el firmware en base a preguntas y respuestas....el master siempre guía la comunicación y todos los esclavos escuchan, pero pueden responder de 1 y en orden. Dicha orden de respuesta de los esclavos, estará dada también por el master.
Para evitar confusiones, tendrías que tener en cuenta los tiempos de respuesta de los esclavos, utilizar caracteres de fin de línea, hacer un protocolo de comunicación en definitiva....

Saludos !
 
Asi es, en efecto las especificaciones hacen pensar primero en RS-485, pero la finalidad del proyecto es en parte, como dices hacer un protocolo nuevo y de mayor capacidad que 256 receptores, la idea es dejar al margen la dependencia del sincronismo, comunicación no diferencial, con posibilidad de distintas velocidades de operación en un amplio rango y trabajar con niveles TTL. Todo eso con la mayor sencillez posible. Saludos.
 
Buenas tardes, estoy haciendo un proyecto en donde utilizo 3 pics en I2c (un maestro y dos esclavos) mi problema es que solo uno de los esclavos funciona bien. Utilizo el Pic16F887 como maestro y un esclavo y el 16F690 como el segundo esclavo, la idea es controlar 2 motores a pasos simultáneamente con los esclavos, el maestro solo envía los datos de cuantos grados hay que mover los motores. Cuando pulso un push button el maestro manda los grados a los dos esclavos, la primera vez que envía datos si responden los dos esclavos, pero al volver a enviar datos una segunda vez el segundo esclavo ya no hace nada, no responde se congela, que es el pic16F690 y el 16f887 responde a la perfección, otro problema es la información que recibe el segundo esclavo, a los dos les mando 90° que son 12 pasos para llegar a esto, el 16f887 si hace solo los 12, pero el otro (16f690) hace 23 pasos, supongo que no recibe los 90. El código del esclavo 1 y prácticamente iguales. Anexo los códigos.Gracias

Maestro

Código:
#include <16F887.h>
#fuses PUT,NOWDT,NOPROTECT,INTRC_IO,NOLVP
#use delay (clock=4000000)                     
#use i2c(MASTER,fast, SDA=PIN_C4, SLOW, SCL=PIN_C3)//MAESTRO
#bit control=0x05.0
#use standard_io(a)
#use standard_io(c)

int8 grados=90,grados2=90; //grados solo de 0-254
byte dato;
byte esclavo; //0xa0 esclavo de motor a pasos 0xb0 esclavo de motor DC


    
void envio_I2C (void) //Funcion de I2C
{ 
  i2c_start();         //Comienzo de la comunicación I2C ...
  i2c_write(esclavo);     //...con la dirección del PIC esclavo...
  i2c_write(dato);      // Envia dato
  i2c_stop();          //Finalización de la transmisión
  delay_ms(100);
}


/******************************************************************************/
/*************************** FUNCIÓN PRINCIPAL ********************************/

void main(void)
{
  setup_oscillator(OSC_4MHZ);
  set_tris_a(0xFF);   
      while (true)
        { 
          
          if (control==0)
           { 
             //envio de variable grado a esclavo 1
             dato=grados;
             esclavo=0xa0;
             envio_I2C();
             
             
             //envio de variable grado a esclavo 2
             dato=grados2;
             esclavo=0xb0;
             envio_I2C();
             
             
           }
       }
}
Esclavo 1
Código:
#include <16F887.h>
#fuses PUT,NOWDT,NOPROTECT,INTRC_IO
#use delay (clock=4000000)
#use standard_io(b)
#use fast_io(C)
#use fast_io(D)
#use I2C(SLAVE, fast,SDA=PIN_C4 , SCL=PIN_C3, ADDRESS=0xa0)
#include <lcd.c>
//#bit ba=0x07.5 // color amarillo 1a
//#bit bb=0x07.4 // color rojo 1b
//#bit la=0x07.3 // color cafe 2a
//#bit lb=0x07.6 //color naranja
#byte control=0x06
   int8 grado;
   int pasos;
   int cont=0,i;
byte const sentido [4]={0x28,0x60,0x50,0x18};//00101000,01100000,01010000,00011000   



#int_ssp
ssp_isr(void){
     while(true){ 
     BYTE state, incoming;
      state = i2c_isr_state();
      
      if(state <= 0x80){                     //Master is sending data
      incoming=i2c_read();
         if(state==1){
         incoming=i2c_read();
         grado=incoming; 
         pasos=grado/7.5;
         lcd_gotoxy(1,2);
         printf(lcd_putc,"\fdato: %x\n pasos=%i" ,grado,pasos);
         delay_ms(200);
         for (i=0;i<pasos;i++)
          {
           control=sentido[cont];
           delay_ms(1000);
          cont++;
          if (cont==4)
          {cont=0;}
         }
         }
      }}
}
void main(void) {
setup_oscillator(OSC_4MHZ);  
set_tris_b(0x00);
control=0;
lcd_init();  
enable_interrupts(global);
enable_interrupts(int_ssp); 
printf(lcd_putc, "Esperando dato...\n");  
 
   while (TRUE) {
   
      
       }
   }
Esclavo 2
Código:
#include <16F690.h>
#fuses PUT,NOWDT,NOPROTECT,INTRC_IO
#use delay (clock=4000000)
#use fast_io(B)
#use standard_io(C)

#use I2C(SLAVE,fast, SDA=PIN_B4, SCL=PIN_B6, ADDRESS=0xb0,force_hw)

//#bit ba=0x07.5 // color amarillo 1a
//#bit bb=0x07.4 // color rojo 1b
//#bit la=0x07.3 // color cafe 2a
//#bit lb=0x07.6 //color naranja
#byte control=0x07
   int8 grado;
   int pasos;
   int cont=0,i;
byte const sentido [4]={0x28,0x60,0x50,0x18};//00101000,01100000,01010000,00011000   



#int_ssp
ssp_isr(void){
     while(true){ 
     BYTE state, incoming;
      state = i2c_isr_state();
      
      if(state <= 0x80){                     //Master is sending data
      incoming=i2c_read();
         if(state==1){
         incoming=i2c_read();
         grado=incoming; 
         pasos=grado/7.5;
         delay_ms(200);
         for (i=0;i<pasos;i++)
          {
           control=sentido[cont];
           delay_ms(1000);
          cont++;
          if (cont==4)
          {cont=0;}
         }
         }
      }}
}
void main(void) {
setup_oscillator(OSC_4MHZ);  
set_tris_c(0x00);
control=0;
enable_interrupts(global);
enable_interrupts(int_ssp); 
  
   while (TRUE) {
   
      
       }
   }
También probé con el esclavo dos utilizar la función de i2c_poll(), de esta forma si responde, ya no se congela el pic, pero en este caso en lugar de 12 pasos hace mas de 40, también anexo este código.

Código:
#include <16F690.h>
#fuses PUT,NOWDT,NOPROTECT,INTRC_IO
#use delay (clock=4000000)
#use fast_io(B)
#use standard_io(C)
#use I2C(SLAVE, SDA=PIN_B4 ,fast, SCL=PIN_B6, ADDRESS=0xb0)
#byte control=0x07
 int8 grado;
int pasos;
   int cont=0,i;
byte const sentido [4]={0x28,0x60,0x50,0x18};//00101000,01100000,01010000,00011000   

void main() {
setup_oscillator(OSC_4MHZ);  
set_tris_c(0x00);
control=0;

  
  
   
   while (TRUE) {
   
      // Recepción por comunicación I2C     
      if(i2c_poll()){
         grado=i2c_read();
          pasos=grado/7.5;
          for (i=0;i<pasos;i++)
          {
           control=sentido[cont];
           delay_ms(1000);
          cont++;
          if (cont==4)
          {cont=0;}
         }
      }
         
      
   }
}
 
Última edición por un moderador:
Yabsa revisa los fuses del esclavo N#2 no estoy seguro pero me parece que falta alguno no se si me equivoco. Además cuando ocurre un problema de esta índole es porque la segunda vez que haces el llamado no existe un reinicio del programa del pic, en tu caso creo que el WHILE (true) dentro de la interrupción te permite realizar la primera vez sin problema la instrucción pero ahí se queda, te recomiendo que pongas una condición distinta, tal vez que leas lo que recibe por I2C y si es para ese pic ejecutar el lo que está en la interrupción, si no entonces que retorne al bucle principal. Espero te sirva, saludos.
 
Yabsa revisa los fuses del esclavo N#2 no estoy seguro pero me parece que falta alguno no se si me equivoco. Además cuando ocurre un problema de esta índole es porque la segunda vez que haces el llamado no existe un reinicio del programa del pic, en tu caso creo que el WHILE (true) dentro de la interrupción te permite realizar la primera vez sin problema la instrucción pero ahí se queda, te recomiendo que pongas una condición distinta, tal vez que leas lo que recibe por I2C y si es para ese pic ejecutar el lo que está en la interrupción, si no entonces que retorne al bucle principal. Espero te sirva, saludos.

Gracias por contestar, no he hecho eso que dices, pero me encontré otro 16f887 y lo conecte como un tercer esclavo y jala sin problemas, el código en los 3 esclavos es el mismo solo cambia la dirección del esclavo y el pic a utilizar así como los pines de salida del segundo esclavo, cheque la hoja de datos y el 16f887 dice que para i2c utiliza mssp y el 16f690 usa ssp ¿eso afecta en algo?
 
Última edición por un moderador:
RS232 o_O bueno mi humilde consejo es que uses el protocolo I2C que se usa unica y exclusivamente para esclavos y puedes tener creo qe hasta 256 esclavos aunque no creo que llegues a tantos tendrias que ller un poco... es un poco dificil pero mucho mas efectivo creeme (lo he comprobado :) )
 
Atrás
Arriba