Autor Tema: Problema con RS485  (Leído 3036 veces)

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

Desconectado jonathanPIC888

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 320
Problema con RS485
« en: 16 de Mayo de 2010, 18:37:59 »
Hola tengo un problema con las comunicaciones RS485 quería saber si alguien me puede orientar en el desarrollo de una librería en C por que tengo que leer y enviar 16 bytes por medio de este bus.
He traducido la librería pero no entiendo 100% su funcionamiento...si alguien la ha utilizado o puede explicarme como usarla le agracederé   :-/

Código: C
  1. /////////////////////////////////////////////////////////////////////////
  2. ////                                                                 ////
  3. //// Driver para implementar comunicaciones bajo el bus RS485.       ////
  4. //// Modificación para usar solo con interrupción por el puerto B.   ////
  5. //// Programador: Moyano Jonathan.                                   ////
  6. //// Lineas de programa comentadas en español para mejor comprensión ////
  7. //// por parte del programador.                                      ////
  8. ////                                                                 ////
  9. //// int1 rs485_recibe_datos(int *datos, int espera)                 ////
  10. ////     - Toma datos del bus RS485.                                 ////
  11. ////     - La dirección puede ser 1 a 33.                            ////
  12. ////     - La función retorna 1 si hay error en la paridad de los    ////
  13. ////       datos recibidos.                                          ////
  14. ////                                                                 ////
  15. //// int1 rs485_envia_mensaje(int destinatario,int len, int*dato)    ////
  16. ////     - Envía un mensaje por el bus RS485.                        ////                                                   ////
  17. ////     - Destinatario : Dirección del destinatario.                ////
  18. ////     - len: Longitud del mensaje.                                ////
  19. ////     - *dato: Puntero al mensaje.                                ////
  20. ////     - Retorna TRUE si lo envió con exito, FALSE en caso de      ////
  21. ////       poder haber enviado el mensaje.                           ////
  22. ////                                                                 ////
  23. //// void rs485_espera(int1 clrwdt)                                  ////
  24. ////     - Espera que se abra el bus RS485. Normalmente usado antes  ////
  25. ////       de efectuar un envio de mensajes para evitar colisiones   ////
  26. ////       de datos.                                                 ////
  27. ////     - Resetea al perro guardían en caso de que no responda.     ////                          ////
  28. ////     - Función no necesaria en caso de que se envíen datos       ////
  29. ////       continuamente.                                            ////
  30. ////                                                                 ////
  31. //// void rs485_init()                                               ////
  32. ////     - Inicia el bus RS485...se llama antes que cualquier        ////
  33. ////       función.                                                  ////
  34. ////                                                                 ////
  35. /////////////////////////////////////////////////////////////////////////
  36. /////////////////////////////////////////////////////////////////////////
  37.  
  38. #ifndef RS485_ID
  39. #define RS485_ID  0x10   // Dirección del dispositivo dentro del bus por defecto.
  40. #endif
  41.  
  42.    #define RS485_RX_PIN       PIN_B0   // PIN recepción de datos.
  43.    #define RS485_TX_PIN       PIN_B1   // PIN transmisión de datos.
  44.    #define RS485_ENABLE_PIN   PIN_B3   // PIN DE.  (recepción) low, (transmisión) high.
  45.    #define RS485_RX_ENABLE    PIN_B2   // PIN RE.  Debería mantenerse en nivel bajo...
  46.  
  47.    #use rs232(baud=9600, xmit=RS485_TX_PIN, rcv=RS485_RX_PIN, enable=RS485_ENABLE_PIN, bits=9, long_data, errors, stream=RS485)
  48.    #use rs232(baud=9600, xmit=RS485_TX_PIN, rcv=RS485_RX_PIN, enable=RS485_ENABLE_PIN, bits=9, long_data, multi_master, errors, stream=RS485_CD)
  49.  
  50.    #define RCV_OFF() {disable_interrupts(INT_EXT);} // Función para apagar recepciones de datos.
  51.  
  52. #define RS485_wait_time 20   // Periodo de espera en milisegundos.
  53.  
  54. #bit rs485_collision = rs232_errors.6 // Definimos bit indicador de colisión de datos.
  55.  
  56. #ifndef RS485_RX_BUFFER_SIZE     // Definimos tamaño del BUFFER de recepción.
  57. #define RS485_RX_BUFFER_SIZE  20 // Por defecto 20....
  58. #endif
  59.  
  60. int rs485_state, rs485_ni, rs485_no;
  61. int rs485_buffer[RS485_RX_BUFFER_SIZE]; // Definimos la variable que contendrá los datos.
  62.                                         // Su tamaño viene dado por RS485_RX_BUFFER_SIZE....
  63.  
  64.  
  65. // Función: Habilitar la recepción de datos.
  66.  
  67. void RCV_ON(void) {
  68.   clear_interrupt(INT_EXT);
  69.   enable_interrupts(INT_EXT);
  70. }
  71.  
  72.  
  73. // Función: Iniciar el bus RS485.
  74.  
  75. void rs485_init() {
  76.    RCV_ON(); // Habilitamos recepción de datos.
  77.    rs485_state=0; // Ponemos variable a 0....
  78.    rs485_ni=0;
  79.    rs485_no=0;
  80.    ext_int_edge(H_TO_L);        // Interrupción por flanco de bajada.
  81.    enable_interrupts(INT_EXT);  // Habilitamos interrupción por cambio de estado PIN_RB0.
  82.    enable_interrupts(GLOBAL);   // Habilitamos interrupciones globalmente.
  83.    output_low(RS485_RX_ENABLE); // Escuchamos....( modo recepción ).
  84. }
  85.  
  86.  
  87. // Indice para el buffer de recepción temporal..
  88. int8 temp_ni;
  89.  
  90. // Función: Añade un byte al buffer de recepción temporal.
  91. void rs485_add_to_temp(int8 b) {
  92.    // Guarda el byte.
  93.    rs485_buffer[temp_ni] = b;
  94.  
  95.    // indexado ciclico..
  96.    if(++temp_ni >= RS485_RX_BUFFER_SIZE)
  97.    {
  98.       temp_ni = 0;
  99.    }
  100. }
  101.  
  102.  
  103. // Función: Rutina para manejar los datos entrantes...
  104. #int_ext
  105.  
  106. void incomming_rs485() {
  107.    int16 b;
  108.    static int8  cs,state=0,len;
  109.    static int16 to,source;
  110.  
  111.    b=fgetc(RS485); // Toma los datos del bus y los guarda en b.
  112.    cs^=(int8)b;    // Aplica una XOR a la variable b.
  113.  
  114.    switch(state) {
  115.       case 0:  // Toma desde la dirección...
  116.          temp_ni=rs485_ni;
  117.          source=b;
  118.          cs=b;
  119.          rs485_add_to_temp(source);
  120.          break;
  121.  
  122.       case 1:  // Llegar a la dirección...
  123.          to=b;
  124.          break;
  125.  
  126.       case 2:  // Obtiene la longitud...
  127.          len=b;
  128.          rs485_add_to_temp(len);
  129.          break;
  130.  
  131.       case 255:   // Hace un checksum..para ver si los datos están correctos.
  132.          if ((!cs)&&(bit_test(to,8))&&(bit_test(source,8))&&((int8)to==RS485_ID)) {  // Si cs==0,entonces el checksum está bien.
  133.             rs485_ni=temp_ni;
  134.          }
  135.          state=0;
  136.          return;
  137.  
  138.       default: // Obtiene los datos...
  139.          rs485_add_to_temp(b);
  140.          --len;
  141.          break;
  142.    }
  143.    if ((state>=3) && (!len)) {
  144.       state=255;
  145.    }
  146.    else {
  147.       ++state;
  148.    }
  149. }
  150.  
  151.  
  152. // Función: Envía datos por el bus RS485.
  153. // Entradas:     1) La dirección del destinatario.
  154. //               2) El número de datos a enviar.
  155. //               3) El puntero a los datos a enviar.
  156. // Salidas:   TRUE si puede concretar la operación.
  157. //            FALSE si falla.
  158. // Nota:       Formato:  fuente| destinatario | longitud de datos |datos | checksum.
  159.  
  160. int1 rs485_envia_mensaje(int destinatario,int len, int*dato) {
  161.    int8 try, i, cs;
  162.    int1 ret = FALSE;
  163.  
  164.    RCV_OFF(); // Desactivamos la recepción de datos.
  165.    disable_interrupts(GLOBAL); // Desactivamos las interrupciones.
  166.  for(try=0; try<5; ++try) {
  167.       rs485_collision = 0; // No hay colisión de datos.
  168.       fputc((int16)0x100|rs485_id, RS485_CD); // Transmitimos...quien envía los datos..
  169.       fputc((int16)0x100|destinatario, RS485_CD); // elejimos un destinatario...
  170.       fputc(len, RS485_CD); // Enviamos la longitud de datos...
  171.  
  172.       for(i=0, cs=rs485_id^to^len; i<len; ++i) {
  173.          cs ^= *dato;
  174.          fputc(*dato, RS485_CD); // enviamos los datos...
  175.          ++dato;
  176.       }
  177.  
  178.       fputc(cs, RS485_CD); // transmite checksum...
  179.       if(!rs485_collision) { // Si no hay colisión de errores....
  180.          ret = TRUE; // Sigue y salta...
  181.          break;
  182.       }
  183.       delay_ms(RS485_ID);
  184.    }
  185.  
  186.    RCV_ON(); // Habilitamos la recepción de datos nuevamente.
  187.    enable_interrupts(GLOBAL); // Habilitamos las interrupciones globales.
  188.    return(ret); // Retornamos reporte de fallas...
  189. }
  190.  
  191.  
  192. // Función: Espera a que el bus esté listo para poder enviar datos.
  193. // Entrada: TRUE - Resetea el perro guardían para evitar el reset.
  194. //          FALSO - El perro guardían no ha sido reseteado.
  195. // Salidas: Ninguna.
  196.  
  197. void rs485_espera(int1 clrwdt)
  198. {
  199.    int16 i;
  200.  
  201.    RCV_OFF(); // Apagamos la recepción de datos.
  202.    for(i=0; i <= (rs485_wait_time*20); ++i)
  203.    {
  204.       if(!input(RS485_RX_PIN)) // Esperamos a que el PIN RX esté a 0.
  205.          i = 0; // reseteamos el contador.
  206.       else // sino ....
  207.          delay_us(50);
  208.  
  209.       if(clrwdt)
  210.          restart_wdt(); // reseteamos el perro guardían.
  211.    }
  212. }
  213.  
  214.  
  215. // Función: Toma un dato del bus RS485 y lo guarda en un buffer.
  216. // Entradas:   1) Puntero al buffer para guardar el mensaje.
  217. //             2) TRUE  - En espera del mensaje.
  218. //                FALSE - Solo comprueba si un mensaje está disponible.
  219. // Salidas:       TRUE  - Si el mensaje fue recibido.
  220. //                FALSE - Si espera es FALSO y ningún mensaje está disponible.
  221. // Nota: Los datos rellenan el buffer con el siguiente orden:
  222. //       ID del emisor | Longitud del dato | datos...
  223.  
  224. int1 rs485_recibe_datos(int *datos, int espera)
  225. {
  226.    while(espera && (rs485_ni == rs485_no)) {}
  227.  
  228.    if(rs485_ni == rs485_no)
  229.       return FALSE;
  230.  
  231.    else {
  232.       int n;
  233.       n = rs485_buffer[(rs485_no+1)%sizeof(rs485_buffer)] + 2;
  234.  
  235.       for(; n>0; --n)
  236.       {
  237.          *datos = rs485_buffer[rs485_no];
  238.          if(++rs485_no >= sizeof(rs485_buffer))
  239.          {
  240.             rs485_no = 0;
  241.          }
  242.          ++datos;
  243.       }
  244.       return TRUE;
  245.    }
  246. }

Desconectado bagmel

  • PIC12
  • **
  • Mensajes: 56
    • Semaforos los Llanos C.A.
Re: Problema con RS485
« Respuesta #1 en: 18 de Mayo de 2010, 18:29:28 »
amigo yo utilizo mucho el protocolo rs485 pero no utilizo esa libreria utilizo un codigo que recopile de varios lados y modifique a mi antojo y funciona perfecto, yo envio desde el maestro una cadena de caracteres por ejemplo "1rse32nrds!" y yo a grozo modo lo que hago es ir recogiendo los caracteresque van llegando y cuando llega el caracter de final de la cadana que lo puse como"!" proceso el comando, siquieres la data avisame y te ayudo es muy facill de verdad
La necesidad es la madre de todos los inventos!!!!!!!!!!!!!!

Desconectado jonathanPIC888

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 320
Re: Problema con RS485
« Respuesta #2 en: 19 de Mayo de 2010, 17:45:21 »
Uhh buenisimo si pudieras pegarme una mano estaría buenismo... :-/ Le he estado dando muchas vueltas a este tipo de comunicaciones pero la verdad se me ha complicado más de la cuenta.

Desconectado MLO__

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 4581
Re: Problema con RS485
« Respuesta #3 en: 19 de Mayo de 2010, 19:10:59 »
Hola.

En el foro esta todo eso  :mrgreen: mira aqui.

Saludos
El papel lo aguanta todo

Desconectado jonathanPIC888

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 320
Re: Problema con RS485
« Respuesta #4 en: 19 de Mayo de 2010, 22:29:43 »
Muchisimas gracias MLO  :-/ Ahora lo voy a leer a ver que saco en claro
Un saludo !

Desconectado bagmel

  • PIC12
  • **
  • Mensajes: 56
    • Semaforos los Llanos C.A.
Re: Problema con RS485
« Respuesta #5 en: 20 de Mayo de 2010, 00:23:38 »
ese es el mejor codigo que he visto y de alli la modificacion que yo utilizo, swi deseas explicacion de algo dime o si quieres te subo mi codigo. avisame cualquier cosa!!!!!!
La necesidad es la madre de todos los inventos!!!!!!!!!!!!!!

Desconectado jonathanPIC888

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 320
Re: Problema con RS485
« Respuesta #6 en: 20 de Mayo de 2010, 03:33:52 »
Dale un día de estos me pongo a armar el circuito para las pruebas y cualquier cosa te comento. Muchas gracias por tu ayuda  :-/ :-/

Un saludo !

Desconectado chasol

  • PIC10
  • *
  • Mensajes: 2
Re: Problema con RS485
« Respuesta #7 en: 17 de Enero de 2013, 21:22:29 »
¡Saludos foreros!  :)

¡Hola amigo bagmel!
Estoy peleandome con una red RS-485 de 4 microcontroladores PIC18F4550, donde uno de ellos es el maestro. Estoy utilizando como tranceptor al CI SN75176A de TI, la configuración de la red es en half-dúplex, estoy utilizando un pin del microcontrolador para el control del tranceptor. La idea que quiero implementar es la siguiente:

El maestro enviará un par de bytes a través del bus, el 1er. byte es el identificador del esclavo y el 2do. es algún comando en específico que el esclavo entenderá. Cuando el maestro envíe ambos bytes sólo un esclavo deberá de esponder al maestro, es decir, enviará el identificador del maestro y el mismo dato que recibió de el (el 2do. byte), con esto el maestro sabrá que el esclavo recibió bien el dato; y obviamente los demás esclavos no harán nada hasta que el maestro envie una orden para ellos.

¿Qué tal suena la idea? Hasta el momento solo el maestro manda el comando a ejecutar y los esclavos lo interpretan y realizan alguna acción (activar pines del puerto x), estoy utilizando la directiva: #use rs232(BAUD=9600, PARITY=N, XMIT=PIN_C6, RCV=PIN_C7, ENABLE=PIN_C2, BITS=8), con lo que al mandar un dato a través de la USART del PIC maestro se habilita el tranceptor y puedo escribir en el bus; así que en los esclavos no mandan confirmación (por el momento), sus tranceptores se encuentran inhabilitados por lo que todos pueden recibir el dato proveniente del maestro.

De igual forma que muchos amigos nuestros del foro han intentado utilizar la librería RS485.c que acompaña al compilador de CCS C Compiler, no he tenido suerte de tener un ejemplo funcional con ella, he leído que tienes una librería con funciones para manejar el RS-485, ¿podrías ayudarme con está misión?

Saludos  :-/