Autor Tema: Filtro antirebote mediante interrupción del timer1 (20ms)  (Leído 6339 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Filtro antirebote mediante interrupción del timer1 (20ms)
« en: 08 de Abril de 2008, 00:40:57 »
Hola!, gracias a la ayuda del compañero jgpeiro06 he podido simplificar éste código y hacerlo un poquito más elegante, basicamente es un filtro antirebote a través de una interrupción de 20ms del timer1 del pic18f4550, esto es útil cuando tenemos un proceso en el que no podemos quedarnos a esperar 20ms, 40ms ó lo que sea porque hay otras cosas que requieren nuestra atención también, entonces le dejamos el trabajo al timer a que nos recuerde que pasaron 20ms y que la señal es estable y confiable.

Resumiendo, el programa funciona así: Si hay un cambio de estado en el pin de entrada, se manda a contar 20ms a través de la interrupción del timer1, al pasar este lapso de tiempo se verifica que ese cambio se mantuvo, entonces regresa el valor que tiene ese pin, porque dicha señal es considerada estable.

A continuación el código:

Código: [Seleccionar]
#include <18F4550.h>
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN,NOPBADEN
#use delay(clock=48000000)

#byte PORTA = 0xF80// Son las direcciones de memoria de los puertos en este PIC
#byte PORTB = 0xF81
#byte PORTC = 0xF82
#byte PORTD = 0xF83
#byte PORTE = 0xF84

#define  entrada1  PIN_A0
#define  entrada2  PIN_A1

#define  salida1  PIN_B0
#define  salida2  PIN_B1

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

typedef struct{
   long   pin;// Es el pin de entrada del LS
   int8   contando;// si es 0 indica que no está contando
   int8   pasaron_20ms;// Si es 1 significa que pasaron 20ms
   int8   estado_actual;// Es el estado actual del pin, sea 0 ó 1
   int8   estado_anterior;// Es el estado anterior del pin
} mi_LS;// mi Limit Switch

const long delay_20ms=35535;

mi_LS ls_art_1={entrada1 0 0 0 0};
mi_LS ls_art_2={entrada2 0 0 0 0};

// Prototipos de funciones
short estado(mi_LS *);
// Verifica el estado de un pin con un antirebote de 20ms
// manejado con la interrupción del timer1

#INT_TIMER1
void timer1_isr()
{
    if (ls_art_1.contando==1){// mando a contar el ls de la art1??
      ls_art_1.pasaron_20ms=1;
    }
    if (ls_art_2.contando==1){// mando a contar el ls de la art2??
      ls_art_2.pasaron_20ms=1;
    }
    disable_interrupts(INT_TIMER1);// Deshabilitamos
}

void main(void) {

   set_tris_a(0b00000011);// A0 y A1 como entradas
   set_tris_b(0);// Todo el puerto B como salida
   
   enable_interrupts(GLOBAL);  // Deshabilitamos todas las inteerupciones
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_8);
   // 8*(1/12E6)=666,6667ns= 1 Tick, 20ms/1Tick=30.000Ticks
   // 65535-30000=35535, es el valor con el que tengo que cargar
   // al timer1 para que ocurra una interrupción a los 20ms
   // set_timer1(delay_20ms);// delay_20ms=35535
   
   output_low(salida1);
   output_low(salida2);
   
   while (TRUE){
     
      if (estado(&ls_art_1)) output_high(salida1);
      else output_low(salida1);
     
     
      if (estado(&ls_art_2)) output_high(salida2);
      else output_low(salida2);
     
   }
}

short estado(mi_LS * ls){
// Verifica el estado de un pin con antirebote mediante la interrupción
// del timer 1, basicamente detecta si hubo un cambio de estado en el pulsador
// si ese cambio de estado se mantiene por 20ms entonces la señal se considera
// estable y por consiguiente la función regresa su valor.
   
   if (ls->contando){// Mandé a contar??
      // Efectivamente hemos mandado a contar 20ms
      if (ls->pasaron_20ms){// Pasaron 20ms??
         // han pasado 20ms
         ls->contando=0;// Apago éste indicador porque ya pasaron los 20ms
                        // y no estamos contando más
         ls->estado_actual=input(ls->pin);// Leemos el valor actual del pin
         // Verificamos si el cambio se mantiene
         if ((ls->estado_actual)!=(ls->estado_anterior)){
            // Efectivamente el cambio se mantuvo
            ls->estado_anterior=ls->estado_actual;
            // Actualizamos el valor del estado anterior del pin, porque la
            // señal es estable
         }
      }
   }
   else{// No he mandado a contar aún
     
      ls->estado_actual=input(ls->pin);// Leemos el valor actual del pin
     
      // verificamos si hubo un cambio de estado
      if ((ls->estado_actual)!=(ls->estado_anterior)){
         // Entonces hubo un cambio de estado en el pin
         ls->contando=1;// indico que mande a contar
         ls->pasaron_20ms=0;// Indico que no han pasado 20ms
         set_timer1(delay_20ms);
         clear_interrupt(INT_TIMER1);// Para prevenir una interrupción
                                     // indeseada al iniciar el servicio
                                     // de interrupción
         enable_interrupts(INT_TIMER1);// Habilito interrupción del timer1
      }
   }
   if (ls->estado_anterior) return(1);
   else return(0);
}

Pueden utilizar éste diseño en el proteus para verificar su funcionamiento.

Saludos.

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: Filtro antirebote mediante interrupción del timer1 (20ms)
« Respuesta #1 en: 08 de Abril de 2008, 01:50:46 »
Guillermo, ¿tu programa está constantemente entrando en la interrupción durante toda la ejecución del mismo aunque no se pulse ningún pulsador?

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Filtro antirebote mediante interrupción del timer1 (20ms)
« Respuesta #2 en: 08 de Abril de 2008, 09:40:45 »
Nop, la interrupción solo se activa cuando ocurre un cambio de estado en el pin.

Voy a tratar de explicar con un ejemplo, supongamos que la lectura del pin de entrada en un momento dado es 1, y para nosotros significa que el pulsador está abierto, si ese valor se mantiene en el tiempo, cuando llames a la función estado(pin) ésta te regresará '1' porque ese es el estado del pin, ahora bien ocurre un cambio de estado y pasó a ser 0, al llamar a la función estado(pin) ésta sabrá que a ocurrido un cambio de estado y configurará al timer1 para que a los 20ms active una bandera y a la vez se desactive la interrupción, cuando la función estado() detecte que esa bandera está activa (ya pasaron 20ms) entonces vuelve a leer el pin y lo compara con el valor que tenía hace 20ms atrás, si son diferentes significa que ocurrió un cambio de estado, por lo tanto hay un nuevo valor de dicho pin.

Como dije al principio, solo entra en la interrupción cuando hay un cambio de estado, de lo contrario nop, de esta manera es más eficiente, ya que no hacemos entradas innecesarias a la interrupción  :wink:

Si algo no ha quedado claro me explico cuantas veces sea necesario. 8)

Saludos. :)


Desconectado manex_1987

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1127
Re: Filtro antirebote mediante interrupción del timer1 (20ms)
« Respuesta #4 en: 08 de Abril de 2008, 15:44:08 »
Maxim tiene unos chips que eliminan los rebotes de los pulsadores directamente por hardware. MAX6818 creo que es uno. Los he pedido pero estan en estado "approval" osea "vamos a ver majete..." , aver si me llegan. La verdad es bastante incomodo lo de los rebotes.

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Filtro antirebote mediante interrupción del timer1 (20ms)
« Respuesta #5 en: 08 de Abril de 2008, 16:45:01 »
Muy buen dato Manex  :idea: no sabía.  :c) Eso de los rebotes son como una piedrita en el zapato, por más que estemos apurados hay que detenerse para sacarsela  :roll:

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Filtro antirebote mediante interrupción del timer1 (20ms)
« Respuesta #6 en: 08 de Abril de 2008, 17:57:21 »
Como una imagen dice más que mil palabras, realicé un diagrama de flujo de como funciona el código de manera general:



Es una visión general, para más detalle, chequear el código.

Saludos.


 

anything