Autor Tema: Estoy implementando MODBUS en PIC  (Leído 12213 veces)

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

Desconectado Sasián

  • PIC24F
  • *****
  • Mensajes: 825
    • www.sasian.es
Re: Estoy implementando MODBUS en PIC
« Respuesta #15 en: 01 de Noviembre de 2006, 17:24:02 »
buenass:
por si os sirve de ayuda hace un par de años trabaje algo con modbus y pic......

///////////////////////////////////////////////////////////////////////////
////                             pic_modbus.C                          ////
////                         esclavo MODBUS para PIC                   ////
////                  por Félix Sasián WWW.PACALACONCURSO.COM          ////
////                                                                   ////
////  rev0: enero 2004                                                 ////
////  - implementados comandos 0x03, 0x04, 0x06 y 0x10                 ////
////  rev1: febrero 2004                                               ////
////  - solucionado bug en rx gracias a los amigos                     ////
////    de foro www.todopic.foro.st                                    ////
////  -Escritura / lectura desde la eeprom via MODBUS DIR >100         ////
////   esta por terminar, solo usar 32 bytes                           ////
////                                                                   ////
////                                                                   ////
////                                                                   ////
////                                                                   ////
///////////////////////////////////////////////////////////////////////////
////                                                                   ////
////                                                                   ////
////        (C) Copyright 2004 www.pacalaconcurso.com                  ////
//// Este codigo puede ser usado, modificado y distribuido libremente  ////
//// sin eliminar esta cabecera y  sin garantía de ningún tipo.        ////
////                                                                   ////
////                                                                   ////
///////////////////////////////////////////////////////////////////////////
//// usar de esta forma en la int_rda
////#int_rda
////void serial_isr()
//// {
////   while(kbhit())
////     {
////      analiza(getc());
////     }
//// }
////
///////////////////////////////////////////////////////////////////////////
//VARIABLES
//////////////////
//estructura para los datos modbus

struct _modbus
{
int8 address; //mi direccion modbus
char trama[30]; //espacio para almacenar datos del comando que se esta recibiendo
char datos[16]; //espacio para los datos de recibidos por modbus
char datostx[16]; //espacio para los datos a transmitir  por modbus
int16 CRC ; // para calcular el CRC del paquete recibido
int16 comandos; //comandos mandados desde modbus
int1 nuevo_comando; // hay al menos un comando recibido desde modbus
} modbus;

int1 hay_modbus;


/* tenemos 32 bytes de intercambio con modbus.
dejo la primera word (dos primeros bytes para poder recibir 16 comandos desde
modbus */


//alias y defines para pruebas
int8 puntero; //puntero para la lectura de un nuevo comando modbus
int8 puntdatos; //puntero para los datos del comando 0x10
int16 num_datos; //numero de bytes de datos a guardar del comando 0x10
int1 timeout_error,pausa_rx;
//int1 led;
const int8 timeout_rx=5; //timeout para recepcion de mensaje modbus
///////////////


//rutinas

/************************************************************************************
      Mb_test_crc : verifica si el CRC del paquete recibido es correcto
*************************************************************************************
entrada   :
-------
trame     : packet con el crc incluido
n         : longitud del paquete sin el CRC
salida    : 1 = crc erroneo
--------    0 = crc ok
************************************************************************************/
int1 Mb_test_crc(CHAR trame[],int n)
{
   unsigned int16 crc,i,j,carry_flag,a;
   crc=0xffff;
   for (i=0;i<n;i++)
   {
      crc=crc^trame;
      for (j=0;j<8;j++)
      {
         a=crc;
         carry_flag=a&0x0001;
         crc=crc>>1;
         if (carry_flag==1)
            crc=crc^0xa001;
      }
   }
   if ((trame[n+1]!=(crc>>8)) || (trame[n]!=(crc&255)))
      return 1;
   else
      return 0;
}

/************************************************************************************
      Mb_calcul_crc : calcula el CRC de un paquete
*************************************************************************************
entrada   :
-------
trame     : packet con espacio para el crc
n         : longitud del paquete sin el CRC
salida    : CRC
--------
************************************************************************************/
int16 Mb_calcul_crc(CHAR trame[],int n)
{
   int16 crc,carry_flag,a;
   int8 i,j;
   crc=0xffff;
   for (i=0;i<n;i++)
   {
      crc=crc^trame;
      for (j=0;j<8;j++)
      {
         a=crc;
         carry_flag=a&0x0001;
         crc=crc>>1;
         if (carry_flag==1)
            crc=crc^0xa001;
      }
   }
   trame[n+1]=crc>>8;
   trame[n]=crc&255;
   return crc;
}

/************************************************************************************
      comando : mira si se ha recibido algun comando desde modbus y activa el flag
*************************************************************************************
entrada   :
-------
salida    :
--------
************************************************************************************/
void comando()
{
int16 aux;

aux = (modbus.datos[0]<<8)+modbus.datos[1];
modbus.comandos ^= aux;
//se mantiene los comandos que pudieran estar a la espera de ejecutarse
//printf(lcd_putc, "\n %Lx",aux);

if (modbus.comandos >0) modbus.nuevo_comando = true;
   {
   //borramos el area de comandos
   modbus.datos[0] = 0;
   modbus.datos[1] = 0;
   }
}
/************************************************************************************
      modbus : Interpreta los paquetes válidos y envia ack al master
*************************************************************************************
usa la matrices de definicion global trama[] y datos[]
************************************************************************************/
void int_modbus()
{
int8 direccion;  //direccion incicial del registro
int8 longitud,longitud1;  //longitud en decimal de los datos
int8 i,bytes;
direccion =(int8)(modbus.trama[2]<<8)+modbus.trama[3];
longitud=(int8)(modbus.trama[4]<<8)+modbus.trama[5];
switch (modbus.trama[1])
{
      case 0x03:
      case 0x04:
      for (i=0;i<longitud*2;i++)
      {
      if (direccion<100)
      {
         modbus.trama[3+i]=modbus.datostx[direccion*2+i];
        // modbus.trama[4+i*2]=modbus.datostx[direccion*2+i+1];
      }
      else
      {
       modbus.trama[3+i]=read_ext_eeprom(direccion*2+i);
    //   modbus.trama[4+i*2]=read_ext_eeprom(direccion*2+i+1);

      }
      }
      //calcular el CRC
      modbus.trama[2]=longitud*2;//numero de bytes de datos a transmitir
      Mb_calcul_crc(modbus.trama,longitud*2+3);
      longitud1=longitud*2+5;
      for (i=0;i<longitud1;i++)
      {
         printf("%c",modbus.trama);
      }
      break;
      case 0x06:
//guardamos los registros a partir de la direccion indicada
 for (i=0;i<2;i++)
      {
      if (direccion<100)
      modbus.datos[direccion*2+i]=modbus.trama[4+i];
      else
      write_ext_eeprom(direccion*2+i,modbus.trama[4+i]);
      }
      //respuesta al master
      for (i=0;i<8;i++)
      {
         printf("%c",modbus.trama);
      }
      //mirar si hay algun comando nuevo (en la primera word recibida)
      comando() ;
      break;

      case 0x10:
      bytes=modbus.trama[6];
      //guardamos los registros a partir de la direccion indicada
       for (i=0;i<bytes;i++)
      {
      if (direccion<100)
      modbus.datos[direccion*2+I]=modbus.trama[7+i];
      else
      write_ext_eeprom(direccion*2+I,modbus.trama[7+i]);
      }
      //respuesta al master
      //longitud1=num_datos+7;
      //calcular el CRC para la respuesta
      Mb_calcul_crc(modbus.trama,6);
      //mandar la respuesta
      for (i=0;i<8;i++)
      {
         printf("%c",modbus.trama);
      }
      //mirar si hay algun comando nuevo
      comando() ;
         break;
      default:


}
}
/************************************************************************************
      analiza : analiza los caracteres recibidos para montar un paquete
*************************************************************************************
entrada   :
-------
c         : caracter procedente de la usart
salida    : ---
--------
************************************************************************************/

void analiza(char c)
{
switch (puntero)
{
   case 0:// recibo el esclavo a quien va dirigido
   if (c==modbus.address) //si el comando es para mi
   {
   modbus.trama[0]= c;
   puntero+=1;
   }
   else
   {
   puntero =0;
   pausa_rx=true;
   }
   break;
   case 1:// recibo el comando
   if (c == 0X03 ||c==0x04 ||c==0x06||c==0x10 ) //si es un comando conocido
      {
         modbus.trama[1]= c;
         puntero+=1;
      }
   else
   {
   if (c==modbus.address) //si el comando es para mi
         {
         modbus.trama[0]=c;
         puntero=1;
         set_timer1(0);
         }
      else
         {
         puntero=0;
         }
  }
   break;
   case 2:
   modbus.trama[2]=c; //direccion inicial (H)
   puntero+=1;
   break;
   case 3:
   modbus.trama[3]=c; //direccion inicial (L)
   puntero+=1;
   break;
   case 4:
   modbus.trama[4]=c; //Numero de registros (H)
   puntero+=1;
   break;
   case 5:
   modbus.trama[5]=c; //Numero de registros (L)
   puntero+=1;
   break;
   case 6:
      modbus.trama[6]=c; //CRC (H)
      puntero+=1;
      if  (modbus.trama[1] == 0X10)//si se trata del comando 0x10
      {
       // num_datos = (modbus.trama[4]<<8)+modbus.trama[5];
       // num_datos=num_datos*2+2;//datos que faltan por recibir incluido CRC
       num_datos=(int)modbus.trama[6] + 2;
        puntdatos=0;
      }
   break;
   case 7:
   if (modbus.trama[1] == 0X10)//si se trata del comando 0x10
      {
            modbus.trama[7+puntdatos]=c;
            puntdatos+=1;
         if (puntdatos>= num_datos) // si no se han recibido todos los datos
         {
             if (Mb_test_crc(modbus.trama,5+num_datos)==0)
                {
                 int_modbus(); //rutina para interpretar el comando
                 hay_modbus=1;
                }
              puntero=0;
              puntdatos=0; //
         }
      }
   else  // no se trata del comando 0x10
      {
         modbus.trama[7]=c; //CRC (L)
         if (Mb_test_crc(modbus.trama,6)==0)
            {
             int_modbus(); //rutina para interpretar el comando
             hay_modbus=1;
             puntero=0;
            }
         else
            {
            puntero=0; //el CRC no es correcto
            }
      }
   break;
   default:
   puntero =0;
}
}



saludossss

Puerto Real - Cádiz - España
  www.sasian.es

Desconectado Zaphyrus

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 323
    • Mi blog: Es cuestión de actitud
Re: Estoy implementando MODBUS en PIC
« Respuesta #16 en: 01 de Noviembre de 2006, 18:00:00 »
Que buen empujón para Alfredo!!!  :)

Sasián excelente página web!!!

Saludos.

Martín
"¿Lo quiere rápido, barato, o bien hecho? Puede elegir dos de las tres cosas." Arthur C. Clarke.
Mi Proyecto Final de Carrera-Microprocesador RISC de 16 bits en HDL: http://martin.calveira.googlepages.com/home
Mi página web o blog: http://es-cuestion-de-actitud.blogspot.com/
Martín Calveira - Zárate - Argentina

Desconectado XLR

  • PIC10
  • *
  • Mensajes: 1
Re: Estoy implementando MODBUS en PIC
« Respuesta #17 en: 01 de Noviembre de 2006, 19:19:11 »

Tomando como base la función de Sasián he comunicado el Pic mediante DASMSerial con InTouch sin demasiados problemas.

Como he empleado el 16F877A me encuentro un limitado con la memoria de datos y aún no he podido comprobar el refresco con bloques de datos mas extensos. Es un buen punto de referencia. Gracias Sasián

Saludos

Desconectado alfredoenvalpo

  • PIC10
  • *
  • Mensajes: 10
Re: Estoy implementando MODBUS en PIC
« Respuesta #18 en: 02 de Noviembre de 2006, 10:12:58 »
HOLA!

Gracias por la ayuda y por el codigo enviado, si bien debo decir
que ya lo habia visto hace un tiempo en la pagina web, (www.sasian.es)
me sirvió para tener
una idea de como hacer el programa  :-) y de motivación para seguir.

Ahora me encuentro viendo lo de las interrupciones con la ayuda
que me enviaron anteriormente..

Saludos!
Alfredo

Desconectado Sasián

  • PIC24F
  • *****
  • Mensajes: 825
    • www.sasian.es
Re: Estoy implementando MODBUS en PIC
« Respuesta #19 en: 02 de Noviembre de 2006, 17:59:31 »
Me alegro que sea de utilidad. la verdad es que no esta demasiado optimizado pero si que funciona, yo lo use para comunicar con una red de S7-200 sin problemas e incluso con el pc via OPC para el labview.

saludos

Puerto Real - Cádiz - España
  www.sasian.es

Desconectado alfredoenvalpo

  • PIC10
  • *
  • Mensajes: 10
Re: Estoy implementando MODBUS en PIC
« Respuesta #20 en: 02 de Noviembre de 2006, 19:12:41 »
Sasian,
Estuve buscando en tu codigo como se hizo el manejo de la recepción de la trama
modbus pero no pude encontrarlo. ¿Consideraste los tiempos de resguardo indicados
en el protocolo?

Saludos
Alfredo


Desconectado MGLSOFT

  • Moderador Local
  • DsPIC33
  • *****
  • Mensajes: 7912
Re: Estoy implementando MODBUS en PIC
« Respuesta #21 en: 03 de Noviembre de 2006, 09:54:50 »
En CCS, ultimas versiones, ya aparece una rutina probada para manejar el protocolo Modbus.
Encontraras adjunta la rutina como MODBUS.C
Aqui pego un ejemplo de como utilizarla segun un ejemplo hecho con el Wizard incorporado
Código: C
  1. #include "C:\Archivos de programa\PICC V4\Projects\Modbus.h"
  2. #include <LCD.C>
  3.   #ZERO_RAM
  4. #int_RDA
  5. RDA_isr()
  6. {
  7.  
  8. }
  9.  
  10. #int_CCP2
  11. CCP2_isr()
  12. {
  13.  
  14. }
  15.  
  16.  
  17.  
  18. #define MODBUS_BUS SERIAL
  19. #define MODBUS_TYPE SLAVE
  20. #define MODBUS_SERIAL_RX_BUFFER_SIZE 8
  21. #define MODBUS_SERIAL_BAUD 9600
  22. #define MODBUS_OUR_ADDRESS 10
  23. #include "modbus.c"
  24. void main()
  25. {   modbus_init();
  26.    fprintf(SLAVE_PC, "\n\rReady...");
  27.  
  28.    while(TRUE)
  29.    {
  30.       while(!modbus_serial_receive(FALSE));
  31.       fprintf(SLAVE_PC, "\n\rBus Active");
  32.  
  33.       if((respond = (modbus_rx.address == MODBUS_OUR_ADDRESS)) || modbus_rx.address == 0)
  34.       {
  35.          fprintf(SLAVE_PC, "\n\rRequest for us");
  36.          fprintf(SLAVE_PC, "\n\rFunction: %X", modbus_rx.func);
  37.          switch(modbus_rx.func)
  38.          {
  39.             case 0x01:
  40.                if(respond)
  41.                {
  42.                   //read the coils
  43.                }
  44.                break;
  45.             case 0x02:
  46.                if(respond)
  47.                {
  48.                   //read discrete inputs
  49.                }
  50.                break;
  51.             case 0x03:
  52.                if(respond)
  53.                {
  54.                   //read holding registers
  55.                }
  56.                break;
  57.             case 0x04:
  58.                if(respond)
  59.                {
  60.                   //read input registers
  61.                }
  62.                break;
  63.             case 0x05:
  64.                break;
  65.             case 0x06:
  66.                break;
  67.             case 0x07:
  68.                break;
  69.             case 0x08:
  70.                if(respond) {diagnostics_rsp(MODBUS_OUR_ADDRESS,
  71.                               make16(modbus_rx.data[0],modbus_rx.data[1]),
  72.                               make16(modbus_rx.data[2], modbus_rx.data[3]));}
  73.                fprintf(SLAVE_PC, "\n\rResetting...");
  74.                reset_cpu();
  75.                break;
  76.          }
  77.       }
  78.    }
  79.  
  80.  
  81.    setup_adc_ports(NO_ANALOGS);
  82.    setup_adc(ADC_OFF);
  83.    setup_spi(FALSE);
  84.    setup_wdt(WDT_OFF);
  85.    setup_timer_0(RTCC_INTERNAL);
  86.    setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
  87.    setup_timer_2(T2_DISABLED,0,1);
  88.    setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
  89.    setup_ccp2(CCP_COMPARE_INT);
  90.    lcd_init();
  91.    enable_interrupts(INT_RDA);
  92.    enable_interrupts(INT_CCP2);
  93.    enable_interrupts(GLOBAL);
  94.    setup_oscillator(False);
  95.  
  96.    // TODO: USER CODE!!
  97.  
  98. }

Tambien adjunto el manual en ingles del protocolo, que explica sobre los mensajes y calculo de CRC por tablas.

Suerte !!!
Todos los dias aprendo algo nuevo, el ultimo día de mi vida aprenderé a morir....
Mi Abuelo.

Desconectado Sasián

  • PIC24F
  • *****
  • Mensajes: 825
    • www.sasian.es
Re: Estoy implementando MODBUS en PIC
« Respuesta #22 en: 03 de Noviembre de 2006, 10:01:13 »
Como te comente el código no esta optimizado.
lo correcto es recibir la trama completa y luego analizarla.
por la web www.sasian.es creo recordar que hay un ejemplo de como implementar la recepcion de la trama completa en ccs usando un timer, una vez tengas la trama completa la puedes interpretar usando como base el ejemplo anterior.
con un timer adicionar para generar una base de tiempo puedes incluso implementar un timeout para controlar tu protocolo.

saludos

Puerto Real - Cádiz - España
  www.sasian.es

Desconectado alfredoenvalpo

  • PIC10
  • *
  • Mensajes: 10
Re: Estoy implementando MODBUS en PIC
« Respuesta #23 en: 04 de Diciembre de 2006, 20:52:54 »
Agradezco la ayuda nuevamente, y disculpen por no responder antes,
estoy ahora utilizando el compilador al ccs en lugar del mcc18, asi
que estoy modificando el programa que estaba haciendo,


Saludos     :shock:

Desconectado rodo38

  • PIC10
  • *
  • Mensajes: 3
Re: Estoy implementando MODBUS en PIC
« Respuesta #24 en: 10 de Agosto de 2011, 19:40:38 »

Tomando como base la función de Sasián he comunicado el Pic mediante DASMSerial con InTouch sin demasiados problemas.

Como he empleado el 16F877A me encuentro un limitado con la memoria de datos y aún no he podido comprobar el refresco con bloques de datos mas extensos. Es un buen punto de referencia. Gracias Sasián

Saludos


me podrias ayudar yo necesito comunicar una pic con intouch y tambien tengo el DAserver



saludos


 

anything