Estoy tratando de usar interrupciones de cambio de pin para detectar botones presionados. Hasta ahora nunca he trabajado con este tipo de interrupciones y hay algunos problemas, así que quiero asegurarme de que este sea el uso correcto.
Si obtuve la hoja de datos correcta, se deben hacer las siguientes cosas para usar una interrupción de cambio de pin:
- Establezca qué PIN desea controlar en el registro PCMSK
- Habilite el registro de PIN para el control de interrupción de cambio de pin (PCICR)
- Habilitar interrupciones
- Use el vector de interrupción correspondiente
Proyecto: Moodlamp simple, colores controlados a través de 4 botones.
Preparar:
- Atmega168A-PU
- 4 mini interruptores de botón
- MOSFETS para controlar mi LED RGB de 3 vatios
Aquí está el código que estoy usando que no funciona como se esperaba:
#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define BUTTON1 (1<<PC5)
#define BUTTON2 (1<<PC4)
#define BUTTON3 (1<<PC3)
#define BUTTON4 (1<<PC2)
#define GREEN (1<<PB1)
#define BLUE (1<<PB2)
#define RED (1<<PB3)
void init() {
// enable LED
DDRB |= GREEN;
DDRB |= BLUE;
DDRB |= RED;
// button pullups
PORTC |= BUTTON1;
PORTC |= BUTTON2;
PORTC |= BUTTON3;
PORTC |= BUTTON4;
// pin change interrupts for buttons
PCMSK1 |= PCINT13;
PCMSK1 |= PCINT12;
PCMSK1 |= PCINT11;
PCMSK1 |= PCINT10;
// enable pin change for buttons
PCICR |= PCIE2;
sei();
}
ISR(PCINT2_vect) {
PORTB = BLUE;
}
void ledTest() {
PORTB ^= RED;
_delay_ms(250);
PORTB ^= RED;
_delay_ms(250);
PORTB ^= RED;
_delay_ms(250);
PORTB ^= RED;
PORTB ^= BLUE;
_delay_ms(250);
PORTB ^= BLUE;
_delay_ms(250);
PORTB ^= BLUE;
_delay_ms(250);
PORTB ^= BLUE;
PORTB ^= GREEN;
_delay_ms(250);
PORTB ^= GREEN;
_delay_ms(250);
PORTB ^= GREEN;
_delay_ms(250);
PORTB ^= GREEN;
}
int main() {
init();
ledTest();
_delay_ms(500);
PORTB |= GREEN;
while(1) {
_delay_ms(100);
}
}
Nota: Los botones deben ser eliminados. Como estoy tratando de hacerlo paso a paso y no debería importar encender el LED, lo ignoré aquí.
Pregunta: ¿Es correcta la forma en que intento usar las interrupciones?
Problemas con mi configuración:
- Los botones 1-3 se ignoran por completo.
- Button4 está activando un reinicio de atmega
Cosas que revisé:
- Los botones no están conectados de ninguna manera al PIN de reinicio
- Los botones están conectados correctamente a GND si se presionan
- Los botones no están conectados a GND si no se presionan
Los botones funcionan bien si los utilizo sin interrupción, por ejemplo:
if (! (PINC & BUTTON4)) {PORTB ^ = AZUL; }
- Cristal externo de 16MHZ / cristal interno
- Cualquier error en el enrutamiento
- Estoy usando un condensador de 100nF entre PWR y GND en el atmega
- VCC (7), GND (8), GND (22), AVCC (20) están conectados (ya que no necesito AREF, no está conectado)
fuente
Respuestas:
Las interrupciones de cambio de pin generalmente no son una buena manera de detectar acciones de botón. Esto se debe a que los botones mecánicos rebotan, y obtendrá muchas interrupciones sin sentido, y de todos modos aún tendrá que eliminar el rebote.
Una mejor manera es tener una interrupción periódica, como cada 1 ms (velocidad de 1 kHz). Eso es mucho tiempo en la mayoría de los procesadores, por lo que la fracción de tiempo invertida en la interrupción será pequeña. Simplemente muestre el estado del botón en cada interrupción. Declare un nuevo estado del botón si ha visto el nuevo estado 50 ms seguidos. 50 ms es más largo que el rebote de la mayoría de los botones, pero aún es lo suficientemente corto como para que los humanos no noten ni se preocupen por el retraso.
Tenga en cuenta que de esta manera también puede manejar varios botones en la misma interrupción periódica de 1 ms. Todo lo que necesitas es un contador para cada botón.
Más sobre el tiempo de rebote:
Ocasionalmente, como en este caso, alguien dice que 50 ms es un tiempo de rebote demasiado largo. Esto no es cierto para los botones ordinarios presionados por humanos. Puede ser un problema quizás en aplicaciones muy críticas como un cronómetro, pero hasta ahora no me he encontrado con una. Lo probé a principios de la década de 1980, y muchas otras personas también lo han hecho.
Es cierto que el tiempo de rebote típico de un botón pulsador es de alrededor de 10 ms, con casi todo estableciéndose en 25 ms. El factor limitante en el tiempo de rebote es la percepción humana. 50 ms es un poco más corto que cuando las personas comienzan a notar un retraso cuando no lo están buscando. Incluso entonces, toma mucho más tiempo para que sea molesto. En algunos casos, puede ser posible que un humano detecte una diferencia entre 50 ms y 0 ms de retraso si lo está buscando específicamente , pero eso es bastante diferente de presionar un botón y ver que sucede algo y no pensar en el retraso.
Por lo tanto, 50 ms es un buen tiempo de rebote porque el retraso está por debajo del límite de percepción en aplicaciones normales, muy por debajo del límite de molestia y muy por encima del tiempo de rebote de la mayoría de los interruptores. He encontrado interruptores que rebotaron durante casi ese tiempo, por lo que es mejor que llegues al límite de percepción ya que no hay nada que perder.
He hecho muchos productos con botones de rebote de firmware utilizando 50 ms de tiempo de rebote. Ni una sola vez un cliente mencionó siquiera notar un retraso. Todos aceptaron que los botones funcionaban bien sin problemas.
fuente
Las interrupciones de cambio de pin son una mejor manera de eliminar el rebote que el sondeo. La interrupción generalmente pasa por alguna lógica, como un D-Flip Flop o D-Latch. Aunque esto es cierto, es más difícil implementar esta rutina de rebote con compiladores de nivel superior. Una vez que se produce la interrupción, el indicador de interrupción no se borra y la habilitación de interrupción se borra hasta que se produce un retraso. Una vez que se ha producido el retraso, se verifica el estado del pin y si todavía está en el estado dado que activó la interrupción, se cambia el estado del botón y se borra la bandera de interrupción y se establece la habilitación de interrupción. Si no está en el estado que causó el inicio, se establece la habilitación de interrupción y el estado permanece igual. Esto libera el procesador para otras tareas. Las interrupciones periódicas pierden tiempo en el programa.
fuente
Incorrecto. PC INT es la mejor opción. Si utiliza el sondeo para verificar el estado de un botón, no se realizará nada la mayor parte del tiempo. Pierdes mucho tiempo precioso de CPU. PC INT permite que las acciones se realicen solo a pedido.
Correcto sobre el rebote. Sin embargo, NUNCA debe eliminar el botón / interruptor dentro de una rutina de interrupción (mismo motivo: pérdida de tiempo de CPU). Los ISR están destinados a ser realmente cortos y eficientes, en cuanto a código. Solo use el rebote de hardware. ¡Mantenga limpio su software!
La eliminación de rebotes del hardware es más conveniente, consulte aquí / Eliminación de rebotes RC + disparador Schmitt para referencia. Lo he usado innumerables veces con PC INT, nunca falló.
Entonces sí, puede (y debe) usar PC INT para obtener un estado de botón. Pero también tiene que usar el rebote de hardware adecuado.
fuente