5 votos

Entender las interrupciones y el rebote de los botones por software

Soy bastante nuevo en la programación de AVR (avr-gcc).

Para reaccionar al pulsar el botón, estoy usando un ISR PCINT con la resistencia interna de pull-up habilitada así:

ISR(PCINT0_vect) {
    if (bit_is_clear(PINB, PB0)) {
        _delay_ms(40);
        if (bit_is_clear(PINB, PB0)) {
            // do something
        }
    }
}

Funciona bien, pero supongo que no es especialmente inteligente gastar 40 milisegundos en una ISR sólo para desbaratar un botón.

He leído el libro de Ganssle Contactos de rebote Parte 2 que es un poco demasiado avanzado para mi nivel de experiencia actual, y hay una afirmación de la que no estoy seguro:

El interruptor no rebotado debe conectarse a un pin de E/S programado, nunca a una interrupción.

Entonces, ¿mi planteamiento de conectar el botón a un PCINT ya es erróneo? Todavía no me he metido con los temporizadores, pero ¿debería usar algún tipo de interrupción temporizada para evaluar el estado del botón cada 1 ms o así en lugar de que el botón active una interrupción directamente?

Puedo vivir con que mi desbaratamiento no sea muy inteligente por ahora, pero al menos quiero conseguir lo básico para reaccionar al pulsar un botón.

9voto

Nick Alexeev Puntos 20994

Torsten, sí, introducir un retraso de 40ms en el ISR es una idea equivocada. Normalmente, los botones son sondeados en el bucle main().

Las interrupciones son buenas cuando hay que detectar un evento con una latencia muy pequeña, o cuando un evento es muy breve. Las pulsaciones de botones se producen en la escala de cientos de ms. Un operador mantendrá el botón pulsado entre 50 y 100 ms. Entonces, si su dispositivo reacciona en 10ms o en 70ms, el operador no notará la diferencia. Eso es bastante lento, por lo que no tiene que ser detectado a través de una interrupción.

Se podría conectar un botón a la interrupción si se quiere despertar al microcontrolador del sueño a través de una interrupción. Después de despertarlo, se sondearía el botón.

6voto

Spehro Pefhany Puntos 90994

Te sugiero que te acostumbres a hacer este tipo de cosas con una interrupción del temporizador y sondeando el interruptor en lugar de intentar usar interrupciones. 1kHz es suficientemente rápido.

Como otros han dicho, el interruptor generará múltiples bordes en momentos impredecibles que podrían causar problemas o interacción con otras rutinas.

Se pueden escribir rutinas no bloqueantes que debaten el cambio.

La regla cardinal es que no pases más tiempo en una ISR de lo que tienes que leer el estado del interruptor, si ha cambiado, mete un número en una variable estática para el número de milisegundos antes de aceptar un cambio de estado y decrementa el número cada vez que atiendas la interrupción y el estado no haya cambiado desde la última lectura pero sea diferente del último estado aceptado. Cuando obtengas un cambio de estado estable mételo en una cola para que la rutina de fondo se encargue de él (la cola podría tener una profundidad de 1 en algunos casos). Actualiza el último estado aceptado y listo.

2voto

joelmdev Puntos 148

El problema con las interrupciones y los botones es que el botón da muchos bordes, y por lo tanto una pulsación de botón puede invocar una interrupción docenas de veces. Esto no suele ser lo que se quiere. Además, a algunos MCUs no les gustan los pulsos muy cortos en la entrada de la interrupción, pero este no es el caso de los AVRs.

En tu caso, si realmente quieres usar PCINT, puedes hacerlo si desactivas más interrupciones en el manejador. De esta manera, sólo se detectará la primera pulsación. Por supuesto, querrás volver a habilitar la interrupción PCINT para poder detectar la siguiente pulsación del botón. Esto normalmente significa que se necesita un temporizador, y cuando se tiene un temporizador, es mejor mantenerlo libre y sondear los botones en el manejador - esto será más fácil.

Sin embargo, el "botón para interrumpir el pin" puede ser ocasionalmente útil si se desea iniciar un temporizador de todos modos, por ejemplo, para un breve pitido al pulsar una tecla. En este caso, el escenario es el siguiente:

  • El usuario pulsa un botón, la interrupción se dispara
  • El manejador de la interrupción del botón desactiva las interrupciones del botón, enciende el beeper e inicia un temporizador
  • Cuando el temporizador expira, la interrupción del temporizador apaga el zumbador y vuelve a activar las interrupciones de los botones

1voto

Ben Clifford Puntos 211

Tratando de seguir las respuestas aquí y después de leer sobre los temporizadores, me gustaría lanzar mi propia solución simple en el anillo.

Con la CPU del ATmega328P funcionando a 1 MHz, estoy configurando el temporizador 0 de 8 bits en modo normal con un preescalado de reloj /64 para que se desborde cada 16 ms:

TCCR0B |= (1 << CS00) | (1 << CS01);

y estoy habilitando la interrupción por desbordamiento del temporizador 0:

TIMSK0 |= (1 << TOIE0);

en el ISR correspondiente estoy comprobando el estado del botón:

volatile bool buttonPressed = false;    

ISR(TIMER0_OVF_vect) {
    if (bit_is_clear(PINB, PB0) && ! buttonPressed) {
        buttonPressed = true;
        // do something
    } else if (bit_is_set(PINB, PB0)) {
        buttonPressed = false;
    }
}

Sigue sin ser un algoritmo de desbordamiento elaborado, pero ahora no se pierde tiempo en el ISR, el tiempo de respuesta es agradable y no hay problemas de rebote de los botones por mucho que lo intente con los diferentes botones que tengo aquí.

ACTUALIZACIÓN:

Después de algunas pruebas muy extensas, me parece que el sondeo cada 31 ms rebota perfectamente, incluso mejor que con 16 ms - tal vez porque 16 ms es un poco cerca del tiempo de rebote nominal de 15 ms de los botones que uso.

Para conseguir los 32 Hz he cambiado el temporizador del modo overflow al modo clear timer on compare match con un prescaler/256 que da 3.9 kHz @ 1 MHz y he puesto el respectivo registro de comparación de salida a 122.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X