/** \file firmware_iRELE485-628.c
 * \brief Este fichero contiene el cuerpo principal del Firmware del\n
 * dispositivo auxiliar satélite de la iACD V1.0.4. denominado iRELE485-628
 *
 * Integra todos los subsistemas y funciones implementados.\n
 * Source para el compilador CCS C v.3.242
 *
 * Microprocesador : <b>16F628A</b> (18 pines PDIP) \n\n
 * Fuses utilizados: \n
 * \li <b>INTRC</b> Internal RC Osc
 * \li <b>MCLR</b> Master Clear pin enabled
 * \li <b>NOWDT</b> No Watch Dog Timer
 * \li <b>NOPROTECT</b> Code not protected from reading
 * \li <b>NOPUT</b> No Power Up Timer
 * \li <b>NOBROWNOUT</b> No brownout reset
 * \li <b>NOLVP</b> No low voltage prgming, B3(PIC16)
 * \li <b>NOCPD</b> No EE protection
 *
 * \author Diego Márquez García-Cuervo \n
 * <http://picmania.garcia-cuervo.net>
 *
 * \version 1.0.0.A
 */

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Definiciones estándar del 16F628A
//
///////////////////////////////////////////////////////////////////////////////////////////////////

#include <16F628A.h>

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Fuses y ajuste de Clock
//
///////////////////////////////////////////////////////////////////////////////////////////////////

#fuses INTRC,MCLR,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOLVP,NOCPD
#use delay(clock=4000000)

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Defines y Constantes
//
///////////////////////////////////////////////////////////////////////////////////////////////////

/*! \def DRIVER_RELE
 *   Pin accionador del Relé número 1 \li HIGH : Relé ON.
 */
#define DRIVER_RELE PIN_B4

/*! \def TX_485_ENABLE
 *  Pin que habilita la transmisión por el canal RS485
 * \li LOW Habilita recepción RS485
 * \li HIGH Habilita transmisión RS485.
 */
#define TX_485_ENABLE PIN_B5

/*! \def bytes_for_USART_buffer
 *  \brief Establece el número de bytes de longitud del buffer de la USART.
 */
#define bytes_for_USART_buffer 12

/*! \var const char VERSION
 *  \brief Versión del Firmware.
 */
const char VERSION[]="1.0.0\0";

/*! \var const char WhatAmI
 *  \brief Identificador de tipo de dispositivo: 'R' Relé.
 */
const char WhatAmI = 'R';

/*! \var const char WhoAmI
 *  \brief Identificador de dispositivo.
 */
const char WhoAmI = '1';

/*! \var const char Command_Execute
 *  \brief Comando que indica ejecución de acción : Activar Relé
 */
const char Command_Execute = 'X';

/*! \var const char end_of_transmit
 *  \brief Carácter que indica fin de transmisión (no buffereable)
 */
const char end_of_transmit = '!';

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Canal de Comunicación
//
///////////////////////////////////////////////////////////////////////////////////////////////////

#use rs232(baud=9600,xmit=PIN_B2,rcv=PIN_B1)

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Prototipos de Funciones
//
///////////////////////////////////////////////////////////////////////////////////////////////////

void USART_activa_tx(void);
void USART_activa_rx(void);
void USART_add_to_buffer(char c);

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// R A M : Variables Globales Estáticas
//
///////////////////////////////////////////////////////////////////////////////////////////////////

/*! \var USART_buffer
 *  \brief Buffer de la USART
 */
static char USART_buffer[bytes_for_USART_buffer];

/*! \var USART_nextRec
 *  \brief Indice del Buffer de la USART
 */
static int8 USART_nextRec;

/** \var Command
  * \brief Comando válido para ejecutar.
  */
int8 Command;

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Funciones
//
///////////////////////////////////////////////////////////////////////////////////////////////////

/** \brief Abre el canal RS485 para transmitir.
  * \return void
  */
void USART_activa_tx(void){
  output_high(TX_485_ENABLE);
  delay_ms(5);
}

/** \brief Abre el canal RS485 para recibir.
  * \return void
  */
void USART_activa_rx(void){
  delay_ms(5);
  output_low(TX_485_ENABLE);
  delay_ms(1);
}

/** \brief Inicializa el Buffer de la USART
  *
  * Recoge el último carácter recibido desde la USART sobre USART_buffer
  * e incrementa en 1 el índice USART_nextRec.\n\n
  * Si el carácter recibido es Retorno de carro '\\r' ó 0x0D comprueba identidad de dispositivo y comando recibido
  *
  * \return void
  */
void USART_add_to_buffer(char c){

   USART_buffer[USART_nextRec++]=c;
   if(USART_nextRec==bytes_for_USART_buffer){
      USART_nextRec=0;
   }

   if(c==end_of_transmit){
      --USART_nextRec;
      if(USART_buffer[USART_nextRec-3]==WhatAmI){
         if(USART_buffer[USART_nextRec-2]==WhoAmI){
           if(USART_buffer[USART_nextRec-1]==Command_Execute){
              Command=0x01;
           }
         }
      }
      USART_nextRec = 0;
   }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Rutinas de Servicio de Interrupciones
//
///////////////////////////////////////////////////////////////////////////////////////////////////

#int_rda
/** \brief Interrupción por : Recepción del Canal Serie.
  *
  */
void interrupt_service_rutine_rda(void) {

   char USART_nextChar;

   USART_nextChar='\0';
   if(kbhit()){
      USART_nextChar=getc();
      USART_add_to_buffer(USART_nextChar);
   }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// M A I N
//
///////////////////////////////////////////////////////////////////////////////////////////////////

/** \brief Constituye el núcleo principal del sistema
  *
  * \return void.
  */
void main(void) {

   setup_oscillator(OSC_4MHZ);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_4);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   port_b_pullups(False);
   set_tris_b(0b00000010);
   output_low(DRIVER_RELE);
   delay_ms(100);

   USART_nextRec = 0;
   Command = 0x00;

   USART_activa_rx();
   delay_ms(333);

   enable_interrupts(global);
   enable_interrupts(int_rda);

   USART_activa_tx();
   printf("[iRELE485-628] v.%s\r\n",version);
   USART_activa_rx();

   do{

      if(Command==0x01){
         Command =0x00;

         USART_activa_tx();
         printf("R%cA%c\r\n",WhoAmI,end_of_transmit);
         USART_activa_rx();

         output_high(DRIVER_RELE);
         delay_ms(2000);
         output_low(DRIVER_RELE);
       }

   } while(true);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// End of firmware
//
///////////////////////////////////////////////////////////////////////////////////////////////////
