Hi,
I've wired up a momentary push button to pin2/interrupt0 on an Arduino Uno R3, and registered an interrupt function using attachInterrupt(0, ..., CHANGE). When I press the button I get multiple interrupts, which is fine since it bounces, however I get multiple interrupt calls while LOW and then while HIGH, which confuses me given that I've registered my interrupt routine with CHANGE.
I'm just starting out and I'm trying to understand why such a thing is happening. (I can pretty easily figure out how to debounce this to just the events that I care about.)
Here's the code:
/*
* Log state of digital pin 2, reporting how it changes.
*
* CC0: Public Domain
* To the extent possible under law, Drew Folta has waived all copyright and
* related or neighboring rights to this work.
*/
/**********************************************************************
* LIBRARIES
*/
#include "Arduino.h"
/**********************************************************************
* CONFIGURATION
*/
// how fast to report
const uint32_t REPORT_BAUD = 115200;
// how long to gather interrupts (after first one)
const uint32_t REPORT_WINDOW_MS = 1000;
// max number of interrupts to report
const size_t REPORT_MAX_ENTRIES = 32;
// characters to use for showing state of the pin
const char REPORT_IGNORE = ' ';
const char REPORT_LOW = 'x';
const char REPORT_FALLEN = 'x';
const char REPORT_HIGH = '-';
const char REPORT_RISEN = '-';
/**********************************************************************
* GLOBALS
*/
struct {
uint32_t when; // when interrupt happened
bool pin2; // state of digital pin 2
} report_entries[REPORT_MAX_ENTRIES];
volatile uint32_t report_timeout = 0; // when we stop reporting
volatile size_t report_next = 0; // index of next entry
volatile size_t report_overflows = 0; // number of times we've gone
// past REPORT_MAX_ENTRIES
/**********************************************************************
* REPORTING
*/
void report_clear() {
report_timeout = 0;
report_next = 0;
report_overflows = 0;
}
// interrupts should be disabled when calling this so that the report_next
// doesn't change while we're filling out the structure
void report_entry(bool pin2) {
uint32_t now = millis();
if (! report_timeout) {
report_timeout = now + REPORT_WINDOW_MS;
}
report_entries[report_next].when = now;
report_entries[report_next].pin2 = pin2;
report_next++;
if (report_next >= REPORT_MAX_ENTRIES) {
report_overflows++;
// preserve the first log entry, since we diff aaginst it
report_next = 1;
}
}
void report_print(void) {
char buffer[32];
size_t last;
Serial.print("======================================== REPORT");
if (report_overflows) {
Serial.print(" -- ");
Serial.print(report_overflows);
Serial.print(" overflows");
}
Serial.println("");
for (size_t e = 0; e < report_next; e++) {
last = e - 1;
// index
snprintf(buffer, 32, "%2u ", e);
Serial.print(buffer);
// pin2
snprintf(buffer, 32, " ");
buffer[0] = report_entries[e].pin2 ? REPORT_HIGH : REPORT_LOW;
if (e && (report_entries[e].pin2 != report_entries[last].pin2)) {
buffer[0] = report_entries[e].pin2 ? REPORT_RISEN : REPORT_FALLEN;
}
buffer[1] = '\0'; // just in case;
Serial.print(buffer);
// when
snprintf(buffer, 32, " %6lu", report_entries[e].when);
Serial.print(buffer);
// diff against last
if (e) {
snprintf(buffer, 32, " %5lu", report_entries[e].when - report_entries[last].when);
Serial.print(buffer);
}
else {
Serial.print(" ");
}
// diff against first
snprintf(buffer, 32, " %5lu", report_entries[e].when - report_entries[0].when);
Serial.print(buffer);
Serial.println("");
}
Serial.println("");
}
/**********************************************************************
* INTERRUPTS
*/
void int_0(void) {
report_entry(digitalRead(2));
}
void int_start(void) {
attachInterrupt(0, int_0, CHANGE);
}
// we don't want to stop all interrupts, since Serial will need a clock to get
// data out
void int_stop(void) {
detachInterrupt(0);
}
/**********************************************************************
* EXECUTION
*/
void setup() {
Serial.begin(REPORT_BAUD);
report_clear();
pinMode(2, INPUT_PULLUP);
int_start();
Serial.println("======================================== BOOTED");
Serial.println("");
}
void loop() {
if (report_timeout) {
if (millis() >= report_timeout) {
int_stop();
report_print();
report_clear();
int_start();
}
}
}
(It's a little more complicated then necessary since it's a simplified version of attempting to log all digital pins via the pin change interrupts.)
Here is a sample run:
======================================== BOOTED
======================================== REPORT
0 x 667 0
1 x 724 57 57
2 x 724 0 57
3 - 869 145 202
4 - 869 0 202
Any idea why I'm seeing multiple concurrent LOW readings even with attachInterrupt(..., CHANGE) ?