Entendiendo las interrupciones y el botón de rebote de software

4

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

Para reaccionar al presionar un botón, estoy usando un PCINT ISR con la resistencia de pull-up interna habilitada de esta manera:

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

Funciona bien, pero creo que no es particularmente inteligente gastar 40 milisegundos en un ISR solo para rebotar un botón.

He leído la Debouncing Contacts Part 2 de Ganssle, que también es un poco avanzado para mi nivel actual de experiencia, y hay una afirmación de la que no estoy seguro:

  

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

¿Entonces mi enfoque para conectar el botón a un PCINT ya es incorrecto? Todavía no me he puesto cerca de los temporizadores, pero ¿debería utilizar algún tipo de interrupción temporizada para evaluar el estado del botón cada 1 ms o menos en lugar de que el botón active una interrupción directamente?

Puedo vivir con mi debouncing no siendo muy inteligente por ahora, pero al menos quiero obtener lo básico de reaccionar con un botón presionando a la derecha.

    
pregunta Torsten Römer

4 respuestas

5

Le sugiero que adquiera el hábito de hacer este tipo de cosas con una interrupción de temporizador y sondeo del interruptor en lugar de intentar usar interrupciones. 1kHz es lo suficientemente rápido.

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

Puede escribir rutinas sin bloqueo que reboten el conmutador.

La regla principal es que no pasas más tiempo en un ISR del que tienes que leer el estado del conmutador; si cambia, empuja un número en una variable estática por el número de milisegundos antes de aceptar cambie el estado y disminuya el número cada vez que atiende la interrupción y el estado no ha cambiado desde la última lectura, pero es diferente del último estado aceptado. Cuando obtiene un cambio de estado estable, póngalo en una cola para que la rutina en segundo plano lo haga (la cola puede tener una profundidad de 1 en algunos casos). Actualizar el último estado aceptado y listo.

    
respondido por el Spehro Pefhany
8

Torsten, sí, introducir una demora de 40 ms en el ISR es una idea equivocada. Por lo general, los botones se sondean en el bucle main ().

Las interrupciones son buenas cuando tienes que detectar un evento con una latencia muy pequeña, o cuando un evento es muy breve. Las pulsaciones de botón se producen en la escala de cientos de ms. Un operador mantendrá presionado el botón durante 50 ms a 100 ms. Luego, si su dispositivo reacciona dentro de 10 ms o dentro de 70 ms, 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 puede conectar un botón a la interrupción si desea activar el microcontrolador desde el modo de suspensión a través de una interrupción. Después de despertar, pulsará el botón.

    
respondido por el Nick Alexeev
1

El problema con las interrupciones y los botones es que el botón proporciona muchos bordes, y por lo tanto, con solo presionar un botón se puede invocar una interrupción docenas de veces. Esto no suele ser lo que quieres. Además, a algunas MCU no les gustan los pulsos muy cortos en la entrada de interrupción, pero este no es el caso de los AVR.

En su caso, si realmente quiere usar PCINT, puede hacerlo si deshabilita más interrupciones en el controlador. De esta manera, solo se detectará la primera pulsación. Por supuesto, usted querrá volver a habilitar la interrupción de PCINT para poder detectar el siguiente botón pulsado. Por lo general, esto significa que necesita un temporizador, y cuando tenga un temporizador, también puede mantenerlo funcionando libremente y sondear los botones en el controlador. Esto será más fácil.

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

  • El usuario presiona un botón, interrumpe los incendios
  • El controlador de interrupción de botones desactiva las interrupciones de botones, activa el aviso sonoro e inicia un temporizador
  • Cuando el temporizador expira, la interrupción del temporizador desactiva el pitido y vuelve a habilitar las interrupciones de los botones
respondido por el theamk
0

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

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

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

y estoy habilitando la interrupción de 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;
    }
}

Aún no se trata de un algoritmo de rebote elaborado, pero ahora no se pierde tiempo en el ISR, el tiempo de respuesta es bueno y no hay problemas de rebote de botones, no importa lo mucho que intente con los diferentes botones que tengo aquí.

ACTUALIZAR :

Después de algunas pruebas realmente extensas, me parece que el sondeo cada 31 ms se anuncia perfectamente, incluso mejor que con 16 ms, tal vez porque 16 ms es un poco cercano al tiempo de rebote nominal de 15 ms de los botones que uso.

Para alcanzar los 32 Hz, he cambiado el temporizador del modo de desbordamiento para borrar el temporizador en el modo de comparación de comparación con un prescaler / 256 que da 3.9 kHz a 1 MHz y establezca el registro de comparación de salida respectivo en 122.

    
respondido por el Torsten Römer

Lea otras preguntas en las etiquetas