He estudiado todo lo que he podido encontrar en la red sobre interrupciones y antirrebotes y lo que pruebo no me da una eficacia 100%. Hace algo pero no me es suficiente.
Ahora mismo a un pulsador le tengo puesto un condensador de 1uF y en el software, tengo un código que supuestamente hace el antirrebote.
void setup()
{
pinMode(BlackButton, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BlackButton), Debounce_interrupt_blackButton, FALLING);
}
void loop()
{
flag = false;
pixels.neoPixelModo(modo);
}
//FUNCIONES
void Debounce_interrupt_blackButton() //Sistema antirrebote de los pulsadores
{
if (millis() - startTime > timeThreshold)
{
interrupt_blackButton();
startTime = millis();
}
}
void interrupt_blackButton()
{
flag = true;
pixels.fill(pixels.Color(0,0,0),0,numberLeds);
pixels.show();
modo++;
if(modo > 5)
modo = 1;
Serial.println(modo);
}
Cuando pulso un pulsador, la idea es que de UNA pulsada me cambie de modo en la función interrupt_blackButton() UNA SOLA VEZ. La funcion lo que hace es que unas barra de leds cambie su forma de iluminarse
Necesito que esta parte sea totalmente eficaz para hacer que luego ese boton cuando pulse dos veces consecutiva me haga otra cosa en las barras de leds, por ejemplo que cambien de color.
Bueno ya que lo preguntas: Que dejes de usar interrupciones con un pulsador! El uso de interrupciones esta destinado a eventos que transcurren en periodos muy cortos de tiempo. Un pulsador es bastante lento y no es necesario el uso de una interrupción a menos que tengas una mala programación con el uso de delay().
En tu código dentro de la interrupción haces todo mal, te dejo unas lineas de un tutorial de prometec
Hay varias consideraciones a tener en cuenta con las interrupciones:
*Haz lo que quieras pero no te demores. Acaba cuanto antes y lárgate.
*Hay cosas que no funcionan, como las funciones delay (), millis () y cualquier cosa que dependa de interrupciones o timers.
*Ni se te ocurra meter un Serial.Nada en una interrupción, son muy lentos (Y además tampoco funcionan porque también dependen de interrupciones).
*Debes entender que una interrupción es como un estado de excepción, que se puede usar sin reparos, pero entendiendo que hay que hacer el trabajo y salir cuanto antes.
*De hecho, una ISR o función CallBack, no puede devolver parámetros ni tampoco recibirlos
Ahora bien, si quieres hacer uso de un pulsador en tu código puedes hacerlo de la siguiente manera:
Parece que alguien le ha sangrado los ojos. hahaha vale vale! Me dieron unas ideas de como hacer esta parte.
Yo no sabía que las interrupciones era para poner poco código.
Mi planteamiento era que cuando una secuencia de led terminase cuando yo quieisera sin tener que esperar a que termine una vez pulsado un boton, era poner ese boton como interrupcion.
Pero veo que hacerlo asi es una equivocación
Hace poco tuve un problema similar, para lo cual escribi mi propia clase que resuelve el antirebote y te entrega si el boton fue presionado y por cuanto tiempo fue preosionado o si esta atascado, espero el ejemplo sea claro, si estas trabajando con un atmega328p este codigo te funcionara, de otra manera seria solo un claro ejemplo de como usar Timer Interrupts para realizar el antirebote en una entrada que no necesariamente debe ser un boton.
/*en el Setup debes configurar tus interruptiones para que pasen cada por lo tanto cada milisegundo existira una Interrupt Service Request se ejecuta ese codigo y el funcionamiento de programa seguira*/
enum ButtonMode{
BUTTON_OFF,
BUTTON_SHORT,
BUTTON_LONG,
BUTTON_STUCK
} ;
void setTimer2(void) {/*ISR every 1 mS*/
cli(); // stop interrupts
TCCR2A = 0; // set entire TCCR2A register to 0
TCCR2B = 0; // same for TCCR2B
TCNT2 = 0; // initialize counter value to 0
// set compare match register for 1000 Hz increments
OCR2A = 249; // = 8000000 / (32 * 1000) - 1 (must be <256)
TCCR2B |= (1 << WGM21); // turn on CTC mode
TCCR2B |= (0 << CS22) | (1 << CS21) | (1 << CS20); // Set CS22, CS21 and CS20 bits for 32 prescaler
TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt
sei(); // allow interrupts
}
ISR(TIMER2_COMPA_vect) {/* Every 1 mS update pipelines */
pipeline <<= 1;
if (digitalRead(pin) == HIGH){
pipeline |= 1;
}
if (pipeline == 0xFF) {
if (counter > LONG_PRESS) buttonMode = BUTTON_LONG;
else if (counter > SHORT_PRESS) buttonMode = BUTTON_SHORT;
else buttonMode = BUTTON_OFF;
if (counter > TIME_OUT) buttonMode = BUTTON_STUCK;
}
if (pipeline == 0) {
if (counter < 0xFFFF) counter++;
if (counter > TIME_OUT) buttonMode = BUTTON_STUCK;
}
}
Excelente tu rutina @Buggy_Code pero tal como la veo hasta ahora no es una Clase tal como se espera en C++. Pero es un planteo muy interesante.
De todos modos, lo que se debate es que para accionar un simple pulsador con o sin rebote, no necesitas usar interrupciones, es mas, ya RIG dió todas las razones por las cuales no se debería usar una interrupción para algo tan simple como ver cuando se presiona un pulsador. Aclaro, hablo de pulsador!! Si fuera otra cosa y no queremos perder un click del reloj es otra cosa.
Aporto algo tan simple como Bounce2.h que es una librería que usa millis() basicamente para controlar el estado de un pulsador.
Esta en el playground de Arduino.cc
Este es un ejemplo simple
#include <Bounce2.h>
#define BUTTON_PIN 2
#define LED_PIN 13
// Instantiate a Bounce object
Bounce debouncer = Bounce();
void setup() {
// Setup the button with an internal pull-up :
pinMode(BUTTON_PIN,INPUT_PULLUP);
// After setting up the button, setup the Bounce instance :
debouncer.attach(BUTTON_PIN);
debouncer.interval(5); // interval in ms
//Setup the LED :
pinMode(LED_PIN,OUTPUT);
}
void loop() {
// Update the Bounce instance :
debouncer.update();
// Get the updated value :
int value = debouncer.read();
// Turn on or off the LED as determined by the state :
if ( value == LOW ) {
digitalWrite(LED_PIN, HIGH );
}
else {
digitalWrite(LED_PIN, LOW );
}
}
Presten atención a esta línea
debouncer.interval(5); // interval in ms
Es la que controla el tiempo del rebote. Si quieren poner ahi 100 mseg reemplazan 5 x 100 y listo.
Es muy práctico para situaciones de estado de contactos como los de un rele.
Si esa libreria esta mucho mas practica, gracias en mi caso estoy usando esta clase button en varios proyectos , mi intencion esta mas enfocada en mostrar como implementar una rutina de debouncing usando timer interrupts, si alguien necesitara usar esta libraria completa, puedo adjuntar el .h and .cpp files, pues me gustaria obetener un poco de feedback en mi logica .
Gracias a todos por enseñar un poco de codigo.
Voy a intentar hacer el propuesto buggy_code porque pinta interesante. Puse en practica solo la funcion de setTimer2() y ahora me pondré con la siguente. Aunque hay líneas que no entiendo muy bien como funciona porque todavia no controlo las máscaras de bits.
Aver, intuyo que tengo que escribir una línea de interrupcion tipo:
attachInterrupt(digitalPinToInterrupt(pin), ISR, RISING)
y de ahí me rediccionará a la interrupcion ISR(), ¿cierto?
la funcion setTimer2, configura los registro necesarios el prescaler y el modo en el que ocurrira la interrupcion, lo cual esta especificado, en los comentarios, especificamente and ISR significa INTERRUPT SERVICE REQUEST, tu micro estara ejecutando las rutinas que programaste y luego cada milisegundo en nuestro ejemplo, se genera un interrupcion en esta interrupcion se lee el valor del PIN de entrada. como el rebote mecanico de un boton genera varios pulsos LOW and HIGH antes de ser estable, leemos cada milisigundo en nuestra interrptcion el pin, luego utilizamos una varibale de 8 bits (tu puedes hacer tu pipeline), tan larga como quieras dependiendo de tu boton y que tanto sea el ruido generado.), teneindo en cuenta que usas INPUT_PULLUPS, estarias leyendo cada vez que el PIN sea LOW es un press.
No necesitas hacer el attachInterrupt(digitalPinToInterrupt(pin), ISR, RISING), porque eso lo estas confiurando directamente con registros en la funcion, refierete al DATASHEET atmega328p, para que validez la informacion.
pipeline <<= 1; // aqui el right shift operator corre el uno para la izquierda si no ha sido presionado de // otra manera pondra un zero.
if (digitalRead(pin) == HIGH){ //leer el estado del pin cada ms
pipeline |= 1; // pipeline es una varibale de ocho bits cuando empieza es = 00000000
// cada vez que el pin es HIGH, por lo tanto no ha sido presionado, la varibale pipeline
// pipeline = pipeline | 1 , estas haciendo la operacion OR que basicamente pone un 1 en
//pipeline la primera vez seria = 00000001.
}
Cuando se llene la pipeline con suficientes zeros quiere decir que tenemos un pulso valido, como todo esto pasa en un timer interrupt, yo puedo contar cuanto tiempo ha esta presionado el boton determinando que tipo de press es.
Siento decir que el codigo de Buggy_code no funciona con mi codigo.
He copiado solo el codigo setTimer1() a que vaya 1hz y que cada 1hz se muestre en pantalla on y off. para ver como va funcionando en concordancia con mi codigo.
Pues el problema es que la barra de led no se enciende los 8leds, si no solo 4, se interrumpe como es logico y empieza de nueva.
Si lo pruebo a 2khz, directamente los leds ni se enciende =S