'*******************************************************************************
'*  Name    : MODBUS-Master-PIC18F4520.BAS                                      *
'*  Author  : Aldo Javier Ercigoj                                              *
'*  Notice  : Copyright (c) 2021                                               *
'*          : All Rights Reserved                                              *
'*  Date    : 16/11/2021                                                       *
'*  Version : 3.151                                                            *
'*  Notes   :          DIRECIONES MODBUS RS232 19200 BAUDIO                    *
'*******************************************************************************
'* CANALES ANALOGICOS      ADC0 = 50000  En formato flotante y escalado        *
'*                         ADC1 = 50002  En formato flotante y escalado        *
'*                         ADC2 = 50004  En formato flotante y escalado        *
'*                         ADC3 = 50006  En formato flotante y escalado        *
'*                         ADC4 = 50008  En formato flotante y escalado        *
'*                                                                             *
'* VARIABLES FLOTANTES  DAT_FL0 = 50010  Esta la use para cambiar la ESCALA ADC* 
'*                      DAT_FL1 = 50012  libres de uso                         *
'*                      DAT_FL2 = 50014  libres de uso                         *
'*                      DAT_FL3 = 50016  libres de uso                         *
'*                      DAT_FL4 = 50018  libres de uso                         *
'*                                                                             *
'* HOLDING REGISTROS      Dato0 = 40000  libres de uso DATO TIPO Word          *
'*                        Dato1 = 40001  libres de uso DATO TIPO Word          *
'*                        Dato2 = 40002  libres de uso DATO TIPO Word          *
'*                        Dato3 = 40003  libres de uso DATO TIPO Word          *
'*                        Dato4 = 40004  libres de uso DATO TIPO Word          *
'*                                                                             *
'* INPUT REGISTROS         ADC0 = 20000  Valor conversión analógica 10Bit      *
'*                         ADC1 = 20001  Valor conversión analógica 10Bit      *
'*                         ADC2 = 20002  Valor conversión analógica 10Bit      *
'*                         ADC3 = 20003  Valor conversión analógica 10Bit      *
'*                         ADC4 = 20004  Valor conversión analógica 10Bit      *
'*                                                                             *
'* INPUT STATUS ENT DIG     IN0 = 10000  ESTADO ENTRADA 1 = ON / 0 = OFF       *
'*                          IN1 = 10001  ESTADO ENTRADA 1 = On / 0 = OFF       *
'*                          IN2 = 10002  ESTADO ENTRADA 1 = On / 0 = OFF       *
'*                          IN3 = 10003  ESTADO ENTRADA 1 = On / 0 = OFF       *
'*                          IN4 = 10004  ESTADO ENTRADA 1 = On / 0 = OFF       *
'*                          IN5 = 10005  ESTADO ENTRADA 1 = On / 0 = OFF       *
'*                          IN6 = 10006  ESTADO ENTRADA 1 = On / 0 = OFF       *
'*                          IN7 = 10007  ESTADO ENTRADA 1 = On / 0 = OFF       *
'*                          IN8 = 10008  ESTADO ENTRADA 1 = On / 0 = OFF       *
'*                          IN9 = 10009  ESTADO ENTRADA 1 = On / 0 = OFF       *
'*                         IN10 = 10010  ESTADO ENTRADA 1 = On / 0 = OFF       *
'*                         IN11 = 10011  ESTADO ENTRADA 1 = On / 0 = OFF       *
'*                         IN12 = 10012  ESTADO ENTRADA 1 = On / 0 = OFF       *
'*                                                                             *
'* COIL O RELAY OUTPUT    COIL0 =   200  ESTADO SALODA 1 = On / 0 = OFF        *
'*                        COIL1 =   201  ESTADO SALIDA 1 = On / 0 = OFF        *
'*                        COIL2 =   202  ESTADO SALIDA 1 = On / 0 = OFF        *
'*                        COIL3 =   203  ESTADO SALIDA 1 = On / 0 = OFF        *
'*                        COIL4 =   204  ESTADO SALIDA 1 = On / 0 = OFF        *
'*                        COIL5 =   205  ESTADO SALIDA 1 = On / 0 = OFF        *
'*                        COIL6 =   206  ESTADO SALIDA 1 = On / 0 = OFF        *
'*                        COIL7 =   207  ESTADO SALIDA 1 = On / 0 = OFF        *
'*                                                                             *
'* Funciones MODBUS permitidas:------------------------------------------------*
'*                                                                             *
'* FN 01 (Leer el Estado de un Relay o Coil. 8 OUT habilitadas PortD )         *
'* FN 02 (Leer el Estado de una ENT Digital. 12 ENT habilitadas PortB / PortE) *
'* FN 03 (Leer estado de los ADC y Variables Asignadas)                        *
'* FN 04 (Leer estado de Input Registros, en nuestro caso ADC en 10bit)        *
'* Cuando convierto la dirección del registro Modbus le sumo un 1 porque       *
'* asi la dirección son las mismas que en el Master Modbus, sino tendría       *
'* que preguntar por el registro mas uno, ejemplo Holding Reg. 40001           *
'* Para evitar esto y preguntar en el Maestro por la dirección 40000, le sumo 1*
'* en el esclavo así todas las direcciones son las mismas                      *
'* Esto se repite en todas las preguntas y funciones modbus del Maestro.       *
'*                                                                             *
'-------------------- Para enviar datos por modbus ----------------------------*
'*                                                                             *
'* FN 05 (Para setear un relay ON/OFF por Modbus, 1 relay por pregunta,        *
'*        no admite múltiples coil al mismo tiempo, solo uno)                  *
'* FN 06 (Escribe un solo Holding Registro por vez, no admite múltiples        *
'*        registros al mismo tiempo, valor máximo de un registro  65353)       *
'* FN 16 (Escribe un solo flotante por vez , no admite múltiples registros     *
'*        al mismo tiempo)                                                     *
'*-----------------------------------------------------------------------------*
'* El Pic usado es un 18F4520                                                  *
'* Canales analógicos seteados                                                 *
'* RA0 / RA1 / RA2 /RA3 / RA5                                                  *
'* RA4 /RA6 /RA7    Son I/O de uso general,                                    *
'* MCRL es off y oscilador interno a 32Mhz                                     *
'* Entradas Digitales puertos asignados PortB y PortE                          *
'* RB0 /RB1 /RB2 /RB3 /RB4 /RB5 /RB6/ RB7  +   RE0 /RE1 /RE2 /RE3              *
'* Salidas Digitales PortD                                                     *
'* RD0 /RD1 /RD2 /RD3 /RD4 /RD5 /RD6/ RD7                                      *
'* El Puerto PortC esta asignado para la comunicación Serial y el Display      *
'* RC6 = Tx out                                                                *
'* RC7 = Rx in                                                                 *
'* RC0,RC1,RC2,RC3 = Datos del Display LCD                                     *
'* RC4             = RS pin del Display                                        *
'* RC5             = EN pin del Display                                        *
'*******************************************************************************

Device = 18F4520   '32K EEPROM / 32Mhz
'*******************************************************************************
' CONFIGURACION DE FUSIBLES DEL PIC 
'*******************************************************************************
Reminders = False
Config_Start
  OSC    = INTIO67 'Oscilador interno, Habilitados pines RA6 / RA7 como I/O
  IESO   = OFF	   'Oscilador Switchover modo deshabilitado
  PWRT   = On	   'PWRT habilitado
  BOREN  = OFF	   'Brown-out Reset habilitado por hardware,SLEEP Deshabilitado
  BORV   = 3	   'Minimo voltaje de reset
  WDTPS  = 32768   '1:32768
  PBADEN = OFF	   'PORTB<4:0> PINES  configurados como I/O en reset
  MCLRE  = OFF     'RE3 como Input / Deshabilitado el MCLR
  LVP    = OFF	   'Habilitado el Single-Supply ICSP
Config_End
Reminders = True
'*******************************************************************************
'DECLARACIONES y SETING:
'*******************************************************************************
Declare Xtal                  = 32     'FRECUENCIA DEL CRISTAL EN Mhz
Declare Optimiser_Level       = 3      'HABILITO LA OPTIMIZACION DEL CODIGO
Declare Dead_Code_Remove      = On     'HABILITO LA LIMPIEZA DE CODIGO BASURA
Declare Show_System_Variables = On     'PARA VER LAS VARIABLES EN EL SIMULADOR
Declare PortB_Pullups         = Off    'DESHABILITO LAS RESISTENCIAS EN PORTB
Declare Adin_Tad              = FRC    'DECLARO SETING ADC
Declare Adin_Stime            = 50     'TIEMPO DEL CAPACITOR ANTES DEL ARRANQUE
Declare Float_Display_Type    = LARGE  'AUMENTA LA PRECICION EN NUMEROS FLOAT
Declare Fsr_Context_Save      = On     'HABILITA LA EJECUCION DE LA INTERRUPCION
Declare Watchdog              = OFF    'DESHABILITO EL PERRO GUARDIAN
Declare Create_Coff           = On     
Declare LCD_Type ALPHA                 'INFORMO QUE EL LCD ES ALFANUMERICO
Declare LCD_Interface 4                'DECLARO LA INTERFACE
Declare LCD_Lines 4                    'DECLARO EL USO DE SOLO 4 PINES 
Declare LCD_DTPin PORTC.0              'DECLARO EL USO DE LOS BIT DEL RC0 A RC3
Declare LCD_ENPin PORTC.5              'DELCARO EL RC5 COMO ENABLE DEL CLD
Declare LCD_RSPin PORTC.4              'DECLARO EL RC4 COMO RS DEL LCD
'*******************************************************************************
'DEFINO REGISTRO DE ENTRADA ANALOGICA
'*******************************************************************************
OSCTUNE = %11000000 'Habilito el PPL 8Mhz * 4
OSCCON  = %01110110 'Hab. OscInterno de 8Mhz /
'ADCON1  = %00001010 'Habilito de AN0 a AN4, y Vref = 5Vdc ( 5 Ent Analogica)
'ADCON2  = %10000111 'justificado derecha, FRC, ADT = 0
'*******************************************************************************
''DEFINICION DE VARIABLES 
'*******************************************************************************
Dim NewTrama           As Bit      'Bandera identifica nuevo paquete de datos
Dim BitVal             As Bit      'Bandera para leer bit de puerto   IN/OUT 
'...............................................................................
Dim SlaveAdd           As Byte     'Para la dirección modbus del Equipo
Dim BufferIN[74]       As Byte     'Para el buffer de recepción de Datos
Dim BufferOUT[256]     As Byte     'Para el Buffer de Envió de Datos
Dim CRC16[2]           As Byte     'Check Sum
Dim PinNumero          As Byte     'Para leer el número de pin del puerto
Dim B0                 As Byte     'Numero del Byte en el BufferOUT
Dim Byte_Enviar        As Byte     'Byte a enviar por el puerto Serie
Dim Add_EEPROM         As Byte     'Dirección de memoria EEPROM
Dim Byte_Recibido      As Byte     'Byte recibido en la trama de entrada
Dim MBFnc              As Byte     'Para ver la función modbus recivida
Dim LargoIN            As Byte     'Largo de la trama recibida en byte
Dim CntByteIN          As Byte     'Cantidad de byte de entrada (Datos)
Dim CntByteOUT         As Byte     'Cantidad de byte de salida  (Datos)
Dim LargoOUT           As Byte     'Largo de la trama de salida en Byte
Dim TMR1Tick           As Byte     'Pulsos de el timer TRM1
Dim Msg_Error          As Byte     'Mensaje de Error de la trama recivida
Dim NumReg             As Byte     'Numero de Add Modbus pedidos a enviar
Dim NumRe              As Byte     'Para el cálculo de Numbyte 
Dim AddPOS             As Byte     'Para la posición de el bit a escribir  
Dim AddMemo            As Byte     'Dirección de Memoria EEPROM
Dim NumByte            As Byte     'Numero de Add Modbus a Enviar
Dim PosBit             As Byte     'Para la posicion del bit a caragr en el byte 
Dim Add                As Byte     'Dirección de EEPROM a guardad el BYTE
'-------------------------------------------------------------------------------
Dim A0                 As Byte     'Puntero ara imprimir el LCD trama a enviar
Dim A1                 As Byte     'Puntero ara imprimir el LCD trama a enviar
Dim A2                 As Byte     'Puntero ara imprimir el LCD trama a enviar
Dim A3                 As Byte     'Para leer múltiplos input / output 
Dim A4                 As Byte     'Para recibir comando On/Off Fn05 Modbus
Dim A5                 As Byte     'Para imprimir la trama resivida
Dim A6                 As Byte     'Para imprimir la trama resivida
Dim A7                 As Byte     'Para imprimir la trama resivida
'------------------- FN01 Puedo leer hasta 64 Relay = Rey ----------------------
Dim Rey8               As Byte     'Byte, estado de los Coil Leidos 8
Dim Rey16              As Byte     'Byte, estado de los Coil Leidos 8
Dim Rey24              As Byte     'Byte, estado de los Coil Leidos 8
Dim Rey32              As Byte     'Byte, estado de los Coil Leidos 8
Dim Rey40              As Byte     'Byte, estado de los Coil Leidos 8
Dim Rey48              As Byte     'Byte, estado de los Coil Leidos 8
Dim Rey56              As Byte     'Byte, estado de los Coil Leidos 8
Dim Rey64              As Byte     'Byte, estado de los Coil Leidos 8
'------------------ FN02 Puedo leer hasta 64 INPUT = Inp ----------------------
Dim Inp8               As Byte     'Byte, estado de los Coil Leidos 8
Dim Inp16              As Byte     'Byte, estado de los Coil Leidos 8
Dim Inp24              As Byte     'Byte, estado de los Coil Leidos 8
Dim Inp32              As Byte     'Byte, estado de los Coil Leidos 8
Dim Inp40              As Byte     'Byte, estado de los Coil Leidos 8
Dim Inp48              As Byte     'Byte, estado de los Coil Leidos 8
Dim Inp56              As Byte     'Byte, estado de los Coil Leidos 8
Dim Inp64              As Byte     'Byte, estado de los Coil Leidos 8
'--------- Estos Byte son para convertir Float Microchip a IEEE754 -------------
Dim Flt1               As Byte     'ByteN°1 del Flotante Guardado EEPROM 
Dim Flt2               As Byte     'ByteN°2 del Flotante Guardado EEPROM
Dim Flt3               As Byte     'ByteN°3 del Flotante Guardado EEPROM
Dim Flt4               As Byte     'ByteN°4 del Flotante Guardado EEPROM
Dim Flt5               As Byte     'ByteN°3 del Flotante Guardado EEPROM
Dim Flt6               As Byte     'ByteN°4 del Flotante Guardado EEPROM
'-------------- FN03  PUEDO LEER HASTA 5 HOLDING REGISTROS INPUT ---------------
Dim DtHr0              As Word         'Datos Enteros a leer 
Dim DtHr1              As Word         'Datos Enteros a leer 
Dim DtHr2              As Word         'Datos Enteros a leer 
Dim DtHr3              As Word         'Datos Enteros a leer 
Dim DtHr4              As Word         'Datos Enteros a leer 

Dim Dh0_LO              As DtHr0.LowByte
Dim Dh0_HI              As DtHr0.HighByte
Dim Dh1_LO              As DtHr1.LowByte
Dim Dh1_HI              As DtHr1.HighByte
Dim Dh2_LO              As DtHr2.LowByte
Dim Dh2_HI              As DtHr2.HighByte
Dim Dh3_LO              As DtHr3.LowByte
Dim Dh3_HI              As DtHr3.HighByte
Dim Dh4_LO              As DtHr4.LowByte
Dim Dh4_HI              As DtHr4.HighByte
'---------------------FN04  PUEDO LEER HASTA 5 REGISTROS INPUT -----------------
Dim Dato0              As Word         'Datos Enteros a leer 
Dim Dato1              As Word         'Datos Enteros a leer 
Dim Dato2              As Word         'Datos Enteros a leer 
Dim Dato3              As Word         'Datos Enteros a leer 
Dim Dato4              As Word         'Datos Enteros a leer 

Dim D0_LO              As Dato0.LowByte
Dim D0_HI              As Dato0.HighByte
Dim D1_LO              As Dato1.LowByte
Dim D1_HI              As Dato1.HighByte
Dim D2_LO              As Dato2.LowByte
Dim D2_HI              As Dato2.HighByte
Dim D3_LO              As Dato3.LowByte
Dim D3_HI              As Dato3.HighByte
Dim D4_LO              As Dato4.LowByte
Dim D4_HI              As Dato4.HighByte
'...............................................................................
Dim ADDMB              As Word         'ADDRES DEL REGISTRO A LEER
Dim ADDMB_LO           As ADDMB.LowByte
Dim ADDMB_HI           As ADDMB.HighByte
Dim RF1                As Word         'Numero de Registros pedidos
Dim RF1_LO             As RF1.LowByte
Dim RF1_HI             As RF1.HighByte
Dim RF2                As Word         'Numero de Registros pedidos
Dim RF2_LO             As RF2.LowByte
Dim RF2_HI             As RF2.HighByte
Dim RF3                As Word         'Numero de Registros pedidos
Dim RF3_LO             As RF3.LowByte
Dim RF3_HI             As RF3.HighByte
Dim RF4                As Word         'Numero de Registros pedidos
Dim RF4_LO             As RF4.LowByte
Dim RF4_HI             As RF4.HighByte
Dim RF5                As Word         'Numero de Registros pedidos
Dim RF5_LO             As RF5.LowByte
Dim RF5_HI             As RF5.HighByte

Dim W0                 As Word         'Dirección de Registro Modbus a leer
Dim W1                 As Word         'Dirección modbus elegida 
Dim Temp               As Word         'Para el cálculo del Check_Sum
Dim CRC                As Word         'Para el cálculo del Check_Sum
Dim j                  As Word         'Para el cálculo del Check_Sum

'...............................................................................
Dim DAT_FL0            As Float        'REGISTRO FLOTANTES USO GENERAL N°0
Dim DAT_FL1            As Float        'REGISTRO FLOTANTES USO GENERAL N°1
Dim DAT_FL2            As Float        'REGISTRO FLOTANTES USO GENERAL N°2
Dim DAT_FL3            As Float        'REGISTRO FLOTANTES USO GENERAL N°3
Dim DAT_FL4            As Float        'REGISTRO FLOTANTES USO GENERAL N°4

'*******************************************************************************
' PONGO A CERO LAS VARIABLES A USAR Y DEFINO CONSTANTES NUMERICAS
'*******************************************************************************
SlaveAdd  = 1                 'Direccion MODBUS del equipo ESCLAVO
CntByteIN = 0                 '|-----------------------------------|
LargoIN   = 0                 '|                                   |
NewTrama  = 0                 '| Pongo a cero las variables a Usar |
TMR1Tick  = 0                 '|                                   |
MBFnc     = 0                 '|-----------------------------------|
'TRISB     = %11111111         ' Defino el puertoB como Entrada
TRISD     = %00000000         ' Defino el puertoD como Salidas
'TRISE     = %00000111         ' Defino el puertoE como Entrada
'-------------------------- Inicializo la USART --------------------------------
TRISC.6 = 0         ' Defino el PIN RC6 como OUT
TRISC.7 = 1         ' Defino el pin RC7 como IN
RCSTA   = %10010000 ' Habilito serial port y continua recepcion
TXSTA   = %00100000 ' Habilito tx en asynchrono modo LOW speed
SPBRG   = 25            ' Baudrate = 19200 a 32MHz   
INTCON  = %11000000 ' Habilito int.
PIE1.5  = 1		    ' Habilito int en USART-RX recepcion
PIE1.1  = 1         ' Habilito int en TMR2
T2CON.1 = 1      	' TMR2 prescala 1:16
TMR2    = 1         ' TMR2 Registro para 0.0001 segundos
'*******************************************************************************
DelayMS 200  'ESPERO A QUE EL MICRO SE ESTABILICE DESPUES DE UN INICIO O RESET 
'******************************************************************************* 
'           RUTINA DE MANEJO DE INTERRUPSION DEL PUERTO SERIAL 
'*******************************************************************************
INI:                     ' Declaro interrupt cabecera de routina
On_Interrupt GoTo Leo_MB 
'-------------------------- Inicializo la USART --------------------------------
 
GoTo MAIN           ' SALTO POR ENCIMA DE LA INTERRUPCION
' Baudrate = (((fosc / baudrate)/64)-1)  19200 = 25 / 38400 = 12 / 9600 = 52
'-------------------------------------------------------------------------------
'                 RUTINA DE INTERRUPCION DEL PUERTO USART
'-------------------------------------------------------------------------------
Leo_MB:
Context Save
If RCSTA.1 = 1 Or RCSTA.2 = 1 Then 
		Bcf		RCSTA.4			' Limpio el estatus de resepcion
		Bsf		RCSTA.4			
		Retfie	Fast			' Salgo de la Int. Restauro WREG, STATUS y BSR 
EndIf   
If PIR1.5 = 1 Then       'Si salta la interrupcion del USART
  TMR1Tick = 0             'pongo a cero el contador de Interupciones del TMR2
  T2CON.2 = 1              'habilito el TMR2
 While PIR1.5 = 1      	 'Escribo el registro del byte entrante en el BufferIN
    BufferIN[LargoIN] = RCREG
    LargoIN = LargoIN + 1 'Calculo el largo de la trama recibida en byte
    If LargoIN = 75 Then LargoIN = 0: GoTo Leido  ' que no supere los 74 byte
  Wend
EndIf
PIR1.5 = 0
Leido:
If PIR1.1 = 1 Then       'Espero la interrupcion del TMR2 cada 0.000002segundos
 PIR1.1 = 0              'pongo a cero la interrupcion
 T2CON.2 = 0             'Deshabilito el TMR2
 TMR1Tick = TMR1Tick + 1 'acumulo una interrupcion
 If TMR1Tick > 100 Then  'cuando llega a 50 ~ 100uS, concidero fin de la trama  
  NewTrama = 1           'como no hay nuevo byte entonses la trama esta completa
  CntByteIN = LargoIN    'almaceno el numero de byte recibidos
  LargoIN = 0            'borro el contador para una nueva trama
  GoSub CheckMB		   ' Check la trama MODBUS recibida	
  Select MBFnc
    Case 1
       GoSub MB_Funcion1    ' respuesta funcion modbus 1
    Case 2
       GoSub MB_Funcion2    ' respuesta funcion modbus 2 
    Case 3
       GoSub MB_Funcion3    ' respuesta funcion modbus 3
    Case 4
       GoSub MB_Funcion4    ' respuesta funcion modbus 4
    Case 5
       GoSub MB_Funcion5    ' respuesta funcion modbus 5
    Case 6
       GoSub MB_Funcion6    ' respuesta funcion modbus 6
    Case 16
      GoSub MB_Funcion16   ' respuesta funcion modbus 16
  EndSelect   
  Else
  T2CON.2 = 1              'Habilito el TMR2 nuevo siclo
  TMR2 = 1
 EndIf 
EndIf
Context Restore          'FIN de la interrupcion de la USART
'*******************************************************************************
'                                SUB RUTINAS
'*******************************************************************************
' SUB RUTINA PARA TRASMITIR BYTE A BYTE POR EL PUERTO SERIE 
'-------------------------------------------------------------------------------
Envio_Pregunta:                  
For B0 = 0 To LargoOUT			    
	Byte_Enviar = BufferOUT[B0] 
    TX_Espero:
    If PIR1.4 = 0 Then TX_Espero ' Espero a que el registro de salida este vacio
    TXREG = Byte_Enviar          ' Envio el Byte leido          
Next
Return
'-------------------------------------------------------------------------------
' SUB RUTINA PARA ANALIZAR LA TRAMA RECIBIDA POR EL PUERTO SERIE
'-------------------------------------------------------------------------------
CheckMB: 
If BufferIN[0] <> SlaveAdd Then 		
	MBFnc = 00            ' VERIFICO LA DIRECCION MODBUS DEL ESCLAVO 
 Else
	 If BufferIN[1] <> 1 And BufferIN[1] <> 2 And BufferIN[1] <> 3 And BufferIN[1] <> 4 And BufferIN[1] <> 5 And BufferIN[1] <> 6 And BufferIN[1] <> 16 Then 
	    MBFnc =  BufferIN[1] + $80
	  Else
	     GoSub CRC16in 
	     If BufferIN[CntByteIN - 2] <> CRC16[0] Or BufferIN[CntByteIN - 1] <> CRC16[1] Then 
 	       MBFnc = $ff         ' VERIFICO QUE LE CHECKSUM SEA CORECTO
           Else
           MBFnc = 	 BufferIN[1]  ' TRAM RECIBIDA OK!!!                                                
         EndIf
     EndIf
EndIf


Return
'-------------------------------------------------------------------------------
'             SUB PARA CALCULAR EL CHECKSUM DE LA TRAMA DE ENTRADA
'-------------------------------------------------------------------------------
CRC16in:                         ' claculo el checksum 16 bit TRAMA DE ENTRADA
CRC = $FFFF
For Byte_Recibido = 0 To (CntByteIN - 3)
    Temp = BufferIN[Byte_Recibido]
    CRC = CRC ^ Temp
    For j = 1 To 8
        If CRC.0 = 1 Then
        CRC = $A001^( CRC >> 1)
        Else
        CRC = CRC >> 1
        EndIf
    Next 
Next 
CRC16[0] = (CRC // 256)			' CRC16 low byte
CRC16[1] = (CRC / 256)			' CRC16 high byte
Return
'-------------------------------------------------------------------------------
'             SUB PARA CALCULAR EL CHECKSUM DE LA TRAMA DE SALIDA
'-------------------------------------------------------------------------------
CRC16out:                         ' claculo el checksum 16 bit TRAMA DE SALIDA
CRC = $FFFF
For Byte_Recibido = 0 To (LargoOUT - 2)
    Temp = BufferOUT[Byte_Recibido]
    CRC = CRC ^ Temp
    For j = 1 To 8
        If CRC.0 = 1 Then
        CRC = $A001^( CRC >> 1)
        Else
        CRC = CRC >> 1
        EndIf
    Next 
Next 
CRC16[0] = (CRC // 256)			' CRC16 low byte
CRC16[1] = (CRC / 256)			' CRC16 high byte
Return
'-------------------------------------------------------------------------------
'    SUB RUTINA PARA CONVERCION DE FORMATO FLOTANTE MICROCHIP A IEEE-754 
'-------------------------------------------------------------------------------
'   EXPONENTE     MANTISA_0     MANTISA_1     MANTISA_2
'   XXXX-XXXX     XXXX-XXXX     XXXX-XXXX     XXXX-XXXX
'     Flt1           Flt2          Flt3          Flt4
'  VER NOTA N°3 A PIE DE PAGINA
'-------------------------------------------------------------------------------
'IEEE754:
'EWrite Add,[FloatReg]      'ALMACE EL VALOR FLOTANTE EN LA MEMORIA EEPROM (4 BYTE)
'DelayMS 5
'Flt1 = ERead Add       'LEO EL 1 BYTE DEL FLOTANTE
'Flt2 = ERead (Add + 1) 'LEO EL 2 BYTE DEL FLOTANTE
'Flt3 = ERead (Add + 2) 'LEO EL 3 BYTE DEL FLOTANTE
'Flt4 = ERead (Add + 3) 'LEO EL 4 BYTE DEL FLOTANTE
'Rlf Flt2               'ROTO A LA IZQUIERDA LA MANTISA_0
'Rrf Flt1               'ROTO A LA DERECHA EL EXPONENTE
'Rrf Flt2               'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECHA
'EWrite Add,[Flt1]      'SOBREESCRIVO LA MEMORIA EEPROM EL 1 BYTE
'DelayMS 5 
'EWrite (Add + 1),[Flt2]'SOBREESCRIVO LA MEMORIA EEPROM EL 2 BYTE
'Return
'-------------------------------------------------------------------------------
'    SUB RUTINA PARA CONVERCION DE FORMATO FLOTANTE IEEE-754 A MICROCHIP 
'-------------------------------------------------------------------------------
'   EXPONENTE     MANTISA_0     MANTISA_1     MANTISA_2
'   XXXX-XXXX     XXXX-XXXX     XXXX-XXXX     XXXX-XXXX
'     Flt1           Flt2          Flt3          Flt4
'  VER NOTA N°3 A PIE DE PAGINA
'-------------------------------------------------------------------------------
IEEE745:                 'FORMATO FLOTANTE USADO POR MICROCHIP
'----------------- LOS GUARDO YA CONVERTIDO EN LA MEMORIA EEPROM ---------------
EWrite  AddMemo,     [Flt1]  'ALMACENO EN LA MEMORIA EEPROM EL 1 BYTE 
EWrite (AddMemo + 1),[Flt2]  'ALMACENO EN LA MEMORIA EEPROM EL 2 BYTE
EWrite (AddMemo + 2),[Flt3]  'ALMACENO EN LA MEMORIA EEPROM EL 3 BYTE
EWrite (AddMemo + 3),[Flt4]  'ALMACENO EN LA MEMORIA EEPROM EL 4 BYTE
Flt1 = 0
Flt2 = 0
Flt3 = 0
Flt4 = 0
DAT_FL0 = ERead 0      'LOS VALORES RECIVIDOS POR MODBUS FUERON GUARDADOS EN
DAT_FL1 = ERead 4      'LA MEMORIA EEPROM UNA VEZ CONVERTIDO DE FORMATO 
DAT_FL2 = ERead 8      'FLOTANTE, PARA QUE EL PIC LO INTERPRETE BIEN LOS LEEO
DAT_FL3 = ERead 12
DAT_FL4 = ERead 16
Return
'-------------------------------------------------------------------------------
'SUB RUTINA PARA REALIZAR LAS PREGUNTAS AL SCLAVO
'-------------------------------------------------------------------------------
PREGUNTAS_MB:
ADDMB = ADDMB - 1            'LE RESTO 1 A LA DIRTECCION A ENVIAR AL ESCLAVO
'------------------------------ FN1 MODBUS -------------------------------------
If RF1 <> 0 Then
    BufferOUT[0] = SlaveAdd  ' DIRECCION MOSBUS DEL SLAVE
    BufferOUT[1] = 01        ' FUNCION MODBUS
    BufferOUT[2] = ADDMB_HI  ' DIRECCION DE REGISTRO A LLER HI_BYTE
    BufferOUT[3] = ADDMB_LO  ' DIRECCION DE REGISTRO A LLER LO_BYTE
    BufferOUT[4] = RF1_HI    ' CANTIDAD DE REGISTRO A LEER HI_BYTE
    BufferOUT[5] = RF1_LO    ' CANTIDAD DE REGISTRO A LEER LO_BYTE   
    LargoOUT     = 7         ' LARGO DE LA TRAMA A ENVIAR
    GoSub CRC16out           ' CALCULO EL CHECKSUM TRAMA A ENVIAR
    BufferOUT[6] = CRC16[0]	 ' CARGO EL CHECKSUM CALCULADO				
    BufferOUT[7] = CRC16[1]  ' CARGO EL CHECKSUM CALCULADO
    GoSub Envio_Pregunta     ' ENVIO LA PREGUNTA POR EL PUERTO SERIE
EndIf 
'------------------------------ FN2 MODBUS ------------------------------------- 
If RF2 <> 0 Then
    BufferOUT[0] = SlaveAdd  ' DIRECCION MOSBUS DEL SLAVE
    BufferOUT[1] = 02        ' FUNCION MODBUS
    BufferOUT[2] = ADDMB_HI  ' DIRECCION DE REGISTRO A LLER HI_BYTE
    BufferOUT[3] = ADDMB_LO  ' DIRECCION DE REGISTRO A LLER LO_BYTE
    BufferOUT[4] = RF2_HI    ' CANTIDAD DE REGISTRO A LEER HI_BYTE
    BufferOUT[5] = RF2_LO    ' CANTIDAD DE REGISTRO A LEER LO_BYTE   
    LargoOUT     = 7         ' LARGO DE LA TRAMA A ENVIAR
    GoSub CRC16out           ' CALCULO EL CHECKSUM TRAMA A ENVIAR
    BufferOUT[6] = CRC16[0]	 ' CARGO EL CHECKSUM CALCULADO				
    BufferOUT[7] = CRC16[1]  ' CARGO EL CHECKSUM CALCULADO
    GoSub Envio_Pregunta     ' ENVIO LA PREGUNTA POR EL PUERTO SERIE
EndIf 
'------------------------------ FN3 MODBUS ------------------------------------- 
If RF3 <> 0 Then
    BufferOUT[0] = SlaveAdd  ' DIRECCION MOSBUS DEL SLAVE
    BufferOUT[1] = 03        ' FUNCION MODBUS
    BufferOUT[2] = ADDMB_HI  ' DIRECCION DE REGISTRO A LLER HI_BYTE
    BufferOUT[3] = ADDMB_LO  ' DIRECCION DE REGISTRO A LLER LO_BYTE
    BufferOUT[4] = RF3_HI    ' CANTIDAD DE REGISTRO A LEER HI_BYTE
    BufferOUT[5] = RF3_LO    ' CANTIDAD DE REGISTRO A LEER LO_BYTE   
    LargoOUT     = 7         ' LARGO DE LA TRAMA A ENVIAR
    GoSub CRC16out           ' CALCULO EL CHECKSUM TRAMA A ENVIAR
    BufferOUT[6] = CRC16[0]	 ' CARGO EL CHECKSUM CALCULADO				
    BufferOUT[7] = CRC16[1]  ' CARGO EL CHECKSUM CALCULADO
    GoSub Envio_Pregunta     ' ENVIO LA PREGUNTA POR EL PUERTO SERIE
EndIf
'------------------------------ FN4 MODBUS ------------------------------------- 
If RF4 <> 0 Then
    BufferOUT[0] = SlaveAdd  ' DIRECCION MOSBUS DEL SLAVE
    BufferOUT[1] = 04        ' FUNCION MODBUS
    BufferOUT[2] = ADDMB_HI  ' DIRECCION DE REGISTRO A LLER HI_BYTE
    BufferOUT[3] = ADDMB_LO  ' DIRECCION DE REGISTRO A LLER LO_BYTE
    BufferOUT[4] = RF4_HI    ' CANTIDAD DE REGISTRO A LEER HI_BYTE
    BufferOUT[5] = RF4_LO    ' CANTIDAD DE REGISTRO A LEER LO_BYTE   
    LargoOUT     = 7         ' LARGO DE LA TRAMA A ENVIAR
    GoSub CRC16out           ' CALCULO EL CHECKSUM TRAMA A ENVIAR
    BufferOUT[6] = CRC16[0]	 ' CARGO EL CHECKSUM CALCULADO				
    BufferOUT[7] = CRC16[1]  ' CARGO EL CHECKSUM CALCULADO
    GoSub Envio_Pregunta     ' ENVIO LA PREGUNTA POR EL PUERTO SERIE
EndIf  
Return


'-------------------------------------------------------------------------------
'SUB RUTINA PARA LA FUNCION MODBUS 01 ( LEER ESTADO DE COIL )
'-------------------------------------------------------------------------------
MB_Funcion1:  'Addres 0000 - 9999 ( En este caso eleji  COMENZAR  por Add 200)
If NewTrama = 0 Then MAIN        '( PUEDE ALMACENAR EL ESTADO DE 64 COIL )
W0 = BufferIN[2]                 '( 8 COIL POR BYTE * 8 BYTE )
Select W0                  
   Case $01
    Rey8  = BufferIN[3]
   Case $02 
    Rey8  = BufferIN[3]
    Rey16 = BufferIN[4]
   Case $03                  
    Rey8  = BufferIN[3]
    Rey16 = BufferIN[4]
    Rey24 = BufferIN[5]
   Case $04                 
    Rey8  = BufferIN[3]
    Rey16 = BufferIN[4]
    Rey24 = BufferIN[5]
    Rey32 = BufferIN[6]
   Case $05                 
    Rey8  = BufferIN[3]
    Rey16 = BufferIN[4]
    Rey24 = BufferIN[5]
    Rey32 = BufferIN[6]
    Rey40 = BufferIN[7]
   Case $06                 
    Rey8  = BufferIN[3]
    Rey16 = BufferIN[4]
    Rey24 = BufferIN[5]
    Rey32 = BufferIN[6]
    Rey40 = BufferIN[7]
    Rey48 = BufferIN[8]    
   Case $07                 
    Rey8  = BufferIN[3]
    Rey16 = BufferIN[4]
    Rey24 = BufferIN[5]
    Rey32 = BufferIN[6]
    Rey40 = BufferIN[7]
    Rey48 = BufferIN[8]
    Rey56 = BufferIN[9]       
   Case $08                 
    Rey8  = BufferIN[3]
    Rey16 = BufferIN[4]
    Rey24 = BufferIN[5]
    Rey32 = BufferIN[6] 
    Rey40 = BufferIN[7]
    Rey48 = BufferIN[8]
    Rey56 = BufferIN[9]
    Rey64 = BufferIN[10]              
EndSelect                     
Return
'-------------------------------------------------------------------------------
' SUB RUTINA PARA LA FUNCION MODBUS 02 ( LEER INPUT ESTADOS )
'--------------------------------------------------------------------------------
MB_Funcion2:   'Addres 10000 - 19999   VER    NOTA N°1    A PIE DE PAGINA
If NewTrama = 0 Then MAIN
W0 = BufferIN[2]                 '( 8 input POR BYTE * 8 BYTE )
Select W0                  
   Case $01
    Inp8  = BufferIN[3]
   Case $02 
    Inp8  = BufferIN[3]
    Inp16 = BufferIN[4]
   Case $03                  
    Inp8  = BufferIN[3]
    Inp16 = BufferIN[4]
    Inp24 = BufferIN[5]
   Case $04                 
    Inp8  = BufferIN[3]
    Inp16 = BufferIN[4]
    Inp24 = BufferIN[5]
    Inp32 = BufferIN[6]
   Case $05                 
    Inp8  = BufferIN[3]
    Inp16 = BufferIN[4]
    Inp24 = BufferIN[5]
    Inp32 = BufferIN[6]
    Inp40 = BufferIN[7]
   Case $06                 
    Inp8  = BufferIN[3]
    Inp16 = BufferIN[4]
    Inp24 = BufferIN[5]
    Inp32 = BufferIN[6]
    Inp40 = BufferIN[7]
    Inp48 = BufferIN[8]    
   Case $07                 
    Inp8  = BufferIN[3]
    Inp16 = BufferIN[4]
    Inp24 = BufferIN[5]
    Inp32 = BufferIN[6]
    Inp40 = BufferIN[7]
    Inp48 = BufferIN[8]
    Inp56 = BufferIN[9]       
   Case $08                 
    Inp8  = BufferIN[3]
    Inp16 = BufferIN[4]
    Inp24 = BufferIN[5]
    Inp32 = BufferIN[6] 
    Inp40 = BufferIN[7]
    Inp48 = BufferIN[8]
    Inp56 = BufferIN[9]
    Inp64 = BufferIN[10]              
EndSelect  	
Return
'-------------------------------------------------------------------------------
' SUB RUTINA PARA LA FUNCION MODBUS 03 ( LEER HOLDING REGISTROS O FLOTANTES )
'-------------------------------------------------------------------------------
MB_Funcion3: 'Addres 40000 - 50000     VER             NOTA N°1 A PIE DE PAGINA
If NewTrama = 0 Then MAIN
If ADDMB >= 39999 And ADDMB < 49999 Then ' Verifico entre flotante y entero
    W0 = BufferIN[2]     'CANTIDAD DE BYTE ENVIADOS POR EL ESCLAVO
    Select W0            'ES UN ENTERO 2 BYTE
     Case $02
      Dh0_HI = BufferIN[3]
      Dh0_LO = BufferIN[4]
     Case $04
      Dh0_HI = BufferIN[3]
      Dh0_LO = BufferIN[4]
      Dh1_HI = BufferIN[5]
      Dh1_LO = BufferIN[6]
     Case $06
      Dh0_HI = BufferIN[3]
      Dh0_LO = BufferIN[4]
      Dh1_HI = BufferIN[5]
      Dh1_LO = BufferIN[6]
      Dh2_HI = BufferIN[7]
      Dh2_LO = BufferIN[8]  
     Case $08
      Dh0_HI = BufferIN[3]
      Dh0_LO = BufferIN[4]
      Dh1_HI = BufferIN[5]
      Dh1_LO = BufferIN[6]
      Dh2_HI = BufferIN[7]
      Dh2_LO = BufferIN[8]  
      Dh3_HI = BufferIN[9]
      Dh3_LO = BufferIN[10]
     Case $0A
      Dh0_HI = BufferIN[3]
      Dh0_LO = BufferIN[4]
      Dh1_HI = BufferIN[5]
      Dh1_LO = BufferIN[6]
      Dh2_HI = BufferIN[7]
      Dh2_LO = BufferIN[8]
      Dh3_HI = BufferIN[9]
      Dh3_LO = BufferIN[10]
      Dh4_HI = BufferIN[11]
      Dh4_LO = BufferIN[12]    
    EndSelect
 Else                     'ES UN FLOTANTE 4 BYTE
    W0 = BufferIN[2]      'CANTIDAD DE BYTE ENVIADOS POR EL ESCLAVO
    If W0 =  $04  Then
      Flt1 = BufferIN[3]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[4]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[5]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[6]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECHA
      AddMemo = 0
      GoSub IEEE745

    EndIf  
    If W0 =  $08 Then
      Flt1 = BufferIN[3]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[4]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[5]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[6]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECHA
      AddMemo = 0
      GoSub IEEE745
      Flt1 = BufferIN[7]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[8]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[9]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[10]      'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 4
      GoSub IEEE745    
     EndIf   
     If W0 =  $0C  Then
      Flt1 = BufferIN[3]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[4]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[5]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[6]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 0
      GoSub IEEE745
      Flt1 = BufferIN[7]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[8]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[9]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[10]      'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 4
      GoSub IEEE745  
      Flt1 = BufferIN[11]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[12]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[13]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[14]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 8
      GoSub IEEE745    
     EndIf     
     If W0 = $0F  Then        
      Flt1 = BufferIN[3]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[4]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[5]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[6]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 0
      GoSub IEEE745
      Flt1 = BufferIN[7]        'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[8]        'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[9]        'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[10]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 4
      GoSub IEEE745      
      Flt1 = BufferIN[11]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[12]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[13]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[14]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 8
      GoSub IEEE745
      Flt1 = BufferIN[15]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[16]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[17]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[18]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 12
      GoSub IEEE745  
     EndIf
     If W0 = $14 Then
      Flt1 = BufferIN[3]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[4]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[5]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[6]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 0
      GoSub IEEE745
      Flt1 = BufferIN[7]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[8]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[9]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[10]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 4
      GoSub IEEE745      
      Flt1 = BufferIN[11]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[12]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[13]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[14]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 8
      GoSub IEEE745
      Flt1 = BufferIN[15]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[16]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[17]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[18]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 12
      GoSub IEEE745   
      Flt1 = BufferIN[19]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE 
      Flt2 = BufferIN[20]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt3 = BufferIN[21]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Flt4 = BufferIN[22]       'ALMACENO LOS VALORES RECIVIDOS EN LOS BYTE
      Rlf Flt2                 'ROTO A LA IZQUIERDA LA MANTISA_0
      Rlf Flt1                 'ROTO A LA IZQUIERDA EL EXPONENTE
      Rrf Flt2                 'ROTO DE NUEVO LA MANTISA_0 PERO AHORA A LA DERECH
      AddMemo = 16
      GoSub IEEE745       
    EndIf
EndIf 
'GoSub LECTURA_ADC_VAR 
W0 = 0 
Return
'-------------------------------------------------------------------------------
'             SUB PARA LA FUNCION MODBUS 04 ( LEER INPUT REGISTROS )
'-------------------------------------------------------------------------------
MB_Funcion4:   ' Addres 20000 - 39999    VER    NOTA N°1    A PIE DE PAGINA
If NewTrama = 0 Then MAIN
W0 = BufferIN[2]     'CANTIDAD DE BYTE ENVIADOS POR EL ESCLAVO
Select W0
 Case $02
  D0_HI = BufferIN[3]
  D0_LO = BufferIN[4]
 Case $04
  D0_HI = BufferIN[3]
  D0_LO = BufferIN[4]
  D1_HI = BufferIN[5]
  D1_LO = BufferIN[6]
 Case $06
  D0_HI = BufferIN[3]
  D0_LO = BufferIN[4]
  D1_HI = BufferIN[5]
  D1_LO = BufferIN[6]
  D2_HI = BufferIN[7]
  D2_LO = BufferIN[8]  
 Case $08
  D0_HI = BufferIN[3]
  D0_LO = BufferIN[4]
  D1_HI = BufferIN[5]
  D1_LO = BufferIN[6]
  D2_HI = BufferIN[7]
  D2_LO = BufferIN[8]  
  D3_HI = BufferIN[9]
  D3_LO = BufferIN[10]
 Case $0A
  D0_HI = BufferIN[3]
  D0_LO = BufferIN[4]
  D1_HI = BufferIN[5]
  D1_LO = BufferIN[6]
  D2_HI = BufferIN[7]
  D2_LO = BufferIN[8]
  D3_HI = BufferIN[9]
  D3_LO = BufferIN[10]
  D4_HI = BufferIN[11]
  D4_LO = BufferIN[12]    
EndSelect                
Return
'-------------------------------------------------------------------------------
'             SUB PARA LA FUNCION MODBUS 05 ( SETEAR UN SIMPLE COIL )
'-------------------------------------------------------------------------------
MB_Funcion5: 'Addres 0000 - 9999       VER    NOTA N°1    A PIE DE PAGINA
'If NewTrama = 0 Then MAIN   
'( Esto equivale a 100 Coil )
' Cuando convierto la direccion del registro Modbus le sumo un 1 porque
' asi la direccion son las mismas que en el Master Modbus, sino tendria
' que preguntar por el Coil 199 y no por el 200
' Esto se repite en todas las funciones modbus del Maestro.
'W0 = (BufferIN[2]*256 + BufferIN[3]) + 1 'convierto la direccion modbus 1xx
'Select W0                    'Direccion de registro enviada en la trama recibida
'   Case 200                  'Direccion Coil 200
'    A4 = BufferIN[4]
'    If A4 = $FF Then 
'       SetBit PORTD,0
'    Else
'       ClearBit PORTD,0
'    EndIf   
'   Case 201                  'Direccion Coil 201
'    A4 = BufferIN[4]
'    If A4 = $FF Then 
'       SetBit PORTD,1
'    Else
'       ClearBit PORTD,1
'    EndIf 
'   Case 202                  'Direccion Coil 202
'    A4 = BufferIN[4]
'    If A4 = $FF Then 
'       SetBit PORTD,2
'    Else
'       ClearBit PORTD,2
'    EndIf 
'   Case 203                  'Direccion Coil 203
'    A4 = BufferIN[4]
'    If A4 = $FF Then 
'       SetBit PORTD,3
'    Else
'       ClearBit PORTD,3
'    EndIf    
'   Case 204                  'Direccion Coil 204
'    A4 = BufferIN[4]
'    If A4 = $FF Then 
'       SetBit PORTD,4
'    Else
'       ClearBit PORTD,4
'    EndIf 
'   Case 205                  'Direccion Coil 205
'    A4 = BufferIN[4]
'    If A4 = $FF Then 
'       SetBit PORTD,5
'    Else
'       ClearBit PORTD,5
'    EndIf 
'   Case 206                  'Direccion Coil 206
'    A4 = BufferIN[4]
'    If A4 = $FF Then 
'       SetBit PORTD,6
'    Else
'       ClearBit PORTD,6
'    EndIf 
'   Case 207                  'Direccion Coil 207
'    A4 = BufferIN[4]
'    If A4 = $FF Then 
'       SetBit PORTD,7
'    Else
'       ClearBit PORTD,7
'    EndIf    
'EndSelect 
'--------------------- ARMO LA TRAMA A ENVIAR ---------------------------------- 
'BufferOUT[0] = SlaveAdd    ' DIRECCION MOSBUS DEL SLAVE
'BufferOUT[1] = MBFnc       ' FUNCION MODBUS
'BufferOUT[2] = NumByte     ' CANTIDAD DE BYTE LEIDOS A ENVIAR
'BufferOUT[3] = BufferIN[3] ' DIRECCION MOSBUS coli HIGH
'BufferOUT[4] = BufferIN[4] ' DIRECCION MOSBUS coli LOW
'LargoOUT = 7 + NumByte     ' LARGO DE LA TRAMA A ENVIAR COMO RESPUESTA
'GoSub CRC16out             ' CALCULO EL CHECKSUM TRAMA A ENVIAR
'BufferOUT[LargoOUT - 1] = CRC16[0]	' CARGO EL CHECKSUM CALCULADO				
'BufferOUT[LargoOUT] =     CRC16[1]  ' CARGO EL CHECKSUM CALCULADO
'GoSub Envio_Pregunta          ' ENVIO DATOS POR EL PUERTO SERIE	
Return
'-------------------------------------------------------------------------------
'       SUB PARA LA FUNCION MODBUS 06 ( SETEAR SIMPLE HOLDING REGISTRO)
'-------------------------------------------------------------------------------
MB_Funcion6:  'Addres modbus de holdin registro 40000 - 50000 NOTA N°1 
'If NewTrama = 0 Then MAIN
'W0 = (BufferIN[2]*256 + BufferIN[3])+1 'Convierto la direccion modbus FN03
'Select W0
' Case 40000              ' Add 40000 del registro input registro FN = 03
' EWrite 100,[BufferIN[4]] ' Holding Reg. = word 65535 valor max, Add 40000
' EWrite 101,[BufferIN[5]] 
' BufferOUT[4] = BufferIN[4]
' BufferOUT[5] = BufferIN[5]
' Case 40001              ' Add 40001 del holding registro FN = 03
' EWrite 102,[BufferIN[4]] ' Holding Reg. = word 65535 valor max, Add 40000
' EWrite 103,[BufferIN[5]] 
' BufferOUT[4] = BufferIN[4]
' BufferOUT[5] = BufferIN[5] 
' Case 40002              ' Add 40002 del holding registro FN = 03
' EWrite 104,[BufferIN[4]] ' Holding Reg. = word 65535 valor max, Add 40000
' EWrite 105,[BufferIN[5]] 
' BufferOUT[4] = BufferIN[4]
' BufferOUT[5] = BufferIN[5] 
' Case 40003              ' Add 40003 del holding registro FN = 03
 'EWrite 106,[BufferIN[4]] ' Holding Reg. = word 65535 valor max, Add 40000
 'EWrite 107,[BufferIN[5]] 
 'BufferOUT[4] = BufferIN[4]
 'BufferOUT[5] = BufferIN[5] 
 'Case 40004              ' Add 40004 del holding registro FN = 03
 'EWrite 108,[BufferIN[4]] ' Holding Reg. = word 65535 valor max, Add 40000
 'EWrite 109,[BufferIN[5]]
 'BufferOUT[4] = BufferIN[4]
 'BufferOUT[5] = BufferIN[5]  
'EndSelect 
'--------------------- ARMO LA TRAMA A ENVIAR ---------------------------------- 
'BufferOUT[0] = SlaveAdd    ' DIRECCION MOSBUS DEL SLAVE
'BufferOUT[1] = MBFnc       ' FUNCION MODBUS
'BufferOUT[2] = BufferIN[2] ' DIRECCION MOSBUS H.Reg. HIGH
'BufferOUT[3] = BufferIN[3] ' DIRECCION MOSBUS H.Reg. LOW
'LargoOUT = 7               ' LARGO DE LA TRAMA A ENVIAR COMO RESPUESTA
'GoSub CRC16out             ' CALCULO EL CHECKSUM TRAMA A ENVIAR
'BufferOUT[LargoOUT - 1] = CRC16[0]	' CARGO EL CHECKSUM CALCULADO				
'BufferOUT[LargoOUT] =     CRC16[1]  ' CARGO EL CHECKSUM CALCULADO
'GoSub Envio_Pregunta          ' ENVIO DATOS POR EL PUERTO SERIE	
Return
'-------------------------------------------------------------------------------
'             SUB PARA LA FUNCION MODBUS 16 ( SETEAR SIMPLE FLOTANTE )
'-------------------------------------------------------------------------------
MB_Funcion16:  ' Addres 50010 EN ADELANTE
'If NewTrama = 0 Then MAIN
'W0 = (BufferIN[2]*256 + BufferIN[3])+1 'Convierto la direccion modbus FN03
'Select W0
' Case 50010              ' Add 50010 del registro flotantes FN = 03
'  Add = 40                ' Add memoria EEPROM del PIC
'  GoSub IEEE745
'  DAT_FL0 = ERead Add
'  Cnt_ADC   = DAT_FL0 / 1024' Para pasar el RAW al flotante del ADC
' Case 50012              ' Add 50014 del registro flotantes FN = 03
'  Add = 44              ' Add memoria EEPROM del PIC
'  GoSub IEEE745
' Case 50014              ' Add 50018 del registro flotantes FN = 03
'  Add = 48               ' Add memoria EEPROM del PIC
'  GoSub IEEE745
' Case 50016              ' Add 50022 del registro flotantes FN = 03
'  Add = 52               ' Add memoria EEPROM del PIC
'  GoSub IEEE745
' Case 50018              ' Add 50026 del registro flotantes FN = 03
'  Add = 56               ' Add memoria EEPROM del PIC
'  GoSub IEEE745
'EndSelect 
'--------------------- ARMO LA TRAMA A ENVIAR ---------------------------------- 
'BufferOUT[0] = SlaveAdd    ' DIRECCION MOSBUS DEL SLAVE
'BufferOUT[1] = MBFnc       ' FUNCION MODBUS
'BufferOUT[2] = BufferIN[2] ' DIRECCION MOSBUS H.Reg. HIGH
'BufferOUT[3] = BufferIN[3] ' DIRECCION MOSBUS H.Reg. LOW
'BufferOUT[4] = BufferIN[4] ' CANTIDAD DE REGISTROS 
'BufferOUT[5] = BufferIN[5] ' CANTIDAD DE REGISTROS
'LargoOUT = 7               ' LARGO DE LA TRAMA A ENVIAR COMO RESPUESTA
'GoSub CRC16out             ' CALCULO EL CHECKSUM TRAMA A ENVIAR
'BufferOUT[LargoOUT - 1] = CRC16[0]	' CARGO EL CHECKSUM CALCULADO				
'BufferOUT[LargoOUT] =     CRC16[1]  ' CARGO EL CHECKSUM CALCULADO
'GoSub Envio_Pregunta          ' ENVIO DATOS POR EL PUERTO SERIE	
Return


'///////////////////////////////////////////////////////////////////////////////
'*******************************************************************************
'                              PROGRAMA PRINCIPAL
'.................. Escribir aqui el programa principal ........................
'MEMORIA USADA 25%, RESTA EL 75% LIBRE PARA ESCRIBIR CUALQUIER PROGRAMA QUE SE
'DESEE
'BYTE DE VARIABLES USADAS DE LA MEMOR RAM 481, QUEDAN LIBRES 1000 APROX.
'*******************************************************************************
'///////////////////////////////////////////////////////////////////////////////
MAIN:
Cls
RF1 = 64                     'MAXIMO NUMERO DE COIL 64
RF2 = 0
RF3 = 0
RF4 = 0
ADDMB = 200                  'DIRECCION DE INICIO
GoSub PREGUNTAS_MB
Print At 1,1, Bin8 Rey8," ",Bin8 Rey16," ", Bin8 Rey24," ", Bin8 Rey32 
Print At 2,1, Bin8 Rey40," ",Bin8 Rey48," ", Bin8 Rey56," ", Bin8 Rey64
DelayMS 1000
Cls
RF1 = 0
RF2 = 64                     'MAXIMO NUMERO DE INPUT 64
RF3 = 0
RF4 = 0
ADDMB = 10000                'DIRECCION DE INICIO
GoSub PREGUNTAS_MB
Print At 1,1, Bin8 Inp8," ",Bin8 Inp16," ", Bin8 Inp24," ", Bin8 Inp32 
Print At 2,1, Bin8 Inp40," ",Bin8 Inp48," ", Bin8 Inp56," ", Bin8 Inp64
PORTD  = Inp8
DelayMS 1000
Cls
RF1 = 0
RF2 = 0                     'MAXIMO NUMERO DE COIL 64
RF3 = 5
RF4 = 0
ADDMB = 40000                'DIRECCION DE INICIO
GoSub PREGUNTAS_MB
Print At 1,1, Dec DtHr0," ",Dec DtHr1," ",Dec DtHr2," ",Dec DtHr3," ",Dec DtHr4 
DelayMS 1000
Cls
RF1 = 0
RF2 = 0                     'MAXIMO NUMERO DE COIL 64
RF3 = 10
RF4 = 0
ADDMB = 50000                'DIRECCION DE INICIO
GoSub PREGUNTAS_MB
Print At 1,1, Dec2 DAT_FL0," ",Dec2 DAT_FL1," ",Dec2 DAT_FL2," ",Dec2 DAT_FL3," ",Dec2 DAT_FL4 
DelayMS 1000
Cls
RF1 = 0
RF2 = 0                     'MAXIMO NUMERO DE COIL 64
RF3 = 0
RF4 = 5
ADDMB = 20000                'DIRECCION DE INICIO
GoSub PREGUNTAS_MB
Print At 1,1, Dec Dato0," ",Dec Dato1," ",Dec Dato2," ",Dec Dato3," ",Dec Dato4 
DelayMS 1000
GoTo MAIN
End
'*******************************************************************************
'NOTA N°1:
'-------------------------------------------------------------------------------
' Definición de Addres Modbus según Función
'-------------------------------------------------------------------------------
'       DESCRIPCION                 FUNCION MODBUS    RANGO DE DIRECCION USADA
' Para los Relay            (Coil)       Fn1        Addres 0000  hasta la 9999
' Para las Ent. Digitales   (Input)      Fn2        Addres 10000 hasta la 19999
' Para los Holding Registro (Enteros)    Fn3        Addres 40000 hasta la 49999
' Para los Flotantes        (Float)      Fn3        Addres 50000 hasta la 59999   
' Para los Input Registro   (Enteros)    Fn4        Addres 20000 hasta la 39999
'*******************************************************************************
' NOTA N°2 :
'-------------------------------------------------------------------------------
' ORGANIZACION DE MEMORIA EEPROM DE MICROCHP 18F4520 256 BYTE  
' los datos de lectura de la memoria deben de ser consecuitivos para un mismo
' tipo de variable
'------------------------------------------------------------------------------- 
' De la direccion 00-99 lo asigne a flotantes Fn=3 y Fn=16           4 byte 
' De la direccion 100-149 lo Asigne a enteros del Word = 65535 Fn=3  2 byte 
' De la direccion 150-199 Lo Asigno a input Registros Fn=4           2 byte 
' De la direccion 200-256 Libres de utilizacion
'-------------------------------------------------------------------------------
'*******************************************************************************
' NOTA N°3 :
'-------------------------------------------------------------------------------
' CONVERCION DE FLOTANTE MICROCHIP A NORNMA IEEE754
'-------------------------------------------------------------------------------
'El problema que me encontré cuando quise enviar un flotante por comunicación 
'Modbus desde el PIC a la PC es que estos utilizan la norma IEEE-745 , y  el 
'estándar Modbus utiliza IEEE-754, para tal fin tuve que realizar la conversión
'de un formato a otro, debajo de estas líneas esta la Explicación del manual 
'Del Proton IDE de cómo hacerlo. y en mi programa verán la implementación del
'del mismo que es muy simple. 
' EXPLICACION DE CONVERCION DE FORMATOS DE COMA FLOTANTE SACADO DEL MANUAL DE
' PROTON-IDE
'El compilador de Proton utiliza la variación de Microchip del formato de punto 
'flotante IEEE 754. Las diferencias al estándar IEEE 745 son menores y están 
'bien documentados en la nota de aplicación de Microchip
'AN575 (descargable de www.microchip.com).
'Los números de coma flotante se representan en un formato IEEE-754 modificado.
'Este formato permite rutinas de punto flotante para aprovechar la arquitectura
'deL PIC y reducir la cantidad de gastos generales necesarios en los cálculos. 
'La representación se muestra a continuación en comparación con el Formato 
'IEEE-754: donde (S) es el bit de signo, (Y) es el lsb del exponente y (X) es 
'un marcador de posición para los bits de mantisa y exponente.
'Los dos formatos se pueden convertir fácilmente de uno a otro mediante la 
'manipulación del exponente y Mantisa0. El siguiente código ensamblador muestra 
'un ejemplo de esta operación.

' IEEE-754
'   EXPONENTE     MANTISA_0      MANTISA_1     MANTISA_2
'  S-XXX-XXXX     Y-XXX-XXXX     XXXX-XXXX     XXXX-XXXX
'     Flt1           Flt2          Flt3          Flt4
'MICROCIP 
'   EXPONENTE     MANTISA_0     MANTISA_1     MANTISA_2
'  XXXX-XXX-Y    S-XXX-XXXX     XXXX-XXXX     XXXX-XXXX
'     Flt1           Flt2          Flt3          Flt4
'-------------------------------------------------------------------------------
'IEEE-754    A   Microchip

'Rlf MANTISA_0     Rlf es un comando en Assembler para rotar bit a la Izquierda
'Rlf EXPONENTE
'Rrf MANTISA_0     Rrf es un comando en Aseembler para rotar bit a la derecha
'-------------------------------------------------------------------------------
'Microchip   A   IEEE-754

'Rlf MANTISA_0
'Rrf EXPONENTE
'Rrf MANTISA_0
'-------------------------------------------------------------------------------
' NOTA N°4 :
'-------------------------------------------------------------------------------
' TIMER 2 PRESCALA 16
'-------------------------------------------------------------------------------
'Para 32Mhz de OSC
'32Mhz / 4 , esto es según el datasheet, pues en osc_interno se divide por 4
'con lo cual tenemos 8Mhz, en nuestro caso la preescala se fijo en 8, esto 
'significa 8Mhz/16 = 0.5MHz que será la frecuencia de nuestro Timer 2
'el Timer2 es de 16Bit así que cuenta 65536 pulsos para una interrupción
'Con lo cual cada pulso de nuestro timer valdrá = 1/0.5MHz = 2uSg
'Ahora si queremos que cuente 1 segundo , tendremos que contar 500.000 Pulsos
'*******************************************************************************


