I tried to follow the answer here: https://arduino.stackexchange.com/a/18545/51302
Unfortunately I can only get the interrupt to go once (on serial monitor is the time of that first interrupt), then nothing. I know this has already been answered but I don't understand the mistake and would appreciate help.
What I'm trying to do: get a button to set an interrupt to trigger foo and thus write the steps on the monitor.
I've left remarks to try and explain what I try to do, so it would be easier to understand what I left out or used incorrectly. Thanks.
#define IRF520 A2
#define BUTTON 3
static unsigned long last_press = 0;
static bool button_state = HIGH; //pull up resistor
volatile bool interrupt_flag = 0; //when the interrupt fires, button press or noise
bool is_pressed = false; //after checking that the button was really pressed
bool was_pressed = false; //last state of the button
unsigned long button_pause_duration = 20; //pause required to accept press in ms
unsigned long interrupt_time=0;
void button_interrupt() {
interrupt_flag = true; //can be a false alarm
Serial.print("interrupt time: "); //this is the onlt step that works
Serial.print(millis());
Serial.print("\n");
}
void setup() {
Serial.begin(9600);
pinMode(IRF520, OUTPUT); //A2 controls IRF520
pinMode(BUTTON, INPUT_PULLUP); //pin D2 and D3 are sutible for interrupts
attachInterrupt(digitalPinToInterrupt(BUTTON), button_interrupt, FALLING);
}
void debounce_button() {
if (digitalRead(BUTTON) != button_state) {
button_state = digitalRead(BUTTON); //if button changed state remember the new state
interrupt_time=millis();
}
if (button_state == LOW) {
last_press = millis();
Serial.print("last_press was updated to: ");
Serial.print(last_press);
Serial.print("\n");
} else {
last_press = 0;
interrupt_flag = false;
}
if ( last_press && (millis() - last_press) > button_pause_duration) {
last_press = 0; //set to 0 for the next check
is_pressed = true; //button was really pressed
Serial.print("last_press was set to 0 and is_pressed to true - BUTTON WAS PRESSED at: ");
Serial.print(millis());
Serial.print("\n");
}
}
unsigned int foo(bool is_pressed) {
if (is_pressed) {
Serial.print("foo is active, MOSFET does its thing");
Serial.print("\n");
}
}
}
void loop() {
if(interrupt_flag){
Serial.print("the button is now being pressed: ");
Serial.print(millis());
Serial.print("\n");
}
debounce_button();
foo(is_pressed);
}
3 Answers 3
So, I grabbed a Mega and several pushbutton switches (one of which turned out to be a push-on, push-off, much to my bemusement), and ginned up a small Arduino program to illustrate an interrupt-driven button debounce technique. Please feel free to point out any and all errors ;-)
Here is a small program to debounce any momentary contact switch. The Mega interrupt line (pin 2, interrupt 0) triggers on any low-to-high or high-to-low transition. the interrupt service routine (ISR) simply sets a flag to TRUE and exits. If multiple interrupts occur before the loop() function can service the flag, the second and subsequent ISR calls do nothing (well, they set the flag, but since it was already set...). The loop() code looks for a set flag, and if it finds one, generates a 50uSec pulse on a selected pin, delays 100mSec, resets the flag, and exits. As long as the switch's contact bounce activity ceases before the 100mSec delay expires, one and only one pulse per transition will be generated. YMMV.
/*
Small program to demonstrate switch debounce technique using an interrupt
The ISR is called for low-to-high and high-to-low transitions, and may get
called multiple times per button press. However, the code in loop() ensures
that each transition produces one and only one pulse per transition, assuming
the typical 10-20 mSec max bounce duration.
Note that if the line 'delayMicroseconds(50); //used to show multiple bounces'
is used instead of the line 'delay(100);//ignore bounces for 100mSec', then
multiple contact bounces will each generate their own 50uSec pulse. This is
useful for O'scope diagnosis if you happen to have one.
*/
#include <elapsedMillis.h>
#include <PrintEx.h> //allows printf-style printout syntax
StreamEx mySerial = Serial; //added for printf-style printing
//pin assignments
const int LED_PIN = 13;
const int BUTTON_PIN = 2;
const int BUTTON_STATE_PIN = 3;
//variables
bool buttonState = 0;
bool blinkState = 0;
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
volatile bool Interrupt = false; // indicates whether MPU interrupt pin has gone high
void BtnInterrupt()
{
Interrupt = true;
}
// ================================================================
// === INITIAL SETUP ===
// ================================================================
void setup() {
// configure pins
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(BUTTON_STATE_PIN, OUTPUT); //scope monitor pin
digitalWrite(BUTTON_STATE_PIN, LOW);
buttonState = digitalRead(BUTTON_PIN);
mySerial.printf("Initial button state is %s\n", buttonState == LOW ? "LOW" : "HIGH");
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), BtnInterrupt, CHANGE);
}
// ================================================================
// === MAIN PROGRAM LOOP ===
// ================================================================
void loop()
{
if (Interrupt)
{
digitalWrite(BUTTON_STATE_PIN, HIGH);
delayMicroseconds(50);
digitalWrite(BUTTON_STATE_PIN, LOW);
//delayMicroseconds(50); //used to show multiple bounces
delay(100);//ignore bounces for 100mSec
Interrupt = false;
}
}
Here are two O'scope screen grabs. One shows the pulse train generated by a single button transition with a 50uSec delay, and the other shows the same switch with the 100mSec delay. In both photos, the horizontal time scale is 500uSec/cm
-
Amazing answer. This deserves more likes!Tono Nam– Tono Nam2021年07月24日 01:37:07 +00:00Commented Jul 24, 2021 at 1:37
I think the problem is that you only trigger the interrupt when the button is pressed (interrupt FALLING)
Therefore, there is no opportunity to call debounce when the button is released.
Therefore, there is no way to change the button state variable back to HIGH (I.e. when the button is released).
To verify the hypothesis or gain some other insights, I suggest trying to put a debug statement in your loop similar to the following
If (millis () >= debugTime) {
DebugTime = millis() + 1000: // output every second...
Serial.print("value of button state is: ");
Serial.println(button_state);
// and a few other variables.
}
I will leave it to you to properly declare debugTime (unsigned long) and choose a few other key variables (maybe interrupt flag) to print in the debug message block.
And i think you already have done so, but also delete the print statements in your ISR.
The problem is that you need to check whether the last interrupt was far enough in the past that the output can be considered stable. The most obvious solution to me is to have the ISR record the time it was called, and the main loop can simply check if that time is sufficiently before the current time. The comparison is very cheap, and doesn't require a delay. The "same record interrupt time and then compare" can cover multiple buttons on MCUs like the ATTINY85 which uses a single shared ISR. The disambiguation etc. resides outside the ISR.
&&
in theif
statement looks wrong ... what are you trying to do?