Autor Tema: Como poner la salida en alta impedancia de un pic esclavo en una red rs485  (Leído 2396 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado juancho056

  • PIC10
  • *
  • Mensajes: 35
Hola, buen  dia para todos.

Estoy implementando una red rs485 para comunicar un maestro con varios esclavos, envio una trama en la cual el primer caracter es el esclavo a quien va dirigido la consulta, necesito ponerlos a todos a modo de escucha y la salida en alta impedancia, para que responda solo el esclavo al que va dirigida la pregunta. No se como puedo hacer eso, mi problema es que los esclavos siempre estan escuchando tanto la consulta del maestro, como la respuesta de otros esclavos.

Actualmente trabajo con el pic 16f1936 y empleo el pin_C5 para habilitar la comunicacion por 485

Intento implementar un codigo muy sencillo con la interrupcion por serial en ccs (int_rda), para hacer la prueba con una trama de 2 caracteres y comunicar 3 esclavos.

trama que envio: [esclavo-02]   donde esclavo puede ser (01) (02) ó (03)
Código: [Seleccionar]

#include <16F1936.h>
#FUSES NOWDT, NOLVP, NOBROWNOUT, NOPROTECT,NOPUT, INTRC_IO, NOMCLR
#use delay(clock=4000000)
//#define EN_485=PIN_C5;
#include <stdlib.h>
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)// RS232 Estándar

char dato_rx=' ';  // Variable para almacenar el dato recibido
char trama[3];
int puntero;

/****** Interrupcion por serial************/
#int_rda
void serial_isr() {

  dato_rx=0x00;
  if(kbhit()){
    dato_rx=getc(); // almaceno el dato
     add_trama(dato_rx);  // lo añado al buffer
     
    }
 }
void add_trama(char c){
 dat=dat+1;// Contador para garantizar que es el primer dato

    switch (puntero){
    Case 0
     if (c==0x01 && dat==1)       // el comando es para mi y es inicio de trama
     {
      trama[0]= c;
       puntero+=1
     }
  else   // En caso de que no sea para este esclavo
    {
        puntero =0;  // Vuelva a preguntra desde el inicio de Case
        dat=0;
         output_float(pin_C5);  // asi creo que se pone la salida en alta impedacia, para que no reciba en un tiempo
        delay_ms(200); // tiempo que requiero espere antes de volver a  recibir datos y garantizar que el otro esclavo ya respondio
 
    }
  break;

  Case 1:
   trama[1]=c;
  output_high(pin_C5); // habilito para tx por 485
   printf("%x%x",trama[0],trama[1]);
   
  break;
 output_low(pin_C5); // Deshabilito para poder volver a recibir
  dat=0;
  puntero=0;
}


void main() {

  enable_interrupts(global);
  enable_interrupts(int_rda);

  do {


  } while (TRUE);
}

No se si la problematica este bien planteada.

Alguien que tenga la gentileza de ayudarme implementar este concepto, es algo basico pero soy novato con ganas de aprender.
Gracias por su atencion. Saludos!


Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re: Como poner la salida en alta impedancia de un pic esclavo en una red rs485
« Respuesta #1 en: 26 de Mayo de 2015, 03:28:49 »
Yo creo que te estas haciendo mucho problema. Ademas de tener un delay en tu rutina de interrupcion...

Primero definiria el mensaje a enviar. Si o si yo definiria un comienzo de trama, si es de tamaño fijo no incluiria un valor de tamaño , si es de valor variable si, o sino otro valor de fin de trama.

Ej: Trama FIJA 2 datos, al esclavo 2
0xFF 0x02 0x50 0x06

Comienzo de trama - Esclavo - Datos

Trama variable al esclavo 4
0xFF 0x04 0x05 0x87 0x56 0xFF 0x89 0x32

Comienzo de trama - Esclavo - Largo - Datos

Realizaria una maquina de estados como hiciste vos. Yo lo haria como el codigo que puse a continuacion, entonces que siga escuchando siempre, no me importaria eso, mientras escuche no hay problema ya que si no es del receptor este lo va a quitar o no dar importancia al valor que llegue.

Código: C
  1. #define ESPERA 0
  2. #define ESCLAVO 1
  3. #define RECIBIENDO 2
  4. #define MALRECEPTOR 3
  5.  
  6.  
  7. #int_rda
  8. void serial_isr() {
  9.  
  10.         dato_rx=getc(); // almaceno el dato
  11.         switch(estado) {
  12.        
  13.         case ESPERA:
  14.                 if(dato_rx==0xFF) { estado=ESCLAVO; j=0;}
  15.                 break;
  16.         case ESCLAVO:
  17.                 if(dato_rx==0x02)
  18.                 {
  19.                         estado=RECIBIENDO;
  20.                 }
  21.                 else
  22.                 {
  23.                         estado=MALRECEPTOR;
  24.                 }
  25.                 break;
  26.         case RECIBIENDO:
  27.                 vector_a_guardar[j]=dato_rx;
  28.                 j++;
  29.                 if(j==2) {estado=ESPERA; dato_listo=1;} // Bandera para saber cuando se actualizo por completo el dato que llego.
  30.                 break;
  31.         case MALRECEPTOR:
  32.                 j++;
  33.                 if(j==2) {estado=ESPERA;}
  34.                 break;
  35.         default:
  36.                 estado=ESPERA;
  37.                 break;
  38.         }
  39. }

Si es variable la cantidad de datos, entonces agregas un nuevo estado, en el que el 3er dato se le asigne a una variable, y esa variable es luego la que se compara con j

Código: C
  1. #define ESPERA 0
  2. #define ESCLAVO 1
  3. #define LARGO 2
  4. #define LARGO_MAL 3
  5. #define RECIBIENDO 4
  6. #define MALRECEPTOR 5
  7.  
  8.  
  9. #int_rda
  10. void serial_isr() {
  11.  
  12.         dato_rx=getc(); // almaceno el dato
  13.         switch(estado) {
  14.        
  15.         case ESPERA:
  16.                 if(dato_rx==0xFF) { estado=ESCLAVO; j=0;}
  17.                 break;
  18.         case ESCLAVO:
  19.                 if(dato_rx==0x02)
  20.                 {
  21.                         estado=LARGO;
  22.                 }
  23.                 else
  24.                 {
  25.                         estado=LARGO_MAL;
  26.                 }
  27.                 break;
  28.         case LARGO:
  29.                 largo_dato=dato_rx;
  30.                 estado=RECIBIENDO;
  31.                 break;
  32.         case LARGO_MAL:
  33.                 largo_dato=dato_rx;
  34.                 estado=MALRECEPTOR;
  35.                 break;
  36.         case RECIBIENDO:
  37.                 vector_a_guardar[j]=dato_rx;
  38.                 j++;
  39.                 if(j==largo_dato) {estado=ESPERA; dato_listo=1;}
  40.                 break;
  41.         case MALRECEPTOR:
  42.                 j++;
  43.                 if(j==largo_dato) {estado=ESPERA;}
  44.                 break;
  45.         default:
  46.                 estado=ESPERA;
  47.                 break;
  48.         }
  49. }
(Podria haberme ahorrado un estado si le ponia una bandera para indicar que no era dirigido a ese receptor, pero por simplicidad lo deje asi nomas.)

Por supuesto, esto tiene un problema, es que si en algun momento el maestro se cae a la mitad del mensaje, deberia enviar al menos un varios caracteres basura hasta que los esclavos salgan al estado ESPERA. Lo podrias hacer al comienzo del programa. asi limpiar a los esclavos, por ejemplo si tu mensajes tienen 5 datos, enviar al menos 6 o 7 datos con 0x00 y se limpiaria. Podria haberme olvidado del tema de la cantidad de datos y volver a preguntar si hay un 0xFF, pero esto evitaria que pueda mandar un 0xFF como dato. Tambien se podria implementar una funcion de timeout que si al no recibir datos durante un tiempo(desde detectado el primer dato 0xFF) que reinicie los estados a ESPERA.

La comunicacion del esclavo AL maestro podria ser:

0xFF 0x00 datos
el 0x00 sea del maestro.

eso tampoco afectaria a los esclavos, ya que la direccion es otra.
« Última modificación: 26 de Mayo de 2015, 03:39:16 por KILLERJC »

Desconectado juancho056

  • PIC10
  • *
  • Mensajes: 35
Re: Como poner la salida en alta impedancia de un pic esclavo en una red rs485
« Respuesta #2 en: 26 de Mayo de 2015, 09:32:27 »
Hola  KILLERJC, gracias por responder siempre eres bastante claro cuando intervienes.
Mi planteamiento no fue del todo claro y pido disculpas por ello, trate de hacer un abordaje simple para hacerme a una idea de como hacer para comunicar varios esclavos sin que colisionen los datos.
Lo que realmente estoy tratando de implementar es una red Modbus RTU, la trama que envio como consulta desde el maestro siempre es fija, solo varia el esclavo, pero al ser un protocolo industrial tan generalizado, en un principio si se envia un inicio de trama funcionaria para los esclavos pic que programe, pero la idea es que tambien pueda conectar variadores y demas aparatos que tienen incorporado este protocolo, entonces si envio un inicio de trama no podria comunicarme con otros dispositivos pues no entenderian la consulta.

Lo que especifica el protrecolo, lo copiare en esta cita
Citar
En el modo RTU, los mensajes comienzan con un intervalo de silencio de al menos 3,5 veces el carácter.
Esto se implementa más fácilmente como un múltiplo de veces de caracteres en la velocidad en baudios que se está utilizando en la red (que se muestra como T1-T2-T3-T4 en la figura siguiente).
_______________________________________________________________
|        Inicio         | Dirección| Función | Datos     |  CRC  |        End          |
| T1-T2-T3-T4-T5 |   8 bits    |  8bits    | n * 8 bits| 16 bits| T1-T2-T3-T4-T5|

El primer campo transmitido es, entonces, la dirección del dispositivo.
Los caracteres permitidos de transmisión para todos los campos son hexadecimal 0-9, A-F.
Dispositivos en red supervisan el bus de red de forma continua, incluso durante intervalos "silenciosos". Cuando el primer campo (el campo de dirección) se recibe, cada dispositivo decodifica para averiguar si es el dispositivo direccionado.
Después del último carácter transmitido, un intervalo similar de al menos 3,5 caracteres marca el final del mensaje. Un nuevo mensaje puede comenzar después de este intervalo.

Para ser muy sincero no tengo idea de como realizar un intervalo silencioso de 3,5 caracteres, lo que creo es que es un tiempo pequeño para enviar la consulta desde el maestro y un tiempo de espera para esperar la respuesta del esclavo direccionado y asi no colisionen los datos, por eso intentaba colocar un delay en mi codigo en caso de que no fuese el esclavo correcto, despues de un tiempo volver a recibir los datos del bus con todos los esclavos.

Tengo entendido que si el dato que llega no es la dirección del esclavo, puedo colocar la patita C5 en alta impedancia durante un tiempo, para que no transmita, ya que esta es la que habilito para transmitir los datos a 485, o deba ser la patita C7 que es la de recepcion y no reciba mas datos en un tiempo,  si se descarta que la consulta es para el.

Seria entonces algo como

Envio la trama, ingresa a el esclavo que va direccionado y los otros esclavos inmediatamente descartan la trama durante un tiempo en el que seguro el esclavo direccionado ya ha respondido, volviendo todos a esperar la siguiente consulta.

Ya tengo todo listo pero realizar esta comunicacion a sido en verdad un dolor de cabeza... ayuda por favor  :(




 

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re: Como poner la salida en alta impedancia de un pic esclavo en una red rs485
« Respuesta #3 en: 26 de Mayo de 2015, 13:40:45 »
Entonces la idea es la misma, por que directamente no escucharla completa y simplemente no tener en cuenta eso. Segui haciendo uso de una maquina de estados, Lo peor que te puede pasar es que agarres una trama desde la mitad, pero no se si es hot plug el rs485. Como para que suceda eso. Pero en fin siempre tenerlo en cuenta.


Código: C
  1. #define INICIO 0
  2. #define DIRECCION 1
  3. #define FUNCION 2
  4. #define DATOS 3
  5. #define CRC 4
  6. #define END 5
  7.  
  8. char vector_t,funcion; // 8 bits
  9. bit  flag_receptor=0,crc_t=0, dato_valido,transmision_deshabilitada;  // Varaible 1 bit
  10. uin16_t valor_crc;  //Variable 16 bits
  11.  
  12. #int_rda
  13. void serial_isr() {
  14.  
  15.         dato_rx=getc(); // almaceno el dato
  16.         switch(estado) {
  17.        
  18.         case INICIO:
  19.                 vector_t=dato_rx;
  20.                 dato_valido=0;
  21.                 transmision_deshabilitada=1;
  22.                 estado=DIRECCION;
  23.                 break;
  24.         case DIRECCION:
  25.                 if(dato_rx==0x02)
  26.                 {
  27.                         flag_receptor=1;
  28.                 }
  29.                 else
  30.                 {
  31.                         flag_receptor=0;
  32.                 }
  33.                 estado=FUNCION;
  34.                 break;
  35.         case FUNCION:
  36.                 funcion=dato_rx;
  37.                 estado=DATOS;
  38.                 break;
  39.         case DATOS:
  40.                 // No se bien donde sacar la cantidad de datos transmitidos, si es dependiente de la funcion o no, se guardaran o no segun flag_receptor
  41.                 estado=CRC;
  42.                 valor_crc=0;
  43.                 break;
  44.         case CRC:
  45.                 valor_crc= (dato_rx << (8 * crc_t));
  46.                 crc_t ^= 1;
  47.                 if (crc_t==0) {estado=END;}
  48.                 break;
  49.         case END:
  50.                 if(dato_rx==vector_t) { dato_valido=1; estado=INICIO; } else {//Aqui esperar hasta que no existan mas datos, solo si no se agarra de comienzo de la trama}
  51.                 TMR0 = 0x00;                                    // Valor para que se produzca el overflow
  52.                 enable_interrupt(int_rtcc);                     // habilito interrupcion del timer
  53.                 disable_interrupt(int_rda);
  54.                 // Si queres alta impedancia lo habilitas aca
  55.                 break;
  56.         default:
  57.                 estado=INICIO;
  58.                 break;
  59.         }
  60. }
  61.  
  62. #int_rtcc
  63. void timer0_isr() {
  64.  
  65.         if(kbhit())
  66.         {
  67.                 dato_rx=getc();  //Limpio buffer ( no se la profundidad solo tome uno ) por si algun esclavo respondio algo mientras esperaba los 3.5 caracteres, no deberia y seria un error.
  68.                 borrar_flag_UART();
  69.         }
  70.         enable_interrupt(int_rda);                      // habilito interrupcion del timer
  71.         disable_interrupt(int_rtcc);
  72.         transmision_deshabilitada=0;               // habilito nuevamente la transmision, ya paso el tiempo requerido entre tramas
  73.         // Aca quitarias la alta impedancia
  74. }


El por que no de un delay, creo que es obvio, es esperar haciendo nada, y mas en una interrupcion lo cual inhabilitaria que se produzca una interrupcion si es que se envio la trama mas rapido.

Citar
Tengo entendido que si el dato que llega no es la dirección del esclavo, puedo colocar la patita C5 en alta impedancia durante un tiempo, para que no transmita, ya que esta es la que habilito para transmitir los datos a 485, o deba ser la patita C7 que es la de recepcion y no reciba mas datos en un tiempo,  si se descarta que la consulta es para el.

Por que inhabilitarlo asi?, por que simplemente evitar que transmita con un flag dentro del programa ?, algo como puse en el codigo. Lo unico que ve de malo es que si el maestro sigue continuamente mandando datos podria haber una colision.

Desconectado juancho056

  • PIC10
  • *
  • Mensajes: 35
Re: Como poner la salida en alta impedancia de un pic esclavo en una red rs485
« Respuesta #4 en: 28 de Mayo de 2015, 02:09:28 »
Hola, KILLERJC , que pena no haber atendido antes pero me cojio un resfriado muy fuerte y suspendi actividades.

La maquina de estados para recolectar los datos la implemente de forma muy similar, y me funciona bien al calcular el CRC para verificar la trama.
Examinando tu codigo, si el primer dato en la trama es el Esclavo, al ingresa a Inicio no tendria forma de validar el esclavo en el estado DIRECCION, por que ya entro a inicio.

Por decir.. Trama 02-04-00-02-00-03-cb-05 ,primer dato ingresa a estado:INICIO ( vector_t=02 ) fin de CASE, siguiete estado: DIRECCION el siguiente dato es 04, y el condicional  if(dato_rx==0x02) no entraria a habilitar la bandera de recepcion.

En el ultimo estado END con este condicional
Código: [Seleccionar]
if(dato_rx==vector_t) { dato_valido=1; estado=INICIO; }
else {//Aqui esperar hasta que no existan mas datos, solo si no se agarra de comienzo de la trama}

No entindo muy bien el condicional, en este estado ya se imprime la trama de respuesta  por que se garantizo el CRC es correcto, en el estado CRC, luego  habilitas una interrupcion para desbordar el Timer 0, donde se borrar el buffer de recepcion o la trama  y volver habilitar la recepcion por interrupcion?

Sin embargo lo que pasa es que la trama que envio es de 10 caracteres, y la de respuesta del pic es de 22, en la mayoria de casos estas tramas tiene 01 o 02 y asi a mitad de trama, lo que significaria que volveria a entrar a la maquina de estados sin ser la consulta para este esclavo..

En fin me desvio del tema, ya tengo la trama lista ya calculo todo y transmite bien por 485 para 1 esclavo, para hacer la transmision tengo un conversor de 232 a 485, en el cual tengo como salida DATA+(A) y DATA-(B), y en las targetas de los esclavos la cominicacion se realiza a traves de un max485, que tambien tiene sus patitas 6(A) y 7(B). Si conecto el conversor a un solo esclavo funciona muy bien, cuando intento puentiar A y B al otro esclavo , se bloquea la comunicacion por que empieza a llegar ruido solo con conetarlo a otro max485, las tierras estan acopladas.

Alguien tiene idea de por que pasa esto?, o que puedo hacer...
Dejo el esquema de conexion que intento realizar. Gracias, Saludos.


« Última modificación: 28 de Mayo de 2015, 03:22:35 por juancho056 »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re: Como poner la salida en alta impedancia de un pic esclavo en una red rs485
« Respuesta #5 en: 28 de Mayo de 2015, 04:43:15 »
Es que tome lo que me pasaste

|        Inicio         | Dirección| Función | Datos     |  CRC  |        End          |
| T1-T2-T3-T4-T5 |   8 bits    |  8bits    | n * 8 bits| 16 bits| T1-T2-T3-T4-T5|


Suponiendo tu trama pense en algo asi, si T2 fuera un 0x52 por ejemplo y quisiera ir al esclavo 2

0x52    0x02      0x04         0x00 0x02 0x00 0x03    0xCB 0x05     0x52
T2     Esclavo     Funcion       Dato                           CRC              T2

Yo entendi la trama asi y tal ves este equivocado, el 0x02 del if(dato_rx==0x02) es la direccion del esclavo.
El por eso de mi codigo.

Y con respecto a la conexcion no tengo la menor idea :/
Tenes puesta las resistencias de 120 ohms de terminacion ??
« Última modificación: 28 de Mayo de 2015, 04:47:11 por KILLERJC »