'*******************************************************************************
'*  Name    : MODBUS-Slave-PIC18F4520.BAS                                      *
'*  Author  : Aldo Javier Ercigoj                                              *
'*  Notice  : Copyright (c) 2021                                               *
'*          : All Rights Reserved                                              *
'*  Date    : 11/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 Dat0               As Byte     'Byte, estado de los pines IN/OUT portB
Dim Dat1               As Byte     'Byte, estado de los pines IN/OUT portC
Dim Dat2               As Byte     'Byte, estado de los pines IN/OUT portD
Dim Dat3               As Byte     'Byte, estado de los pines IN/OUT PortE
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 Dat0
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
'--------- 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 W0                 As Word         'Dirección de Registro Modbus a leer
Dim W1                 As Word         'Dirección modbus elegida 
Dim RAW                As Word         'Para la conversión adc Word 
Dim RAW_LO             As RAW.LowByte  'Byte bajo de la conversión ADC
Dim RAW_HI             As RAW.HighByte 'Byte alto de la conversión ADC
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 Dato0              As Word         'Datos Enteros a leer Holding Registros
Dim D0_LO              As Dato0.LowByte
Dim D0_HI              As Dato0.HighByte
Dim Dato1              As Word         'Datos Enteros a leer Holding Registros
Dim D1_LO              As Dato1.LowByte
Dim D1_HI              As Dato1.HighByte
Dim Dato2              As Word         'Datos Enteros a leer Holding Registros
Dim D2_LO              As Dato2.LowByte
Dim D2_HI              As Dato2.HighByte
Dim Dato3              As Word         'Datos Enteros a leer Holding Registros
Dim D3_LO              As Dato3.LowByte
Dim D3_HI              As Dato3.HighByte
Dim Dato4              As Word         'Datos Enteros a leer Holding Registros
Dim D4_LO              As Dato4.LowByte
Dim D4_HI              As Dato4.HighByte
'...............................................................................
Dim FloatReg           As Float        'Para el valor float del ADC, Holding R.
Dim Cnt_ADC            As Float        'Escala del ADC = 5 Volt / 1024 bit
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
Dim CH0                As Float        'VALOR ADC0 FLOTANTE CH0
Dim CH1                As Float        'VALOR ADC1 FLOTANTE CH1
Dim CH2                As Float        'VALOR ADC2 FLOTANTE CH2
Dim CH3                As Float        'VALOR ADC3 FLOTANTE CH3
Dim CH4                As Float        'VALOR ADC4 FLOTANTE CH4
'*******************************************************************************
' 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                 '|-----------------------------------|
TRISC.6   = 0                 ' Defino el PIN RC6 como OUT
TRISC.7   = 1                 ' Defino el pin RC7 como IN
TRISB     = %11111111         ' Defino el puertoB como Entrada
TRISD     = %00000000         ' Defino el puertoD como Salidas
TRISE     = %00000111         ' Defino el puertoE como Entrada
DAT_FL0   = ERead 40          ' Leo de la memoria EEPROM la escala elejida
Cnt_ADC   = DAT_FL0 / 1024    ' Para pasar el RAW de ADC a flotante y escalarlo
'-------------------------- Inicializo la USART --------------------------------
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 
'*******************************************************************************						
'                   DOY NOMBRE A LOS PINES DE LOS PUERTOS
'*******************************************************************************
'ENTRADAS DIGITALES.............................................................
Symbol IN1  = PORTB.0
Symbol IN2  = PORTB.1
Symbol IN3  = PORTB.2
Symbol IN4  = PORTB.3
Symbol IN5  = PORTB.4
Symbol IN6  = PORTB.5
Symbol IN7  = PORTB.6
Symbol IN8  = PORTB.7

Symbol IN9  = PORTE.0
Symbol IN10 = PORTE.1
Symbol IN11 = PORTE.2
Symbol IN12 = PORTE.3
'SALIDAS DIGITALES .............................................................
Symbol COIL1  = PORTD.0
Symbol COIL2  = PORTD.1
Symbol COIL3  = PORTD.2
Symbol COIL4  = PORTD.3
Symbol COIL5  = PORTD.4
Symbol COIL6  = PORTD.5
Symbol COIL7  = PORTD.6
Symbol COIL8  = PORTD.7

'*******************************************************************************
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  
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
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 > 50 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 MODBUS
 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_Datos:  'Borro el Display si las cadenas son muy largas
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
'-------------------------------------------------------------------------------
' Muestro las cadenas de byte resividas y enviadas en el display 
' Se puede borra si no se utiliza 
' El display debe de ser  40 x 2 alfanumerico 
'-------------------------------------------------------------------------------
'cls
'A2 = LargoOUT + 1
'A2 = A2 * 3
'A1  = 0
'If A2 > 40 Then A2 = 1:Print At 2,3,"  CADENA MUY LARGA PARA VISUALIZAR"
'For A0 = 1 To A2 Step 3
'    Print At 2,A0,Hex2 BufferOUT[A1]
'    A1 = A1 + 1
'Next
'A7 = CntByteIN
'A7 = A7 * 3
'A5 = 0 
'For A6 = 1 To A7 Step 3
'    Print At 1,A6,Hex2 BufferIN[A5]
'    A5 = A5 + 1
'Next
Clear BufferOUT
Return
'-------------------------------------------------------------------------------
'  SUB PARA CARGAR EN EL BUFER DE SALIDA (BufferOUT) TODOS LOS DATOS PEDIDOS
'-------------------------------------------------------------------------------
Leo_DATOS_EEPROM:
For B0 = 3 To AddMemo
    Byte_Enviar = ERead Add_EEPROM  ' leo el Dat0 de le EEPROM INTERNA 
 	BufferOUT[B0] = Byte_Enviar
    Add_EEPROM = Add_EEPROM + 1
Next
Add_EEPROM = 0
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
        Msg_Error = $01   ' VERIFICP LAS FN MODBUS QUE SEAN CORRECTAS
	Else
	If BufferIN[4] > 0 And BufferIN[5] > 255 Then 
		MBFnc =  BufferIN[1] + $80
		Msg_Error = $03   ' VERIFICO QUE NO SUPERE LOS 255 REGISTROS
	Else
	If CntByteIN < 7 Then 
		MBFnc =  BufferIN[1] + $80
		Msg_Error = $03   ' TRAMA MUY CORTA
	Else
    If (BufferIN[2] * 256 + BufferIN[3])> 60000 Then 
		MBFnc =  BufferIN[1] + $80
		Msg_Error = $02   ' VERIFICO QUE LA DIRECCION DEL Dat0 NO EXEDA EL WORD
	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!!!                                                
      NumReg = (BufferIN[4] * 256) + BufferIN[5]'CONVIERTO LOS BYTE DE NUM REG.
     EndIf
    EndIf
   EndIf
  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
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
'---------------------- LO CONVIERTO DE IEEE754 A MICROCHIP --------------------
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
'----------------- LOS GUARDO YA CONVERTIDO EN LA MEMORIA EEPROM ---------------
EWrite  Add,     [Flt1]  'ALMACENO EN LA MEMORIA EEPROM EL 1 BYTE 
DelayMS 5
EWrite (Add + 1),[Flt2]  'ALMACENO EN LA MEMORIA EEPROM EL 2 BYTE
DelayMS 5
EWrite (Add + 2),[Flt3]  'ALMACENO EN LA MEMORIA EEPROM EL 3 BYTE
DelayMS 5
EWrite (Add + 3),[Flt4]  'ALMACENO EN LA MEMORIA EEPROM EL 4 BYTE
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    '( Esto equivale a 100 Coil )
Clear Dat0 
' 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
If NumReg = 1 Then           'Direccion del registro input registro FN = 02
  Select W0                  'Direccion de registro enviada en la trama recibida
   Case 200                  'Direccion Coil 200
    BitVal = GetBit PORTD,0
    LoadBit Dat0,0,BitVal
    BufferOUT[3] = Dat0
   Case 201                  'Direccion Coil 201
    BitVal = GetBit PORTD,1
    LoadBit Dat0,0,BitVal
    BufferOUT[3] = Dat0
   Case 202                  'Direccion Coil 202
    BitVal = GetBit PORTD,2
    LoadBit Dat0,0,BitVal
    BufferOUT[3] = Dat0
   Case 203                  'Direccion Coil 203
    BitVal = GetBit PORTD,3
    LoadBit Dat0,0,BitVal
    BufferOUT[3] = Dat0    
   Case 204                  'Direccion Coil 204
    BitVal = GetBit PORTD,4
    LoadBit Dat0,0,BitVal
    BufferOUT[3] = Dat0
   Case 205                  'Direccion Coil 205
    BitVal = GetBit PORTD,5
    LoadBit Dat0,0,BitVal
    BufferOUT[3] = Dat0
   Case 206                  'Direccion Coil 206
    BitVal = GetBit PORTD,6
    LoadBit Dat0,0,BitVal
    BufferOUT[3] = Dat0
   Case 207                  'Direccion Coil 207
    BitVal = GetBit PORTD,7
    LoadBit Dat0,0,BitVal
    BufferOUT[3] = Dat0    
  EndSelect 
Else 
Select NumReg
   Case 1 To 8   
    W0 = W0 - 200
    For PinNumero = W0 To (W0 + NumReg)-1
     BitVal = GetBit PORTD,PinNumero
     LoadBit Dat0,PinNumero,BitVal
    Next
    BufferOUT[3] = Dat0
   Case  9 To 16                  ' SELECIONAR SEGUN CONFIGURACION DE PINES
    'BufferOUT[3] = (Dat0 = PORTB)' y la cantidad de puertos que elija A,B,C,D,E
    'BufferOUT[5] = (Dat1 = PORTC)' hay que configurar los dos puestos
   EndSelect 
EndIf                    
'--------------------- 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
LargoOUT = 4 + NumByte   ' LARGO DE LA TRAMA A ENVIAR COMO RESPUESTA
AddMemo  = 2 + NumByte   ' POSISIN DE INICIO DE LECTURA DE LA EEPROM 
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_Datos        ' ENVIO DATOS POR EL PUERTO SERIE	
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
Clear Dat0
Clear Dat1
W0 = (BufferIN[2]*256 + BufferIN[3]) + 1 'convierto la direccion modbus 1xxxx
If NumReg = 1 Then
    Select W0
       Case 10000                'direccion 10000 del input registro FN = 02
         BitVal = GetBit PORTB,0
         LoadBit Dat0,0,BitVal
         BufferOUT[3] = Dat0
       Case 10001                'direccion 10001 del input registro FN = 02
         BitVal = GetBit PORTB,1
         LoadBit Dat0,0,BitVal
         BufferOUT[3] = Dat0
       Case 10002                'direccion 10002 del input registro FN = 02
         BitVal = GetBit PORTB,2
         LoadBit Dat0,0,BitVal
         BufferOUT[3] = Dat0
       Case 10003                'direccion 10003 del input registro FN = 02
         BitVal = GetBit PORTB,3
         LoadBit Dat0,0,BitVal
         BufferOUT[3] = Dat0
       Case 10004                'direccion 10004 del input registro FN = 02
         BitVal = GetBit PORTB,4
         LoadBit Dat0,0,BitVal
         BufferOUT[3] = Dat0
       Case 10005                'direccion 10005 del input registro FN = 02
         BitVal = GetBit PORTB,5
         LoadBit Dat0,0,BitVal
         BufferOUT[3] = Dat0
       Case 10006                'direccion 10006 del input registro FN = 02
         BitVal = GetBit PORTB,6
         LoadBit Dat0,0,BitVal
         BufferOUT[3] = Dat0
       Case 10007                'direccion 10007 del input registro FN = 02
         BitVal = GetBit PORTB,7
         LoadBit Dat0,0,BitVal
         BufferOUT[3] = Dat0
       Case 10008                'direccion 10008 del input registro FN = 02
         BitVal = GetBit PORTE,0
         LoadBit Dat0,0,BitVal
         BufferOUT[3] = Dat0
       Case 10009                'direccion 10009 del input registro FN = 02
         BitVal = GetBit PORTE,1
         LoadBit Dat0,0,BitVal
         BufferOUT[3] = Dat0
       Case 10010                'direccion 10010 del input registro FN = 02
         BitVal = GetBit PORTE,2
         LoadBit Dat0,0,BitVal
         BufferOUT[3] = Dat0
       Case 10011                'direccion 10011 del input registro FN = 02
         BitVal = GetBit PORTE,3
         LoadBit Dat0,0,BitVal
         BufferOUT[3] = Dat0                 
    EndSelect 
Else 
    Select NumReg                 ' SELECIONAR SEGUN CONFIGURACION DE PINES
'-------------------------------------------------------------------------------
       Case 1  To 8
         W1 = W0 - 10000 
         PosBit = 0
         If W1 <= 7  Then
            For PinNumero = W1 To 7            
               BitVal = GetBit PORTB,PinNumero
               LoadBit Dat0,PosBit,BitVal
               PosBit = PosBit + 1
            Next
            AddPOS = (8 - W1) - NumReg
            For PinNumero = 0 To AddPOS
               BitVal = GetBit PORTE,PinNumero
               LoadBit Dat0,PosBit,BitVal
               PosBit = PosBit + 1
            Next
            BufferOUT[3] = Dat0 
         Else  
            W1 = W1 - 8
            For PinNumero = W1 To (W1 + NumReg)-1
               BitVal = GetBit PORTE,PinNumero
               LoadBit Dat0,PinNumero,BitVal
            Next
            BufferOUT[3] = Dat0 
         EndIf
         
'------------------------------------------------------------------------------
       Case 9 To 16 
         W1 = W0 - 10000 
         If W1 <= 7 Then
            For PinNumero = W1 To 7
              BitVal = GetBit PORTB,PinNumero
              LoadBit Dat0,PinNumero,BitVal
            Next
            BufferOUT[3] = Dat0
            For PinNumero = 0 To (NumReg - (9 - W1))
              BitVal = GetBit PORTE,PinNumero
              LoadBit Dat1,PinNumero,BitVal
            Next
            BufferOUT[4] = Dat1
         Else'Esta condicion nuca se va a dar porque solo tengo 4 pin en el RE0-RE4
'           Pero en caso de utilizar mas puertos digitales tener en cuenta
'           W1 = W1 - 8
'           For PinNumero = W1 To (W1 + NumReg)-1
'           BitVal = GetBit PORTE,PinNumero
'           LoadBit Dat1,PinNumero,BitVal
'         Next
'         BufferOUT[4] = Dat1
         EndIf 
    EndSelect 
EndIf                    
'--------------------- 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
LargoOUT = 4 + NumByte   ' LARGO DE LA TRAMA A ENVIAR COMO RESPUESTA
AddMemo  = 2 + NumByte   ' POSISIN DE INICIO DE LECTURA DE LA EEPROM 
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_Datos        ' ENVIO DATOS POR EL PUERTO SERIE	
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
W0 = 0
' VERIFICO QUE RIRECCION MODBUS QUE QUIERO LEER  Y LE SUMO 1, ASI SON LAS MISMAS 
' QUE EN EL MAESTRO. YA QUE SINO, ME ENVIA EL ADD MENOS 1
W0 = (BufferIN[2]*256 + BufferIN[3])+1 'Convierto la direccion modbus FN03
Select W0
 Case 50000              ' Add 50000 del registro input registro FN = 03
  Add_EEPROM = 0        ' Add memoria EEPROM del PIC
 Case 50002              ' Add 50002 del registro input registro FN = 03
  Add_EEPROM = 4        ' Add memoria EEPROM del PIC
 Case 50004              ' Add 50004 del registro input registro FN = 03
  Add_EEPROM = 8        ' Add memoria EEPROM del PIC
 Case 50006              ' Add 50006 del registro input registro FN = 03
  Add_EEPROM = 12        ' Add memoria EEPROM del PIC
 Case 50008              ' Add 50008 del registro input registro FN = 03
  Add_EEPROM = 16        ' Add memoria EEPROM del PIC
 Case 50010              ' Add 50010 del registro input registro FN = 03
  Add_EEPROM = 20        ' Add memoria EEPROM del PIC
 Case 50012              ' Add 50012 del registro input registro FN = 03
  Add_EEPROM = 24        ' Add memoria EEPROM del PIC
 Case 50014              ' Add 50014 del registro input registro FN = 03
  Add_EEPROM = 28        ' Add memoria EEPROM del PIC
 Case 50016              ' Add 50016 del registro input registro FN = 03
  Add_EEPROM = 32        ' Add memoria EEPROM del PIC
 Case 50018              ' Add 50018 del registro input registro FN = 03
  Add_EEPROM = 36        ' Add memoria EEPROM del PIC          
'------------------------ Holding Registros ------------------------------------
 Case 40000              ' Add 40000 del registro input registro FN = 03
  Add_EEPROM = 100        ' Add memoria EEPROM del PIC
 Case 40001              ' Add 40001 del registro input registro FN = 03
  Add_EEPROM = 102        ' Add memoria EEPROM del PIC
 Case 40002              ' Add 40002 del registro input registro FN = 03
  Add_EEPROM = 104        ' Add memoria EEPROM del PIC
 Case 40003              ' Add 40003 del registro input registro FN = 03
  Add_EEPROM = 106        ' Add memoria EEPROM del PIC
 Case 40004              ' Add 40004 del registro input registro FN = 03
  Add_EEPROM = 108        ' Add memoria EEPROM del PIC
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
LargoOUT = 4 + NumByte   ' LARGO DE LA TRAMA A ENVIAR COMO RESPUESTA
AddMemo  = 2 + NumByte   ' POSISIN DE INICIO DE LECTURA DE LA EEPROM
GoSub Leo_DATOS_EEPROM   ' SALTO A LA SUBRUTINA Y CARLO LOS DATOS
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_Datos        ' ENVIO DATOS POR EL PUERTO SERIE
Return
'-------------------------------------------------------------------------------
'             SUB PARA LA FUNCION MODBUS 04 ( LEER INPUT REGISTROS )
'-------------------------------------------------------------------------------
MB_Funcion4:   ' Addres 20000 - 39999    VER    NOTA N°1    A PIE DE PAGINA
W0 = 0         'En este caso leeo los datos digitales de el ADC de 10 bit
               ' y los guardo en la EEprom para enviarlos por modbus
               ' Se puede utilizar la funcion FN3 y leerlos como H.Reg 
If NewTrama = 0 Then MAIN
W0 = (BufferIN[2]*256 + BufferIN[3])+1 'convierto la direccion modbus 20xxxx
Select W0
 Case 20000    'direccion del registro input registro FN = 04
  Add_EEPROM = 150
 Case 20001 
  Add_EEPROM = 152
 Case 20002 
  Add_EEPROM = 154
 Case 20003 
  Add_EEPROM = 156
 Case 20004 
  Add_EEPROM = 158
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
LargoOUT = 4 + NumByte   ' LARGO DE LA TRAMA A ENVIAR COMO RESPUESTA
AddMemo  = 2 + NumByte   ' POSISIN DE INICIO DE LECTURA DE LA EEPROM
GoSub Leo_DATOS_EEPROM   ' SALTO A LA SUBRUTINA Y CARGO LOS DATOS
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_Datos        ' ENVIO DATOS POR EL PUERTO SERIE
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_Datos          ' 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_Datos          ' 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_Datos          ' ENVIO DATOS POR EL PUERTO SERIE	
Return

'-------------------------------------------------------------------------------
'               SUB PARA EJECUTAR EL PROTOCOLO MODBUS
'-------------------------------------------------------------------------------
MODBUS:
GoSub CheckMB		   ' Check la trama MODBUS recibida	
Select MBFnc
   Case 1
       If NumReg > 8 Then  
         NumRe = NumReg / 8 ' Calculo el numero de byte a enviar entero 2
           If NumRe < 2 Or NumRe < 3 Or NumRe < 4  Then 
             NumByte = NumRe + 1  ' maximo 4 puertos del pic              
           Else
             NumByte = 2 
           EndIf 
      Else
         NumByte = 1
     EndIf 
      GoSub MB_Funcion1    ' respuesta funcion modbus 1
   Case 2
      If NumReg > 8 Then  
         NumRe = NumReg / 8 ' Calculo el numero de byte a enviar entero 2
           If NumRe < 2 Or NumRe < 3 Or NumRe < 4  Then 
             NumByte = NumRe + 1  ' maximo 4 puertos del pic
            Else
             NumByte = 2 
           EndIf 
      Else
         NumByte = 1
     EndIf 
      GoSub MB_Funcion2    ' respuesta funcion modbus 2 
   Case 3
      NumByte = NumReg * 2 ' Calculo el numero de byte a enviar flotante 2
      GoSub MB_Funcion3    ' respuesta funcion modbus 3
   Case 4
      NumByte = NumReg * 2 ' Calculo el numero de byte a enviar entero 2
      GoSub MB_Funcion4    ' respuesta funcion modbus 4
   Case 5
      NumByte = NumReg     ' Calculo el numero de byte a enviar entero 
      GoSub MB_Funcion5    ' respuesta funcion modbus 5
   Case 6
      NumByte = NumReg     ' Calculo el numero de byte a enviar entero 
      GoSub MB_Funcion6    ' respuesta funcion modbus 6
   Case 16
      GoSub MB_Funcion16   ' respuesta funcion modbus 16
EndSelect   
NewTrama = 0
Return
'-------------------------------------------------------------------------------
'  SUB PARA LEER TODOS LOS DATOS DE LA EEPROM
'-------------------------------------------------------------------------------
LECTURA_ADC_VAR:
' CH0 ANALOGICO ---------------------------------------------------------------- 
RAW = ADIn 0       'LEO EL CANAL ANALOGICO N°0  
EWrite 150,[RAW_HI] 'invierto el orden en que se almacenan los byte del word raw
DelayMS 5
EWrite 151,[RAW_LO] 'invierto el orden en que se almacenan los byte del word raw
FloatReg = RAW*Cnt_ADC 'CONVIERTO EL VALOR LEIDO DEL ADC A FLOTANTE
CH0 = FloatReg     'GUARDO EL VALOR LEIDO PARA USO DEL PIC
Add = 0            'DIRECCION DE MEMORIA EEPROM DEL PIC
GoSub IEEE754      'SUB-RUTINA PARA CONVERCION DE FLOTANTES MICROCHIP A IEEE754
' CH1 ANALOGICO ----------------------------------------------------------------
RAW = ADIn 1       'LEO EL CANAL ANALOGICO N°1
EWrite 152,[RAW_HI] 'invierto el orden en que se almacenan los byte del word raw
DelayMS 5
EWrite 153,[RAW_LO] 'invierto el orden en que se almacenan los byte del word raw
FloatReg = RAW*Cnt_ADC 'CONVIERTO EL VALOR LEIDO DEL ADC A FLOTANTE
CH1 = FloatReg      'GUARDO EL VALOR LEIDO PARA USO DEL PIC
Add = 4             'DIRECCION DE MEMORIA EEPROM DEL PIC
GoSub IEEE754       'SUB-RUTINA PARA CONVERCION DE FLOTANTES MICROCHIP A IEEE754
' CH2 ANALOGICO ---------------------------------------------------------------- 
RAW = ADIn 2        'LEO EL CANAL ANALOGICO N°2
EWrite 154,[RAW_HI] 'invierto el orden en que se almacenan los byte del word raw
DelayMS 5
EWrite 155,[RAW_LO] 'invierto el orden en que se almacenan los byte del word raw
FloatReg = RAW*Cnt_ADC 'CONVIERTO EL VALOR LEIDO DEL ADC A FLOTANTE
CH2 = FloatReg      'GUARDO EL VALOR LEIDO PARA USO DEL PIC
Add = 8             'DIRECCION DE MEMORIA EEPROM DEL PIC
GoSub IEEE754       'SUB-RUTINA PARA CONVERCION DE FLOTANTES MICROCHIP A IEEE754
' CH3 ANALOGICO ---------------------------------------------------------------- 
RAW = ADIn 3        'LEO EL CANAL ANALOGICO N°3
EWrite 156,[RAW_HI] 'invierto el orden en que se almacenan los byte del word raw
DelayMS 5
EWrite 157,[RAW_LO] 'invierto el orden en que se almacenan los byte del word raw
FloatReg = RAW*Cnt_ADC 'CONVIERTO EL VALOR LEIDO DEL ADC A FLOTANTE
CH3 = FloatReg      'GUARDO EL VALOR LEIDO PARA USO DEL PIC
Add = 12            'DIRECCION DE MEMORIA EEPROM DEL PIC
GoSub IEEE754       'SUB-RUTINA PARA CONVERCION DE FLOTANTES MICROCHIP A IEEE754
' CH4 ANALOGICO ----------------------------------------------------------------  
RAW = ADIn 4        'LEO EL CANAL ANALOGICO N°4
EWrite 158,[RAW_HI] 'invierto el orden en que se almacenan los byte del word raw
DelayMS 5
EWrite 159,[RAW_LO] 'invierto el orden en que se almacenan los byte del word raw
FloatReg = RAW*CNT_ADC 'CONVIERTO EL VALOR LEIDO DEL ADC A FLOTANTE
CH4 = FloatReg      'GUARDO EL VALOR LEIDO PARA USO DEL PIC
Add = 16            'DIRECCION DE MEMORIA EEPROM DEL PIC
GoSub IEEE754       'SUB-RUTINA PARA CONVERCION DE FLOTANTES MICROCHIP A IEEE754
'-------------------------------------------------------------------------------
DAT_FL0 = ERead 40      'LOS VALORES RECIVIDOS POR MODBUS FUERON GUARDADOS EN
DAT_FL1 = ERead 44      'LA MEMORIA EEPROM UNA VEZ CONVERTIDO DE FORMATO 
DAT_FL2 = ERead 48      'FLOTANTE, PARA QUE EL PIC LO INTERPRETE BIEN LOS LEEO
DAT_FL3 = ERead 52
DAT_FL4 = ERead 56
FloatReg = DAT_FL0  'VARIABLE FLOTANTE A CONVERTIR
Add = 20            'DIRECCION DE MEMORIA EEPROM DEL PIC
GoSub IEEE754       'SUB-RUTINA PARA CONVERCION DE FLOTANTES MICROCHIP A IEEE754
FloatReg = DAT_FL1  'VARIABLE FLOTANTE A CONVERTIR
Add = 24            'DIRECCION DE MEMORIA EEPROM DEL PIC
GoSub IEEE754       'SUB-RUTINA PARA CONVERCION DE FLOTANTES MICROCHIP A IEEE754
FloatReg = DAT_FL2  'VARIABLE FLOTANTE A CONVERTIR
Add = 28            'DIRECCION DE MEMORIA EEPROM DEL PIC
GoSub IEEE754       'SUB-RUTINA PARA CONVERCION DE FLOTANTES MICROCHIP A IEEE754
FloatReg = DAT_FL3  'VARIABLE FLOTANTE A CONVERTIR
Add = 32            'DIRECCION DE MEMORIA EEPROM DEL PIC
GoSub IEEE754       'SUB-RUTINA PARA CONVERCION DE FLOTANTES MICROCHIP A IEEE754
FloatReg = DAT_FL4  'VARIABLE FLOTANTE A CONVERTIR
Add = 36            'DIRECCION DE MEMORIA EEPROM DEL PIC
GoSub IEEE754       'SUB-RUTINA PARA CONVERCION DE FLOTANTES MICROCHIP A IEEE754
'-------------------------------------------------------------------------------
D0_HI = ERead 100       ' LEEO LOS ENTEROS HOLDIN REGISTRO POR BYTE HRG = WORD 
D0_LO = ERead 101
D1_HI = ERead 102
D1_LO = ERead 103
D2_HI = ERead 104
D2_LO = ERead 105
D3_HI = ERead 106
D3_LO = ERead 107
D4_HI = ERead 108
D4_LO = ERead 109
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
GoSub LECTURA_ADC_VAR   'leo las variables del sistema
 Print At 1,1,"CH0= ",Dec2 CH0," ","CH1= ",Dec2 CH1," ","CH2= ",Dec2 CH2," "
 Print At 2,1,"CH3= ",Dec2 CH3," ","CH4= ",Dec2 CH4
 High COIL1
 DelayMS 1000            'prendo y apago los led del puerto D para probar
 High COIL2              'La comunicacion modbus funciona por Interrupcion
 DelayMS 1000            'Es independiente de lo que ponga o ejecute aca
 High COIL3              'Si, se debe tener en cuenta que el tiempo maximo 
 DelayMS 1000            'preguntando todas las variables por modbus al mismo
 High COIL4              'tiempo se de 600 minisegundos aprpx.  en replicarlas
 DelayMS 1000             
 High COIL5
  Print At 1,1,"CH0= ",Dec2 CH0," ","CH1= ",Dec2 CH1," ","CH2= ",Dec2 CH2," "
 Print At 2,1,"CH3= ",Dec2 CH3," ","CH4= ",Dec2 CH4
 DelayMS 1000
 High COIL6
 DelayMS 1000
 High COIL7
 DelayMS 1000
 High COIL8
 DelayMS 1000
 Low COIL8
  Print At 1,1,"CH0= ",Dec2 CH0," ","CH1= ",Dec2 CH1," ","CH2= ",Dec2 CH2," "
 Print At 2,1,"CH3= ",Dec2 CH3," ","CH4= ",Dec2 CH4
 DelayMS 1000
 Low COIL7
 DelayMS 1000
 Low COIL6
 DelayMS 1000
 Low COIL5
 DelayMS 1000
 Low COIL4
 DelayMS 1000
 Low COIL3
 DelayMS 1000
 Low COIL2
 DelayMS 1000
 Low COIL1
 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
'*******************************************************************************


