/* v1.0
   Programa ejemplo que hace uso del teclado matricial
   4x4 mediante interrupciones y liberando 4 pines del portb,
   haciendo posible cambiarlas por cualquier pin del uC PIC.
   Los 4 pines liberados corresponden a las columnas que maneja el teclado
   
   Caracteristicas:
   - Cambio de estado en los pines RB[7-4]
   - Retardo antirrebotes por Timer0
   
   con la interrupción por cambio en PORTB, se gestiona el teclado sin tener
   que encerrar todo el programa principal en un bucle.
   
   la interrupción del timer0 sirve para crear un retardo antirrebotes SIN 
   hacer demorar la ejecución del programa principal.
   
   el próposito de ambas interrupciones es detectar la pulsación de una
   tecla y garantizar que no sea un falso contacto todo ello sin "detener" 
   el bucle principal.
   
   solo se gestionará el proceso correspondiente cuando se pulsa la tecla
   (está validado para no responder a soltar la tecla o mantenerla 
    indefinidamente).
   si quiere ejecutar el proceso asociado a la tecla nuevamente, basta pulsar
   otra vez dicha tecla.


   Autor: Palitroquez
   Modificación: Pocher  16-May-2008 10:10am
   
   Este código puede usarse libremente siempre que se mantenga/n y/o respete/n 
   el/los credito/s de/los autor/es

*/

#include <16F876.h>

#fuses XT,NOWDT,NOPROTECT,
#fuses NOLVP,NODEBUG

#use delay(clock=4000000)      //Con 4Mhz la respuesta es muy lenta
#include <lcd_ind.c>

#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7) // opcional

#use fast_io(A)   // opcional
#use fast_io(B)   //    "
#use fast_io(C)   //    "

//*********************************
// variables globales
//*********************************

int8 const iVeces_iContador_1ms=30;
int8 iFlanco_RB=0;           // bandera que detecta el primer flanco del pulsador
int8 iContador_1ms=0;        // con Fcpu=48MHz, el timer0 cuenta máximo ~ 5ms
int8 iTecla_capturada=0;
int1 fImprimir=0;              // Bandera ejemplo para imprimir tecla presionada
/*
#define iColumna0   PIN_D7      //Otra combinación de valores
#define iColumna1   PIN_C2
#define iColumna2   PIN_A0
#define iColumna3   PIN_D0
*/
#define iColumna0   PIN_B0
#define iColumna1   PIN_B1
#define iColumna2   PIN_B2
#define iColumna3   PIN_B3

//*********************************
// archivos a incluir
//*********************************

#include "Teclado_flex_change_portb.C"

//------------------------- INTERRUPCION TMR0 -----------------------------

#int_timer0
void  tmr0_isr(){
int8 temp;

   if(iContador_1ms==iVeces_iContador_1ms){  
      // retardo antirebotes = 1ms * iVeces_iContador_1ms
      // ya pasaron 30 ms después de la interrupción
      // por cambio de PORTB (el retardo puede variarse)
      temp = input_b() & iLos_Cuatro_MSB_PORTB;
      if(iFlanco_RB == temp){
      // pregunta si realmente la tecla se pulsó
      // o por el contrario es un falso contacto
      
      // aqui es donde se procesa la tecla para lo que queramos hacer
         iTecla_capturada=kbd_getc();
         fImprimir=1;
       }
      Limpiar_Columnas();
      iContador_1ms=0;
      disable_interrupts(INT_TIMER0);
      #asm movf PORTB,0 #endasm
      clear_interrupt(int_rb); // RBIF = 0 limpia la bandera
      enable_interrupts(int_rb);
   }else{
      iContador_1ms++; 
      // continua el miniretardo que es multiplo de retardo deseado
   }
}

//------------------------- INTERRUPCION RB4..RB7 -----------------------------

#int_rb // Interrupcion cambio de estado RB[4-7]
void control_rb() {
   iFlanco_RB = input_b() & iLos_Cuatro_MSB_PORTB;
   // almacena el valor de RB[7-4] inicial
   if(iFlanco_RB != iLos_Cuatro_MSB_PORTB){
      // este condicional sirve para validar cuando se pulsa
      // la tecla (mas NO cuando se suelta).
      // consume 4 lineas de asm.
      disable_interrupts(INT_RB);
      // deselecciona la interrupción por cambio en PORTB
      // para comenzar el retardo antirebote
      iContador_1ms=0;
      setup_timer_0(RTCC_INTERNAL|RTCC_DIV_4|RTCC_8_BIT);
      // configura el Timer0 base de tiempo interno, pre-escaler=1:256
      set_timer0(6); // retardo de ~ 1ms
      enable_interrupts(INT_TIMER0);
   }
   #asm movf PORTB,0 #endasm
   Limpiar_Columnas();
}

//--------------------------------------------------
void main() {
    int n;
    const int a[16]={0xEE,0xDE,0xBE,0x7E,0xED,0xDD,0xBD,0x7D,
                               0xEB,0xDB,0xBB,0x7B,0xE7,0xD7,0xB7,0x77};
    const char b[16]={'1','6','7','*','2','5','8','0','3','4','9','#','A','B','C','D'};

   output_a(0);
   output_b(0);
      
   lcd_init();
   
   set_tris_a(0x0);
   set_tris_b(0b11110000);  // RB[7-4] son entradas
   
   port_b_pullups(true);  
   // no es obligatorio. Si es false, se colocan pull-up externas en RB[7-4]
   disable_interrupts(global);
   disable_interrupts(INT_TIMER0);

   printf("Preparando al teclado por interrupcion (int_RB)\n\r");
   
   set_tris_b(0x0F);
   lcd_gotoxy(1,1);   
   printf(lcd_putc,"Teclado por int RB");
   set_tris_b(0xF0);         //Activar teclado

   Limpiar_Columnas();
   
   enable_interrupts(int_rb);
   #asm movf PORTB,0 #endasm  // con estas 2 lineas, se borra
   clear_interrupt(INT_RB);   // RBIF al inicio del programa
   enable_interrupts(global);
   
   while(1){
      if(fImprimir){
         printf("interrupcion: %x\n\r",iTecla_capturada);
         
         disable_interrupts(INT_RB);
         set_tris_b(0x0F);         //Activar LCD
         lcd_gotoxy(1,1);
         printf(lcd_putc,"interrupcion: %x",iTecla_capturada);
         lcd_gotoxy(1,2);
         for(n=0;n<16;n++)
         {
               if(iTecla_capturada==a[n])      printf(lcd_putc,"Tecla=%c",b[n]);
             }
         set_tris_b(0xF0);         //Activar teclado
         
         iTecla_capturada=0;
         fImprimir=0;
         Limpiar_Columnas();
         #asm movf PORTB,0 #endasm  // con estas 2 lineas, se borra
         clear_interrupt(INT_RB);   // RBIF al inicio del programa
         enable_interrupts(int_rb);

      }
   }
   
}

