////////////////////////////////////////////////////////////////////////////////
//
// Título:  ad_temperatura_lm35a_232_INT_ADC_Display LCD_CON Buffer_Versión 3.0 
//
// (c)ad_temperatura_lm35a_232_ reformado  19.11.07 by umalrovi
//
// Ubicación: C:\Archivos de programa\PICC\Projects\
//
// Procesador: PIC16F877A con cristal de 20MHz             
//
// Software: CS PCM Version 4.059
//
// Propósito:  Se quiere visualizar el valor de un sensor de temperatura y de
//          luminosidad en modo local por medio de un display LCD y en modo 
//          remoto por un PC, mediante el puerto de comunicaciones rs232.
//
// El PC enviará uno de los siguientes comandos descritos mas abajo y el PIC
// hará la acción oportuna.            
//
// Técnica Empleada: 
//
//    * CAD por interrupción.
//    * Interrupción serie.
//    * Display LCD utilizando librería flex_lcd_2.
//    * Comandos RS232 con Buffer
//    * Guarda el estado del calentador en EEPROM
//
//  COMANDOS rs232 (RC= retorno de carro):
//
//    "C1_ON+RC"   ----------  Pone el calentador en marcha
//    "C1_OFF+RC"  ----------  Apaga el calentador
//    "STDO+RC"    ----------  Mira el estado del calentador (On/Off)
//    "V_CAD+RC"   ----------  Valor del CAD
//    "BORRAR_E+RC"----------  Borra las variables guardadas en eeprom
//    "VERSION+RC" ---------- Muestra la versión del programa
//    "RESET+RC"   ---------- Resetea el micro por watchdog
//
//
////////////////////////////////////////////////////////////////////////////////
#include <16f877A.h>       // Definiciones del PIC 16F877A
#device  adc=10         // Resolución 10 BITS
#device ICD=TRUE        // Habilitamos el modo IDC
#fuses HS,WDT,NOPROTECT,NOLVP,PUT,BROWNOUT
#use delay(clock=20000000) // Oscilador a 20 Mhz y limpia en delay

//##############################################################################
// El compilador realiza las funciones de IO sin programar el registro dirección
// ocupa menos memoria de programa en el PIC.
//##############################################################################

#use standard_io(a)
#use standard_io(b)
#use standard_io(c)
#use standard_io(d)
#use standard_io(e)

#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7,ERRORS)
// parity=N (no paridad), parity=E(Paridad par), parity=O (paridad impar)
// RS232 Estándar xmit salida de datos, rcv entrada de datos


////////////////////////////////////////////////////////////////////////////////
//                             DEFINE                                         //
////////////////////////////////////////////////////////////////////////////////


#define LCD_DB4 PIN_D4
#define LCD_DB5 PIN_D5
#define LCD_DB6 PIN_D6
#define LCD_DB7 PIN_D7

// si solo va a utilizar escritura en el LCD conecta R/W a GND y
// comente la siguiente línea

#define USE_LCD_RW 1

// definir a continuación qué pines del PIC manejan las señales de control

#define LCD_RS PIN_D1
#define LCD_RW PIN_D2
#define LCD_E PIN_D0

#include "flex_lcd_2.c"      //libreria manejo lcd editada por REDPIC


#define  calentador_OFF   output_low(PIN_B1)
#define  calentador_ON    output_high(PIN_B1)


////////////////////////////////////////////////////////////////////////////////
//                            CONSTANTES                                      //
////////////////////////////////////////////////////////////////////////////////

int const lenbuff=32;      // Longitud de buffer, Ajustar  a lo que desees
                          //  o te sea posible)

////////////////////////////////////////////////////////////////////////////////
//                            VARIABLES EN RAM                                //
////////////////////////////////////////////////////////////////////////////////
float  adc_luminosidad=0x00;
float  adc_temperatura=0x00;
float  grados_temperatura=0;
int   canal=0;
char grad=0b11011111;                      // simblo "º" según datasheet

int  xbuff=0x00;                         // Índice: siguiente char en cbuff
char cbuff[lenbuff];                    // Buffer
char rcvchar=0x00;                     // último caracter recibido
int1 flagcommand=0;                   // Flag para indicar comando disponible
int1 estado_calentador=0;            // Estado del Calentador
int1 flag_reset=0;                  // Indica un reset por consola(PC)


////////////////////////////////////////////////////////////////////////////////
//                     DECLARACIÓN DE FUNCIONES                               //
////////////////////////////////////////////////////////////////////////////////

void toma_adc_y_transmite(void); // pilla el valor de ADC y lo envia por RS232
void visualiza_parametros_LCD(void);//muestra los valores de temp. y luminosidad
void inicbuff(void);                    // Borra buffer
int  addcbuff(char c);                 // añade caracter recibido al buffer
void procesa_comando(void);           // Procesa comando


//##############################################################################
//#
//#                  Interrupción recepcición serie USART
//#
//##############################################################################

#int_rda
void serial_isr() {                    // Interrupción recepción serie USART

   rcvchar=0x00;                       // Inicializo caracter recibido
   if(kbhit()){                        // Si hay algo pendiente de recibir ...
      rcvchar=getc();                  // lo descargo y ...
      addcbuff(rcvchar);               // lo añado al buffer y ...
   }
}

//##############################################################################
//#
//#              Interrupción encargada del conversor AD
//#
//##############################################################################

# INT_AD
void AD_isr()
{
   if (canal==0){
      set_adc_channel(0);
      canal=1;
      delay_ms(1);
      adc_luminosidad=Read_ADC(ADC_READ_ONLY);
      delay_ms(1);
      Read_ADC(ADC_START_ONLY);  //inicia nueva conversion.
   }else{
      set_adc_channel(1);
      canal=0;
      delay_ms(1);
      adc_temperatura=Read_ADC(ADC_READ_ONLY);
      delay_ms(1);
      Read_ADC(ADC_START_ONLY);   //inicia nueva conversion.
   }   
}

////////////////////////////////////////////////////////////////////////////////
//                       DESARROLO DE FUNCIONES                               //
////////////////////////////////////////////////////////////////////////////////

void toma_adc_y_transmite(void){
   
   disable_interrupts(int_ad);
   
   grados_temperatura = (float) ((adc_temperatura * 391) / 1000);

   printf(" L = %.0f T = %.2f (adc= %.0f)\n",adc_luminosidad,grados_temperatura,adc_temperatura);

   enable_interrupts(int_AD);  // Habilita las interrupciones globales
   
// Inicializa el CAD y cuando termine salta a vector interrupción
   
   Read_ADC(ADC_START_ONLY);
   

}

void visualiza_parametros_LCD(void){
   
// Desabilitamos la int del CAD para pillar el valor y guardarlo en su variable
// y posteriormente hacer calculos.

  disable_interrupts(int_ad);
  
  grados_temperatura =(float)((adc_temperatura * 391) / 1000); 
  
  lcd_gotoxy(1,1);
  printf(lcd_putc,"T=%.1f%cC ad=%.0f      "grados_temperatura,grad,adc_temperatura);
// para expresar en el LCD el simbolo de grados hay que poner ß (alt+225) que
//corresponden al caracter decimal 223
  lcd_gotoxy(7,2);
  printf(lcd_putc,"Ladc=%.0f    "adc_luminosidad);
  if (estado_calentador==0){
      lcd_gotoxy(1,2);
      printf(lcd_putc,"C=OFF ");
  }else{
      lcd_gotoxy(1,2);
      printf(lcd_putc,"C=ON ");
  }
// Volvemos a Habilitar las interrupciones del CAD
  enable_interrupts(int_ad);
}


void inicbuff(void){                   // Inicia a \0 cbuff -------------------
   int i=0;

   for(i=0;i<lenbuff;i++){             // Bucle que pone a 0 todos los
      cbuff[i]=0x00;                   // caracteres en el buffer
   }
   xbuff=0x00;                         // Inicializo el indice de siguiente
                                       // caracter
}

int addcbuff(char c){                  // Añade a cbuff -----------------------

      switch(c){
         case 0x0D:                    // Enter -> Habilita Flag para procesar
            flagcommand=1;             // Comando en Main
            break;
         case 0x08:                 // Del   -> Borra último caracter del Buffer
            if(xbuff>0) cbuff[--xbuff]=0x00;
            break;
         case 0x01B:                   // Esc   -> Borra el Buffer completamente
            inicbuff();
            break;
         default:
            cbuff[xbuff++]=c;          // Añade caracter recibido al Buffer
      }
}

void procesa_comando(void){

   int i=0;
   int1 flag=0;                        // Indica si el comando es valido
   char arg[lenbuff];                   // Argumento de comando (si lo tiene)

   flagcommand=0;                       // Desactivo flag de comando pendiente.

   for(i=0;i<lenbuff;i++){             // Bucle que pone a 0 todos los
      arg[i]=0x00;                     // caracteres en el argumento
   }

   if(cbuff[0]=='C'&&cbuff[1]=='1'&&cbuff[2]=='_'&&cbuff[3]=='O'&&cbuff[4]=='N'&&cbuff[5]==0x00){// Comparo inicio del buffer con comando "A1"

      flag=1;          // Activamos el flag para detectar comando valido
      calentador_ON;  // activamos puerta RB1 --> Calentador ON
      disable_interrupts(int_ad);
      printf("CALENTADOR --> Activado"); // mostramos stado calentador por RS232
      enable_interrupts(int_AD);  // Habilita las interrupciones globales
      estado_calentador=1;             // variable estado_calentador=ON
      write_eeprom(0,estado_calentador);  // Escribe en la primera posición de la eeprom el estado del calentador
   }
   if(cbuff[0]=='C'&&cbuff[1]=='1'&&cbuff[2]=='_'&&cbuff[3]=='O'&&cbuff[4]=='F'&&cbuff[5]=='F'&&cbuff[6]==0x00){

      flag=1;      // Activamos el flag para detectar comando valido
      calentador_OFF;   // desactivamos puerta RB1--> calentador OFF
      disable_interrupts(int_ad);
      printf("CALENTADOR --> Desactivado");   // Mostramos stdo calentador RS232
      enable_interrupts(int_AD);  // Habilita las interrupciones globales
      estado_calentador=0;                       // Variable calentador=OFF
      write_eeprom(0,estado_calentador);   // Escribe en la primera posición de la eeprom el estado del calentador
   }
   if(cbuff[0]=='S'&&cbuff[1]=='T'&&cbuff[2]=='D'&&cbuff[3]=='O'&&cbuff[4]==0x00){

      flag=1;            // Activamos el flag para detectar comando valido
      disable_interrupts(int_ad);
      estado_calentador=read_eeprom(0);   // Leemos en la primera posicion de la EEPROM el estado del calentador    
      if (estado_calentador==1)   printf("CALENTADOR --> ESTADO ON");      // Escribimos por pantalla el estado del calentador
         else  printf("CALENTADOR --> ESTADO OFF");
      enable_interrupts(int_AD);  // Habilita las interrupciones globales
   }
   if(cbuff[0]=='V'&&cbuff[1]=='_'&&cbuff[2]=='C'&&cbuff[3]=='A'&&cbuff[4]=='D'&&cbuff[6]==0x00){

      flag=1;      // Activamos el flag para detectar comando valido
      toma_adc_y_transmite();
   }       
   if(cbuff[0]=='B'&&cbuff[1]=='O'&&cbuff[2]=='R'&&cbuff[3]=='R'&&cbuff[4]=='A'&&cbuff[5]=='R'&&cbuff[6]=='_' &&cbuff[7]=='E'&&cbuff[8]==0x00){

      flag=1;       // Activamos el flag para detectar comando valido
      calentador_OFF;    // desactivamos puerta RB1-->calentador OFF
      write_eeprom(0,0);     // Inicializamos a 0 las dos posiciones utilizadas
      write_eeprom(1,0);
      disable_interrupts(int_ad);
      printf("EEPROM BORRADA");
      enable_interrupts(int_AD);  // Habilita las interrupciones globales
   }   
   if(cbuff[0]=='R'&&cbuff[1]=='E'&&cbuff[2]=='S'&&cbuff[3]=='E'&&cbuff[4]=='T'&&cbuff[5]==0x00){

      write_eeprom(0,estado_calentador);   // Guardamos en la EEPROM el estado del calentador
      flag_reset=1;                    // Activamos el flag de reset por consola
      write_eeprom(1,flag_reset);      // Guardamos el valor en la EEPROM
      disable_interrupts(int_ad);
      printf("RESET");
      enable_interrupts(int_AD);  // Habilita las interrupciones globales
     // Bucle infinito para que el Watchdog resetee mi chiporro por Azicuetano
      do{
      
      }while(true);
   }
   if(cbuff[0]=='V'&&cbuff[1]=='E'&&cbuff[2]=='R'&&cbuff[3]=='S'&&cbuff[4]=='I'&&cbuff[5]=='O'&&cbuff[6]=='N'&&cbuff[8]==0x00){

      flag=1;   // Activamos el flag para detectar comando valido
      disable_interrupts(int_ad);
      printf("V3.5"); // Muestra por la RS232 lña versión del programa.
      // lo utilizo mucho por que en Labview cada x tiempo mando el comando "VERSION"
      // compruebo la comunicación si esta OK.
      enable_interrupts(int_AD);  // Habilita las interrupciones globales
   }   
   if (flag==1){         // Si flag==1 es que se ha metido en algún if
      flag=0;            // pone a cero el flag
     }
     else{
         disable_interrupts(int_ad);
         printf("Comando no reconocido");  // Comando no reconocido
         enable_interrupts(int_AD);  // Habilita las interrupciones globales
     }
// Una vez procesado el comando borrams por buffer de la RS232

   inicbuff();     // Borro buffer.
}

////////////////////////////////////////////////////////////////////////////////
//                  PROGRAMA PRINCIPAL VOID MAIN()                            //
////////////////////////////////////////////////////////////////////////////////

void main() {

   port_b_pullups(FALSE);   // Resistencias de Pullup desactivadas
   set_tris_B(0b0000001);   // Configuramos entradas y salidas. RB1:7-->Salida ; RB0--> Entrada
   
   setup_adc(ADC_CLOCK_DIV_32);
   setup_adc_ports(AN0_AN1_AN3);
   
  
   flag_reset=read_eeprom(1);    //leemos la variable reset en eeprom
   if (flag_reset==1){
      printf("reset por consola");
   }
   
   calentador_OFF;

   inicbuff();          // Borra buffer al inicio

   enable_interrupts(int_rda);
   enable_interrupts(int_ad);
   
   setup_wdt(WDT_18MS);  // Configuramos el Watchdog para que salte a los 18ms y el program esté continuamente resetandose para saber si fuciona correctamente.
   RESTART_WDT();        // Incia o watch-dog timer
   
   enable_interrupts(global);

   canal=0;
   Read_ADC(ADC_START_ONLY); // Inicia el CAD por int.

   printf("\n\ AD - LM35a - Monitor\n\n"); // Presenta menu
   
   lcd_init();
   lcd_setcursor(0,0);
   lcd_gotoxy(1,1);
   printf(lcd_putc,"CAD Temp. LM35a \n  foro todopic"); //muestra por la LCD
   delay_ms(3000);
   lcd_gotoxy(1,1);
   printf(lcd_putc,"  V2.0 umalrovi \n  foro todopic"); //muestra por la LCD
   delay_ms(3000);
   
   do {  
      visualiza_parametros_LCD();     
      if (flagcommand==1){     
         procesa_comando();
      }
   } while (TRUE);
}

