//*******************************************************************
//* Temporizador para insoladora, 4 displays 7 segmentos mux
//* *****************************************************************
//* Por Daniel González, Dic 2005 RADON
//********************************************************************

  #include <16F876.h>
  #fuses XT,NOWDT,PUT,NODEBUG, NOPROTECT,NOBROWNOUT,NOLVP
  #use delay (clock=4000000) // 1uS/instrucción

  #include <TONES.C> // Zumbador
  #include <internal_eeprom.c>

  #use fast_io(A)
  #use fast_io(B)
  #use fast_io(C)

  #byte port_a = 5
  #byte port_b = 6
  #byte port_c = 7

  #define Rele_On output_high(PIN_A5)  // Se usará para conmutar la luz
  #define Rele_Off output_low(PIN_A5)

  #define Rele2_On output_high(PIN_B1) // Se usará para conmutar el ventilador
  #define Rele2_Off output_low(PIN_B1)

  #define Disp1_On  output_high(PIN_A0) // Disp izq
  #define Disp1_Off output_low(PIN_A0)
  #define Disp2_On  output_high(PIN_A1)
  #define Disp2_Off output_low(PIN_A1)
  #define Disp3_On  output_high(PIN_A2)
  #define Disp3_Off output_low(PIN_A2)
  #define Disp4_On  output_high(PIN_A3)
  #define Disp4_Off output_low(PIN_A3) // Disp drcha

  #define Puls_Amarillo PIN_B7   //+1
  #define Puls_Blanco PIN_B6     //-1
  #define Puls_Verde PIN_B5      // OK, Aceptar
  #define Puls_Rojo PIN_B4       // Siguiente

  #define antirebotes 50       // para los delays antirebotes en ms


  // Código para los displays 7-segmentos **************************************
  #define cero 0x3F
  #define uno 0x06
  #define dos 0x5B
  #define tres 0x4F
  #define cuatro 0x66
  #define cinco 0x6D
  #define seis 0x7D
  #define siete 0x07
  #define ocho 0x7F
  #define nueve 0x6F

  #define a7 0x77
  #define b7 0x7C
  #define c7 0x39
  #define d7 0x5E
  #define e7 0x79
  #define f7 0x71
  #define g7 0x3D
  #define h7 0x76
  #define i7 0x06
  #define j7 0x1F
  #define l7 0x38
  #define n7 0x54
  #define o7 0x5C
  #define p7 0x73
  #define q7 0x67
  #define r7 0x50
  #define s7 0x6D
  #define t7 0x78
  #define u7 0x3E
  #define punto   0x80
  #define espacio 0x00
  #define fin_mensaje 0xFF
  //****************************************************************************

  int1 fin_cuenta;
  unsigned int16 i;
  unsigned int8 min_x10=0, min=0, seg_x10=0, seg=0;
  unsigned int8 contador=125, display=1, caras=1;


  //*** carácteres y digitos 7-segmentos ***
  // Para el correcto funcionamiento de las librerías, los vectores de mensajes
  // deben acabar por "fin_mensaje" (0xFF)
  int mensaje[35];
  const int numeros[]={cero,uno,dos,tres,cuatro,cinco,seis,siete,ocho,nueve};
  const int bienvenida[]={i7,n7,s7,o7,l7,a7,d7,o7,r7,a7,espacio,p7,o7,r7,espacio,
                              d7,a7,n7,i7,e7,l7,espacio,g7+punto,espacio,espacio,fin_mensaje};
  const int calentar[]={c7,a7,l7,e7,n7,t7,a7,n7,d7,o7+punto,espacio,a7,c7,c7,e7,p7,t7,a7,r7,
                        espacio,p7,a7,r7,a7,espacio,a7,c7,a7,b7,a7,r7+punto,espacio,
                        espacio,fin_mensaje};
  const int una_cara[]={uno,espacio,c7,a7,r7,a7+punto,espacio,espacio,fin_mensaje};
  const int dos_caras[]={dos,espacio,c7,a7,r7,a7,s7+punto,espacio,espacio,fin_mensaje};
  const int msj_listo[]={l7,i7,s7,t7,o7+punto,punto,punto,espacio,espacio,fin_mensaje};
  const int msj_segunda_cara[]={s7,e7,g7,u7,n7,d7,a7,espacio,c7,a7,r7,a7+punto,espacio,espacio,fin_mensaje};

  #INT_RTCC
  void control_rtcc(void)
  {
   set_timer0(131);
   if(contador == 0)
   {
      contador = 125;
      if(seg > 0)
      {
           --seg;
      }
      else
      {
         if(seg_x10 > 0)
         {
            --seg_x10;
            seg = 9;
         }
         else
         {
            if(min > 0)
            {
               --min;
               seg_x10 = 5;
               seg = 9;
            }
            else
            {
               if(min_x10 > 0)
               {
                  --min_x10;
                  min = 9;
                  seg_x10 = 5;
                  seg = 9;
               }
               else
               {
                  fin_cuenta = 1;
               }
            }
         }

      }
   }
  --contador;
  }

  #INT_RB
  void control_rb(void)
  {
   if(!input(Puls_Amarillo))   // +1
   {
      switch(display)
      {
         case 1:  if(min_x10 < 9) ++min_x10;
                  else min_x10 = 0;
                  break;
         case 2:  if(min < 9) ++min;
                  else min = 0;
                  break;
         case 3:  if(seg_x10 < 5) ++seg_x10;
                  else seg_x10 = 0;
                  break;
         case 4:  if(seg < 9) ++seg;
                  else seg = 0;
                  break;
      }
      while(!input(Puls_Amarillo));
      delay_ms(antirebotes);
   }
   if(!input(Puls_Blanco))  // -1
   {
      switch(display)
      {
         case 1:  if(min_x10 > 0) --min_x10;
                  else min_x10 = 9;
                  break;
         case 2:  if(min > 0) --min;
                  else min = 9;
                  break;
         case 3:  if(seg_x10 > 0) --seg_x10;
                  else seg_x10 = 5;
                  break;
         case 4:  if(seg > 0) --seg;
                  else seg = 9;
                  break;
      }
      while(!input(Puls_Blanco));
      delay_ms(antirebotes);
   }
   if(!input(Puls_Verde))
   {
      if(display < 4) ++display;
      else display = 1;
      while(!input(Puls_Verde))
      delay_ms(antirebotes);
   }
  }

  void alarma(void)
  {
   int1 sw=0;
   while(1)
   {
         generate_tone(3000,100);
         if(!input(Puls_Verde) || !input(Puls_Rojo))
         {
            while(!input(Puls_Verde) || !input(Puls_Rojo))
            delay_ms(antirebotes);
            return;
         }
         generate_tone(4000,100);
         if(!input(Puls_Verde) || !input(Puls_Rojo))
         {
            while(!input(Puls_Verde) || !input(Puls_Rojo))
            delay_ms(antirebotes);
            return;
         }
         delay_ms(100);
         if(!input(Puls_Verde) || !input(Puls_Rojo))
         {
            while(!input(Puls_Verde) || !input(Puls_Rojo))
            delay_ms(antirebotes);
            return;
         }
   }
  }

  void pinta_disp1(int x)
  {
   port_c = x;
   Disp1_On;
   delay_ms(2);
   Disp1_Off;
  }

  void pinta_disp2(int x)
  {
   port_c = x;
   Disp2_On;
   delay_ms(2);
   Disp2_Off;
  }

  void pinta_disp3(int x)
  {
   port_c = x;
   Disp3_On;
   delay_ms(2);
   Disp3_Off;
  }

  void pinta_disp4(int x)
  {
   port_c = x;
   Disp4_On;
   delay_ms(2);
   Disp4_Off;
  }

  // Esta función rota un mensaje en los displays 7 segmentos, el último
  // elemento del array deberá ser 0xFF (siendo estos los valores para los displays),
  // acepta:
  //  el 1º un entero, como retardo de la rotacion (recomendado 35)
  //  2º si puede devolver el boton amarillo
  //  3º si puede devolver el boton blanco
  //  4º si puede devolver el boton ver
  //  y 5º si puede devolver el boton rojo
  // y retorna cuando se presiona un pulsador:
  // "0" amarillo, "1" blanco, "2" verde y "3" rojo
  int rotar_mensaje(int retardo, int1 amarillo, int1 blanco, int1 verde, int1 rojo)
  {
   //int1 sw_rotar_mensaje=1;
   unsigned int8 no_caracteres=0;
   unsigned int8 letra1=0, letra2=1, letra3=2, letra4=3; //4 displays, 4 letras mux simultaneamente
      while(mensaje[no_caracteres] != 0xFF)
      {
       ++no_caracteres; // cuenta el nº de carácteres a rotar
      }
      while(1)
      {
         for(i=0;i<=retardo;++i)
         {
            pinta_disp1(mensaje[letra1]);
            pinta_disp2(mensaje[letra2]);
            pinta_disp3(mensaje[letra3]);
            pinta_disp4(mensaje[letra4]);
            if(!input(Puls_Amarillo) || !input(Puls_Blanco) || !input(Puls_Verde) || !input(Puls_Rojo)) // si pulso algo
            {
               if(!input(Puls_Amarillo) && amarillo == 1)
               {
                  while(!input(Puls_Amarillo) && blanco == 1); // espera que suelte el pulsador
                  delay_ms(antirebotes);         // antirebote
                  return 0;
               }
               if(!input(Puls_Blanco) && blanco == 1)
               {
                  while(!input(Puls_Blanco));
                  delay_ms(antirebotes);
                  return 1;
               }
               if(!input(Puls_Verde) && verde == 1)
               {
                  while(!input(Puls_Verde));
                  delay_ms(antirebotes);
                  return 2;
               }
               if(!input(Puls_Rojo) && rojo == 1)
               {
                  while(!input(Puls_Rojo));
                  delay_ms(antirebotes);
                  return 3;
               }
            }
         }
         if(letra1 < no_caracteres-1) ++letra1; // (-1) en el array el "0" es el primer elemento
         else letra1 = 0;
         if(letra2 < no_caracteres-1) ++letra2;
         else letra2 = 0;
         if(letra3 < no_caracteres-1) ++letra3;
         else letra3 = 0;
         if(letra4 < no_caracteres-1) ++letra4;
         else letra4 = 0;
      }
  }

  void mensaje_inicio(void)
  {
   unsigned int no_caracteres = 0;
   while(bienvenida[no_caracteres] != 0xFF)
   {
      ++no_caracteres; // cuenta el nº de carácteres del array "una_cara"
   }
   for(i=0;i<=no_caracteres;++i)   // Copiamos el array "una_cara" a "mensaje"
                                   // 1er elemento [0] --> copiamos inclusive 0xFF (marca final)
   {
      mensaje[i] = bienvenida[i];
   }
   rotar_mensaje(35,0,0,0,1);      // Solo acaba al pulsar rojo (GO)
  }

  void calentar_bombillas(void)
  {
   unsigned int no_caracteres = 0;
   Rele_On;
   while(calentar[no_caracteres] != 0xFF)
   {
      ++no_caracteres; // cuenta el nº de carácteres del array "calentar"
   }
   for(i=0;i<=no_caracteres;++i)   // Copiamos el array "calentar" a "mensaje"
                                   // 1er elemento [0] --> copiamos inclusive 0xFF (marca final)
   {
      mensaje[i] = calentar[i];
   }
   rotar_mensaje(35,0,0,1,0);
   Rele_Off;
  }

  void selec_tiempo(void)
  {
   int1 sw = 0;
   if(read_eeprom(0) >=0 && read_eeprom(0) <= 9) min_x10 = read_eeprom(0); //comprueba la ultima temporizacion
   if(read_eeprom(1) >=0 && read_eeprom(1) <= 9) min = read_eeprom(1);
   if(read_eeprom(2) >=0 && read_eeprom(2) <= 5) seg_x10 = read_eeprom(2);
   if(read_eeprom(3) >=0 && read_eeprom(3) <= 9) seg = read_eeprom(3);
   enable_interrupts(INT_RB);
   do
   {
      for(i=0; i<20;++i)
      {
         if(display == 1 && sw == 0) pinta_disp1(0);
         else pinta_disp1(numeros[min_x10]);
         if(display == 2 && sw == 0) pinta_disp2(punto);
         else pinta_disp2(numeros[min]+punto);
         if(display == 3 && sw == 0) pinta_disp3(0);
         else pinta_disp3(numeros[seg_x10]);
         if(display == 4 && sw == 0) pinta_disp4(0);
         else pinta_disp4(numeros[seg]);
         if(!input(Puls_Rojo)) break;     // si pulsa rojo, sale sin esperar a terminar el for
      }
      if(sw == 1) sw = 0;
      else sw = 1;
   } while(input(Puls_Rojo));
   while(!input(Puls_Rojo));        // espera a que suelte
   delay_ms(antirebotes);           //antirebotes
   disable_interrupts(INT_RB);
   write_eeprom(0,min_x10);         //guarda la temporización en la eeprom
   write_eeprom(1,min);             //para recuperarla la próxima vez
   write_eeprom(2,seg_x10);
   write_eeprom(3,seg);
  }

  void selec_caras(void)
  {
   int8 pulsador = 0;
   unsigned int no_caracteres = 0;
   do
   {
      if(caras == 1)
      {
         while(una_cara[no_caracteres] != 0xFF)
         {
         ++no_caracteres; // cuenta el nº de carácteres del array "una_cara"
         }
         for(i=0;i<=no_caracteres;++i)   // Copiamos el array "una_cara" a "mensaje"
                                         // 1er elemento [0] --> copiamos inclusive 0xFF (marca final)
         {
            mensaje[i] = una_cara[i];
         }
      }
      if(caras == 2)
      {
         while(dos_caras[no_caracteres] != 0xFF)
         {
            ++no_caracteres; // cuenta el nº de carácteres del array "dos_caras"
         }
         for(i=0;i<=no_caracteres;++i) // Copiamos el array "dos_caras" a "mensaje"
                                       // 1er elemento [0] --> copiamos inclusive 0xFF (marca final)
         {
            mensaje[i] = dos_caras[i];
         }
      }
      pulsador = rotar_mensaje(35,1,1,1,0);
      if(pulsador == 0 || pulsador == 1)
      {
         if(caras == 1) caras = 2;
         else caras = 1;
      }
   } while(pulsador != 2 && pulsador != 3);  // Al pulsar verde o rojo  y devolver un 3 o 4 acepta el nº de caras
  }

  void listo(void)
  {
   unsigned int no_caracteres = 0;
   while(msj_listo[no_caracteres] != 0xFF)
   {
      ++no_caracteres; // cuenta el nº de carácteres del array "msj_listo"
   }
   for(i=0;i<=no_caracteres;++i)   // Copiamos el array "msj_listo" a "mensaje"
                                   // 1er elemento [0] --> copiamos inclusive 0xFF (marca final)
   {
      mensaje[i] = msj_listo[i];
   }
   rotar_mensaje(35,0,0,1,0);
  }

  void segunda_cara(void)
  {
   unsigned int no_caracteres = 0;
   while(msj_segunda_cara[no_caracteres] != 0xFF)
   {
      ++no_caracteres; // cuenta el nº de carácteres del array "msj_segunda_cara"
   }
   for(i=0;i<=no_caracteres;++i)   // Copiamos el array "msj_segunda_cara" a "mensaje"
                                   // 1er elemento [0] --> copiamos inclusive 0xFF (marca final)
   {
      mensaje[i] = msj_segunda_cara[i];
   }
   rotar_mensaje(35,0,0,1,0);
  }

  void cuenta(void)
  {
   fin_cuenta = 0;
   min_x10 = read_eeprom(0);
   min = read_eeprom(1);
   seg_x10 = read_eeprom(2);
   seg = read_eeprom(3);
   Rele_On;
   set_rtcc(131);
   enable_interrupts(INT_RTCC);
   do
   {
      if(min_x10 != 0) pinta_disp1(numeros[min_x10]);
      if(min_x10 == 0  && min == 0) pinta_disp2(punto);
      else pinta_disp2(numeros[min]+punto);
      pinta_disp3(numeros[seg_x10]);
      pinta_disp4(numeros[seg]);
   } while(fin_cuenta == 0);
   disable_interrupts(INT_RTCC);
   Rele_Off;
  }

  void main(void)
  {
   set_tris_a(0x00); // Puerto A todo salidas
   port_b_pullups(TRUE); // Resistencias de polarización
   set_tris_b(0xFC); // Todo entradas excepto RB0 (zumbador) y RB1 (Rele2)
   set_tris_c(0x00); // Puerto C todo salidas

   port_a=0;
   port_b=0;
   port_c=0;

   setup_counters(RTCC_INTERNAL, RTCC_DIV_64);
   setup_timer_2(T2_DIV_BY_16, 0xFF, 16);
   enable_interrupts(GLOBAL);

   mensaje_inicio();
   Rele2_On;
   calentar_bombillas();
   selec_tiempo();
   selec_caras();
   listo();
   cuenta();
   alarma();
   if(caras == 2)
   {
      segunda_cara();
      cuenta();
      alarma();
   }
   Rele2_Off;
   reset_cpu();
  }

