//Ésta es una práctica.
#include <16f877a.h>
#fuses XT, NOWDT, PUT, NODEBUG, NOBROWNOUT, NOPROTECT, NOLVP, NOWRT   //Fusibles
#device adc = 10   //ADC
#use delay (clock = 4000000)
#use rs232(BAUD=9600, BITS=8, PARITY=N, STOP=1, XMIT=PIN_C6, RCV=PIN_C7)
#use fast_io(A)
#use fast_io(C)
#define bufferSize 21

//Temporizadores
int8 tempEnv=0; //Cadencia de envío.
int8 tempSens=0; //Frecuencia de actualización de los sensores.
int8 segs=0; //Cuenta los segundos que van pasando.

//Trama de entrada
char buffer[bufferSize];
int8 i=0;
int8 auxCrc;

//Datos de los sensores
/* Resoluciones de bits:
   * 8 bits: 1.95312
   * 9 bits: 0.97656
   * 10 bits: 0.48875
*/
float offSens1=0.0, offSens2=0.0, bitRes1=1024, bitRes2=1024;
int cadEnv1=3, cadEnv2=4;

//Análisis del búfer
int1 found=false, header=false, read = false, hFound = false;
int8 intVal, decVal, inCrc, sign, cmd, start=0, rBytes;

//Trama de salida
int1 sendData=false;
int8 crc;
char frame[7];

//Sensores
int1 readSens=false;
float Temp1, Temp2;
int simBitRes1=0, simBitRes2=0, analogVal=0;

//INTERRUPCIÓN TIMER0.
#int_rtcc
void Timer0_isr()
{
//Incremento de los contadores cada desbordamiento.
   tempEnv++;
   tempSens++;
//Al llegar a 250 desbordamientos (1s), se activa la bandera de envío de datos. Se incrementan los segundos que han pasado.
   if (tempEnv == 250){
      segs++;         //Incremento de los segundos
      sendData=true;  //Activación de la bandera para envío de datos.
      tempEnv=0;      //Se reinicia el contador de la cadencia de Envío.
   }
//Al llegar a 83 desbordamientos (0.3s), se activa la bandera de lectura de los sensores. Se reinicia el contador.
   if (tempSens == 83){
      readSens=true;   //Activación de bandera de lectura.
      tempSens=0;      //Reinicio del contador.
   }
}

//INTERRUPCIÓN RS-232.
#INT_RDA
void RS232_isr()
{
   while(kbhit()){
      rBytes=getc();    //Se recibe el byte
      if(rBytes == '<') //Si se encuentra el header...
         hFound=true;   //se marca como encontrado
      else  //Si no...
      {
         if(rBytes == '>') //Si es el trailer...
         {
            hFound=false;  //Se desactiva la bandera del header, y termina la escritura en el búfer
            buffer[i] = rBytes;  //Se escribe el trailer al búfer
            i++;           //Se incrementa el índice
            read = true;   //La trama está completa y lista para ser analizada
         }
      }
      if(hFound)  //Si el header está como encontrado...
      {
         buffer[i] = rBytes;  //Se escribe en el búfer
         i++;                 //Se incrementa el índice
      }
      if(i > (bufferSize - 1))   //Al desbordarse el índice...
         i = 0;                  //se reinicia
   }
}

//LECTURA DE SENSORES ANALÓGICOS.
void ReadSensData()
{
//Lectura del primer sensor (PIN_A0).
   set_adc_channel(0);   //Se establece el canal en 0 (Número del PIN)
   delay_ms(3); //3ms para estabilización de la señal
   analogVal = read_adc() >> simBitRes1;   //Lee los datos del sensor
   Temp1=(100*(5.0*analogVal/bitRes1))+offSens1;    //Cálculo de los ºC.

//Lectura del segundo sensor (PIN_A1).
   set_adc_channel(1);   //Se establece el canal en 1 (Número del PIN)
   delay_ms(3); //3ms para estabilización de la señal.
   analogVal = read_adc() >> simBitRes2;   //Lee los datos del sensor
   Temp2=(100*(5.0*analogVal/bitRes2))+offSens2;    //Cálculo de los ºC.
}

//CREA LA TRAMA Y LA ENVIA.
void MakeSendFrame(int Sensor)
{
   int8 k=0, aux;
//De acuerdo al sensor que enviará los datos, se crea su trama.
   switch(Sensor){
//Para Sensor1.
      case 1:
         frame[0] = '<';   //Header
         frame[1] = 0x10;  //Sensor que envía los datos.
//Dependiendo de si la lectura es negativa o positiva, se determina el signo.
         if(Temp1 < 0){
            frame[2] = '-';  //Signo
            Temp1 *= -1;     //Se enviará valor absoluto
         }
         else
            frame[2] = '+';  //Signo
         frame[3] = (int)Temp1;  //Número entero
         aux = Temp1*10;
         frame[4] = aux - ((int)Temp1 * 10); //Número decimal
//Se obtiene el CRC.
         crc = 255 - frame[1] - frame[2] - frame[3] - frame[4];
         frame[5] = crc;   //CRC
         frame[6] = '>';   //Trailer
         break;
//Para Sensor2.
      case 2:
         frame[0] = '<';   //Header
         frame[1] = 0x20;  //Sensor que envía los datos.
//Dependiendo de si la lectura es negativa o positiva, se determina el signo.
         if(Temp2 < 0){
            frame[2] = '-';  //Signo
            Temp2 *= -1;     //Se enviará valor absoluto
         }
         else
            frame[2] = '+';  //Signo
         frame[3] = (int)Temp2;  //Número entero
         aux = Temp2*10;
         frame[4] = aux - ((int)Temp2 * 10); //Número decimal
//Se obtiene el CRC.
         crc = 255 - frame[1] - frame[2] - frame[3] - frame[4];
         frame[5] = crc;   //CRC
         frame[6] = '>';   //Trailer
         break;
   }
   for (k=0; k<7; k++)  //La variable 'k' marca el índice.
   putc(frame[k]);   //Se envían Byte por Byte los datos de la trama   
}

//Lectura de los datos en el búfer.
void ReadBufferData(){
//Mientras no se encuentre la trama...
read = false;
   while(!header){
      if(buffer[start] == '<'){
         if(start+6 < bufferSize){
            if(buffer[start+6] == '>'){
               buffer[start] = 'Z';
               cmd = buffer[start+1];
               sign = buffer[start+2];
               intVal = buffer[start+3];
               decVal = buffer[start+4];
               inCrc = buffer[start+5];
               header=true;
               found = true;
               start+=7;
            }
            else
               header=true;
         }
      }
      else
         start++;
      if (start >= bufferSize){
         start=0;
         header=true;
      }
   }
   header=false;   
}

//Analizar trama
void ExecuteCommand()
{
//Si el CRC coincide...
   auxCrc = 255 - cmd - sign - intVal - decVal;
   if (found){
      found = false;
      if(auxCrc==inCrc){
//Se analiza el comando que entró...
         switch(cmd){
//Comandos de Sensor1.
            case 0x11:   //Resolución de bits
               if(intVal==8){
                  bitRes1=256;
                  simBitRes1=2;
               }
               else if(intVal==9){
                  bitRes1=512;
                  simBitRes1=1;
               }
               else if(intVal==10){
                  bitRes1=1024;
                  simBitRes1=0;
               }
               else
                  putc(0xFF);
            break;
            case 0x12:
               if (intVal >= 1 && intVal <= 10)
                  cadEnv1 = intVal; //Cadencia de envío
               else
                  putc(0xFF);
            break;
            case 0x14:
               //Offset
               if (intVal >= 0 && intVal <= 2 && decVal >= 0 && decVal <= 9){
                  offSens1 = intVal;
                  offSens1+= (float) decVal/10.0;
                  if (sign == '-')
                     offSens1*=-1;
               }
               else
                  putc(0xFF);
            break;
//Comandos de Sensor2.
            case 0x21:         //Resolución de bits
               if(intVal==8){
                  bitRes2=256;
                  simBitRes2=2;
               }
               else if(intVal==9){
                  bitRes2=512;
                  simBitRes2=1;
               }
               else if(intVal==10){
                  bitRes2=1024;
                  simBitRes2=0;
               }
               else
                  putc(0xFF);
            break;
            case 0x22:
               if (intVal >= 1 && intVal <= 10)
                  cadEnv2 = intVal; //Cadencia de envío
               else
                  putc(0xFF);
            break;
            case 0x24:
               //Offset
               if (intVal >= 0 && intVal <= 2 && decVal >= 0 && decVal <= 9){
                  offSens2 = intVal;
                  offSens2+= (float) decVal/10.0;
                  if (sign == '-')
                     offSens2*=-1;
               }
               else
                  putc(0xFF);
            break;
         }
     }
   }
}

//Programa principal
void main ()
{
//Configuración de Timer0.
// Prescaler de 16. Desbordamiento a 4.0ms. 250 desbordamientos necesarios para 1s.
   setup_timer_0(rtcc_internal|rtcc_div_16);
//Configuración del adc.
   setup_adc(adc_clock_internal); //Reloj interno usado para conversión
   setup_adc_ports(AN0_AN1_AN3);  //Puertos analógicos A0, A1, A3
//Habilitación de interrupciones.
   enable_interrupts(int_rtcc);   //Interrupción Timer0
   enable_interrupts(int_rda);   //Interrupción RS232
   enable_interrupts(global);
//Configuración de puertos.
   set_tris_a(0x0B);              //Se configuran como entrada los puertos analógicos
   set_tris_c(0xC0);              //Se configura como entrada el puerto de recepción del rs232

   while(true){
//Al recibir un bloque de trama completo, se activa una bandera para su análisis.
      if ((i-1)%7==0){
         //No sé porqué, pero funciona.
         ReadBufferData();
         ExecuteCommand();
      }
//Cuando la bandera de lectura de sensores está activa...
      if(readSens){
         readSens=false; //Se reinicia la bandera
         ReadSensData(); //Se leen los datos de los sensores.
      }
//Cuando se cumpla el tiempo de envío de datos del Sensor1, se crea la trama y se envía.
      if (sendData){
         sendData=false;
         if (segs % cadEnv1 == 0){
            MakeSendFrame(1);
         }
//Cuando se cumpla el tiempo de envío de datos del Sensor2, se crea la trama y se envía.
         if (segs % cadEnv2 == 0){
            MakeSendFrame(2);
         }
      }
   }
}
