I've got a question about arduino Interrupts. I'm making a robot costume for my 2 boys this year and "bedazzled" it with a programmable RGB LED strip from SparkFun. My sketch does a simple chase sequence in the loop() using adafruit's NeoPixel library. I've also attached a momentary button to pin2 and set the pinMode to INPUT_PULLUP. I've got an UNO Rev3 board and called attachInterrupt with interrupt 0:
My incColor() interrupt handler toggles a direction variable and cycles a myColor enum:
enum colorToUse{
RED,
GREEN,
BLUE,
};
volatile colorToUse myColor = RED;
volatile int direction = 0; // 0 -> foward and 1 -> reverse
void incColor()
{
detachInterrupt(0);
int sensorVal = digitalRead(2);
if (sensorVal == HIGH){
direction = !direction;
switch(myColor) {
case RED:
myColor = GREEN;
break;
case GREEN:
myColor = BLUE;
break;
case BLUE:
myColor = RED;
break;
}
}
attachInterrupt(0, incColor, CHANGE);
}
The problem I'm having is that pressing the momentary button sometimes changes the direction global variable only, sometimes changes the myColor global variable only, and sometimes changes both. Shouldn't it change both every time?
I thought maybe the interrupt was being called while I was already in the interrupt handler, so I added the detachInterrupt/addInterrupt. But that didn't seem to change anything. What am I missing here? I even added some button de-boucning code, but nothing changed. (Plus, the attachInterrupt() page says millis() isn't incremented in an interrupt handler, so that wouldn't have worked anyway, right?)
The button is probably bouncing. Add a bit of code so that the interrupt is ignored if it occurs within, say, 25 milliseconds of the previous one. You definitely don't need detachInterrupt/addInterrupt in the interrupt handler.
Do you mean to say you are using an interrupt for a pushbutton?
No wonder you have a problem!
Do not use interrupts for pushbuttons. Problems solved.
This is getting to be my "hobby horse", but interrupts are a nasty distraction to "newbie" programmers. Interrupts can be - and should almost always be - completely ignored and left to the ambit of certain library routines such as those for keeping time.
They are basically never needed for a "HID" interface and in particular should never be used for pushbuttons where there is a need to de-bounce. Interrupts are for things that need a fast response, meaning computer-fast, that is, microseconds.
In this case (as so often happens), you have been confused by the idea that an interrupt will "interrupt" the task that you are presently doing. In fact and to the contrary, it is a critical point of design that your main program as such should never know that an interrupt has occurred at all because the interrupt has left the operating state of the main routine absolutely unchanged. Interrupts are intended only for events that occur in "computer time"; that need to be attended to within microseconds. By the same token, they have to be dealt with and completed within fractions of a millisecond - if only because other such important events may require attention.
General point on which I tend to harp but remains important: Do not try to use interrupts unless there is a need for them.
There's nothing wrong with using an interrupt for a pushbutton.
Interrupts are for things that need a fast response
Nope. Interrupts are for any asynchronous events that need a response. The interrupt routine itself should, of course, not take too long to execute but that is true whether it is the pushbutton on a robot costume or a meltdown alarm in a nuclear power station.
If the application requires low power and the processor is put to sleep between tasks, the only way to wake it up is an interrupt.
I think that the button is bouncing. You have several ways to solve this problem, my favourites are:
Use an analog or digital de-bouncing circuit that is very very simple and use the same code but without the detachInterrupt/addInterrupt in the interrupt handler;
Add some button de-bouncing code but in this case i don't think that is a good idea with the interrupt.
Sorry for my bad english
MasterPi:
2. Add some button de-bouncing code but in this case i don't think that is a good idea with the interrupt.
You are entirely correct about the bounce problem. The proper solution is the other way around - you use de-bouncing code but it is not that debounce code isn't a good idea with an interrupt where it is certainly possible, but rather that an interrupt is a very bad idea for something that bounces, such as a pushbutton. That was my point. De-bounce code should be by polling in the main loop.
an interrupt is a very bad idea for something that bounces, such as a pushbutton. That was my point.
And you're still wrong.
De-bounce code should be by polling in the main loop
Why?
It's perfectly simple to debounce in the interrupt routine.
On each interrupt, compare the current value of millis() to the last recorded value. If it is less than a preset debounce time (e.g. 25ms) then just return - ignore the interrupt. Otherwise set the last recorded value to millis() and do whatever needs to be done in the interrupt routine (cycle the LEDs, switch the direction or whatever).
De-bounce code should be by polling in the main loop
Why?
It's perfectly simple to debounce in the interrupt routine.
On each interrupt, compare the current value of millis() to the last recorded value. If it is less than a preset debounce time (e.g. 25ms) then just return - ignore the interrupt. Otherwise set the last recorded value to millis() and do whatever needs to be done in the interrupt routine (cycle the LEDs, switch the direction or whatever).
Utter nonsense. A few lines of extra code is a lot better than adding more hardware.
The thing just cycles some LEDs for heaven's sake! What are you trying to save?
I see several responses saying to debounce the button. I have to admit, I'm a software guy, so debouncing in software via millis() is my comfort zone. But attachInterrupt() - Arduino Reference specifically says millis() is not incremented in an interrupt, so a software debounce seems infeasiable. Is that page out of date or just wrong or am I missing something?
The example for attachInterrupt() is specifically for flipping a state variable based on a button press. My loop() spends its time communicating with the LED strip doing its thing. So polling the button state constantly would unnecessarily complicate my code. Being a momentary button, it's only HIGH for a few milliseconds. Hence using an interrupt to guarantee I would catch the button press.
P.S. Almost 24 hours is "going all quiet"? I do have a (semi) life ya know.
You can read millis() in an ISR. You can check when you last got a press. If that was within 10 mS of the previous press, discard it (debounce). Otherwise set a flag saying there is a new press.