PicManía by RedRaven
 

Búsqueda personalizada

 

PROYECTOS : AUX Botones

 

 

Circuito auxiliar para el Uso de Botones

 
Descripción:
 
   En este artículo vamos a intentar presentar de forma clara y concisa el modo correcto de usar botones, pulsadores e interruptores en nuestros montajes. Estos elementos son muy útiles a la hora de proporcionar señales de entrada para nuestros PICs que luego procesamos para modificar su funcionamiento, configuración, etc.
 
   Los botones, pulsadores e interruptores son artilugios mecánicos. Esta circunstancia produce un efecto que es muy importante tener en cuenta a la hora de procesar la señal que le envía al PIC: el rebote.
 
   La conexión o interrupción del contacto eléctrico generada por estos cacharros depende mecánicamente de un muelle o fleje que hace que el contacto permanezca estable, sin embargo el proceso de cierre o apertura de dicho contacto no se produce de una sola vez y para siempre, sino que por el contrario hay una secuencia mas o menos larga de conexiones y desconexiones, dependiendo de la potencia del muelle o fleje, que hace que en vez de tener un paso claro de ON a OFF, o viceversa, lo que tenemos es todo un tren de pulsos, cada vez mas cortos, hasta que se estabiliza en uno u otro estado.
 
   Véase la imagen de abajo en la que mostramos los modos mas usuales de conexión de los pulsadores y cómo la señal vibra antes de estabilizarse:
 


 

   Al ser el PIC mucho mas rápido en su ejecución que las múltiples señales que recibe y al ejecutar nuestro programa funciones distintas dependiendo del estado ON/OFF de entrada es fácilmente observable que dichas funciones se dispararán tantas veces como rebotes tengamos en los pulsadores o interruptores. Los resultados de lanzar la ejecución de estas funciones muchas veces, pudiendo incluso superponerse entre ellas, puede llevar a nuestro programa a cometer errores, inconsistencia o a resultados indefinidos.
 
   Al final de este artículo tenéis un Análisis de rebotes en un botón mediante un programa monitor con el que podéis comprobar de forma práctica y real qué ocurre con vuestro botón cuando es pulsado.
 
Esquema de Conexión:
 
   El esquema de conexión que vamos a utilizar es muy simple y lo vamos a hacer usando los dos métodos de conexión vistos mas arriba, Pull-Up y Pull-Down, con los que vamos a leer "ceros" en T1 y "unos" en T2 con los pulsadores sin accionar y "unos" en T1 y "ceros" en T2 cuando los accionemos :



 

Nuestra solución:
 
   Conceptualmente nuestra solución va a consistir en solo admitir como pulsado, o liberado, un botón cuando periódicamente obtengamos varios valores consecutivos de un estado válido estable. Para ello vamos a tener que usar una variable fundamental en este problema : el tiempo.
 
   Vamos a definir un periodo de tiempo durante el cual vamos a muestrear el estado de la pulsación, si obtenemos sucesivos valores de la pulsación de igual valor a los anteriores daremos por válida la pulsación.
 
Lo que debemos hacer entonces es :
 
  • Definimos cuatro variables: BtnPush, BtnPushCount, PV y NV que nos van a indicar respectivamente el estado de pulsación del Botón, el número de veces que permanece pulsado o despulsado establemente nuestro botón, el valor anterior leído de su estado y el último valor de estado leído. Inicializamos todas la variables ponendolas a 0.

  • Habilitamos una interrupción TMR1, con el módulo Timer1 del PIC 16F628 por ejemplo, de 10 ms de duración.

  • Cada vez que se dispara la interrupción TMR1, recuerda que esto ocurrirá cada 10 milisegundos, se ejecuta el código escrito para ésta. En este código guardamos el valor de la última lectura realizada (NV) sobre la previa (PV) y guardamos el estado del pin asociado al botón en la nueva lectura (NV).

  • Si ahora comparamos ambos valores (NV y PV) tenemos una indicación precisa de lo que está ocurriendo en el pulsador. Si NV y PV tienen el mismo valor indica que han pasado al menos dos interrupciones completas con el botón en el mismo estado. Si ambos valen 1 el botón está pulsado y entonces incrementamos el valor de BtnPushCount. Si ambos valen 0 el botón está liberado y decrementamos el valor de BtnPushCount.

  • Si BtnPushCount vale 0 el pulsador está definitivamente liberado y podemos actuar en consecuencia poniendo a 0 BtnPush. Si su valor es distinto de 0 podemos compararlo con cualquier cantidad, que será el número de interrupciones completas en que permanece en el estado "pulsado", para decidir que damos por válida la pulsación y entonces podemos poner a 1 BtnPush. (Si BtnPushCount alcanza el valor de 10, por ejemplo, y entonces ejecutamos el código escrito para la función correspondiente habremos esperado 100 milisegundos antes de convencernos de que nuestro botón ha sido debidamente pulsado.) 

El siguiente gráfico muestra claramente como actúa nuestra rutina de lectura de botones:
 


 

   El cuerpo principal de nuestro programa solo debe entonces usar el valor de BtnPush que le indicará sin errores el estado de nuestro botón, pulsador o interruptor.
 
 
 
 
 
 

 

 


Análisis de rebotes mediante un programa monitor.

 
   Teniendo en cuenta lo desarrollado en la Descripción de nuestro Circuito auxiliar para el Uso de Botones debemos partir de algún dato real que nos permita saber de cuantos y de que calidad son los rebotes de que estamos hablando.
 
  En este corolario de dicho artículo os propongo un método para conocer la realidad del interruptor o pulsador concreto que estamos utilizando.
 
   Podéis consultar alguna información accesoria y conceptos que ya ha sido tratado en esta Web en:
 
Descripción del análisis:
 
   Nuestra pretensión es calcular cuantos rebotes se producen en una única pulsación del pulsador y cuanto dura cada uno de ellos. Con esto debemos tener una idea clara de qué debemos hacer para corregirlos y obtener una sola pulsación válida, tal como se describe en nuestra solución del artículo que precede a éste.
 
   Nuestro programa hace uso del TIMER0 del PIC configurado en su velocidad máxima, o sea con el Preescaler puesto a 1:2 con lo que TIMER0 da una vuelta completa,  de 00h a FFh cada 512 μS, lo que significa que cada incremento del mismo se procude cada 512 / 256 = 2 μS. Ésta va ser nuestra unidad de media.
 
   Usamos también ExtInt, la interrupción externa, configurada para dispararse con el flanco de bajada, falling edge, que es la que nos va a enviar la información que posteriormente vamos a transmitir a nuestro PC. Recordad que mi hardware está conectado de forma que el PIN RB0 está permanentemente a nivel alto (Vcc) y es el pulsador quien lo tira a nivel bajo (Vss). Al dispararse la interrupción, y dentro de la rutina que la maneja, lo único que hacemos es guardar el estado de TIMER0 en el momento de entrar. Para ello usamos un Array de valores, con su correspondiente índice para ir incrementándolo, y poder así guardar sucesivos valores de TIMER0.
 
   Al recibir por la RS-232 cualquier carácter procedente del PC respondemos enviando el contenido completo del Array al mismo y podemos así conocer lo ocurrido durante la pulsación.

 

 
Programa monitor de rebotes (Versión monoflanco)
 
 
  // button_bounce_analisis

#include <16f876a.h> // Selecciona el PIC
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT // Opciones de configuración
#use delay(clock=4000000) // Velocidad del Cristal : 4 Mhz
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7) // Definición del RS232

int i=0x00;
int uTime=0;
char Keypress=0;
char Command=0;
int Times[20];

#int_rda
void serial_isr() {
  Keypress=0;
  if(kbhit()){
    Keypress=getc();
    if(Keypress!=0){
      Command=Keypress;
    }
  }
}

#int_EXT
EXT_isr() {

  Times[uTime++]=GET_RTCC();
}

void main() {

  ext_int_edge(H_TO_L);

  setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
  setup_timer_1(T1_DISABLED);
  setup_timer_2(T2_DISABLED,0,1);
  setup_comparator(NC_NC_NC_NC);
  setup_vref(FALSE);
  enable_interrupts(int_rda);
  enable_interrupts(int_ext);
  enable_interrupts(global);

  for(i=0;i<20;i++){ Times[i]=0;}


  printf("\r\Button Bounce waiting ...\r\r");

  set_rtcc(0);

  do{

    if(Command!=0){
      Command=0;

      // Transmite resultados

      printf("\n\Results Table:\n\n");
      for(i=0;i<20;i++){
        printf("%2u Value: %3u\r",i,Times[i]);
      }
    }
  } while (TRUE);
}
 
 
 

 

 

 

   El procedimiento a seguir tras grabar el programa en nuestro PIC consiste en conectar el PIC a nuestro PC mediante la RS-232, provocar el RESET del PIC conectando la alimentación o pulsando el botón correspondiente, y pulsar una única vez el botón conectado al PIN receptor de la Interrupción Externa. Tras enviamos cualquier carácter desde nuestro PC a la RS-232 y recibiremos la tabla de tiempos correspondiente al análisis realizado:
 
   Abajo os muestro un ejemplo muy representativo de una pulsación que he obtenido mediante este método:
 


 

   Como podéis comprobar he obtenido 5 interrupciones externas con una única pulsación de mi pulsador, estando en cada una de ellas el contador de TIMER0 en los valores: 89, 168, 254, 90 (después de haber llegado a 255) y 218. Esto puede representarse mediante el siguiente cronograma:
 


 

   Fijaos que he marcado los valores obtenidos sobre cada uno de los flancos de subida. Así tenemos que entre cada uno de ellos hay una diferencia de valores de TIMER0 de 168 - 89 = 79, 254 - 168 = 86, 2 + 90 = 92 y 218 - 90 = 128. Esto corresponde con tiempos de 158 μS, 172 μS, 184 μS y 256 μS respectivamente.
 
   Así que en conclusión mi botón rebota, o puede rebotar, ya que cada pulsación me va dando de 1 a 6 rebotes, durante unos 0.80 milisegundos aproximadamente, casi un milisegundo completo, desde que lo pulso hasta que lo suelto y queda estable.
 
   Ahora ya sabemos de qué estamos hablando.
 
 
Ampliando nuestro análisis:
 
   Este análisis puede realizarse de forma más precisa aún si ampliamos la información recopilada de forma que guardemos los valores de TIMER0 tanto en los flancos de subida como en los de bajada.
 
   Para ello solo tenemos que conmutar el flanco analizado en cada disparo de interrupción. Empezamos con el de bajada, cuando se dispare INTEXT por primera vez guardamos el valor de TIMER0 y conmutamos con ext_int_edge(L_TO_H) para esperar al de subida que volverá a disparar la INTEXT durante la cual volveremos a guardar el valor de TIMER0 y conmutamos de nuevo al flanco de subida con ext_int_edge(H_TO_L). Así se repetirá en cada pulso generado por cada rebote.
  
   Con este procedimiento, mas completo, obtenemos el doble de valores siendo los impares los de los flancos de bajada y lo pares los de subida. Tendremos de esta forma el número de rebotes, la duración de cada uno de ellos y la distancia que los separa.
 
Nuestro nuevo programa quedaría como sigue:
 
 
Programa monitor de rebotes (Versión biflanco)
 
 
 

#include <16f876a.h> // Selecciona el PIC
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT // Opciones de configuración
#use delay(clock=4000000) // Velocidad del Cristal : 4 Mhz
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7) // Definición del RS232

char Keypress=0;
char Command=0;
int i=0x00;
int uTimeUp=0;
int uTimeDown=0;
int TimesUp[6];
int TimesDown[6];
int fedge=0;
int nTintext=0;

void Transmission(void);

#int_rda
void serial_isr() {

  Keypress=0;
  if(kbhit()){
    Keypress=getc();
    if(Keypress!=0){
      Command=Keypress;
    }
  }
}

#int_EXT
EXT_isr() {

  ++nTintext;

  switch(fedge){

    case 0: TimesDown[uTimeDown++]=GET_RTCC();
            fedge=1;
            ext_int_edge(L_TO_H);
            break;

    case 1: TimesUp[uTimeUp++]=GET_RTCC();
            fedge=0;
            ext_int_edge(H_TO_L);
            break;
  }
}

void main() {

  nTintext=0;

  fedge=0;
  ext_int_edge(H_TO_L);

  setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
  setup_timer_1(T1_DISABLED);
  setup_timer_2(T2_DISABLED,0,1);
  setup_comparator(NC_NC_NC_NC);
  setup_vref(FALSE);
  enable_interrupts(int_rda);
  enable_interrupts(int_ext);
  enable_interrupts(global);

  for(i=0;i<6;i++){ TimesUp[i]=0;}
  for(i=0;i<6;i++){ TimesDown[i]=0;}


  printf("\r\Button Bounce waiting ...\r\r");

  set_rtcc(0);

  do{

    if(Command!=0){
      Command=0;
      Transmission();
    }

  } while (TRUE);
}

void Transmission(void){

  printf("\n\Results Table:\n\n");

  printf("Total intext %2u\r",nTintext);

  printf("\n\Down Table:\n\n");
  for(i=0;i<6;i++){
    printf("%2u Value: %3u\r",i,TimesDown[i]);
  }

  printf("\n\Up Table:\n\n");
  for(i=0;i<6;i++){
    printf("%2u Value: %3u\r",i,TimesUp[i]);
  }
}
   
 
 

 

 

 

  Con la modificación correspondiente del programa anterior tenemos una tabla de resultados tal y como puede verse en la imagen inferior en la que Down Table representa los flancos de bajada y Up Table los de subida:
 

 
 ¿Alguien da más?
 
(Pues sí ... ya que aún no tenemos controlado el tiempo que mantenemos pulsado el pulsador ... durante el cual pueden pasar centenares o miles de ticks de TIMER0 cruzando la frontera entre FFh y 00h decenas de veces con lo que los valores entre el ultimo rebote al pulsar y el primero al soltar no son nada válidos; pero su solución es fácil y os la dejo para que la soluciones ustedes :-)  Ya sábies qué hacer ... ¿o no?)
 

 

Esta página se modificó el 27/12/2008


Esta página usa la letra Ñ

Nota pública importante sobre las consultas al Webmaster de PicManía.


Sugerencias a Picmanía... (que serán leídas pero seguramente no podrán ser contestadas)

Esta página pertenece al grupo de páginas de Diego RedRaven

 

 



Nota: Esta página Web esta repleta de imágenes, textos, logotipos y demás material extraídos de los mas variados medios de los que no soy ni autor ni depositario de los correspondientes derechos de autor, uso y/o reproducción. Si Ud. es depositario de dichos derechos y desea que el material correspondiente sea eliminado de esta Web no dude en ponerse en contacto conmigo mediante e-mail y será inmediatamente retirado. Gracias.
 
Visitas
Totales : 10386 Hoy: 1 Activas: 1 Vistas: 10386

Esta página fue modificada el 07-08-2010 22:42:21

           
 DmSoft WAMP Escribir Unreal