Interrupt Question

I'm pretty new to arduino,so bear with me please if I am doing something stupid...

I am making a speedometer/odometer/in-car metrics thing for a friend. It's got a little LCD screen (The LiquidCrystal library is awesome), and I am using attachInterrupt to listen for button presses to switch between modes.

However, the button doesn't seem to work, it just seems to make the screen flicker. The code I am using is below...

int Setting=0;

//Setup
void setup(){
  //Setup LCD
  lcd.begin(16,2);
  Startup();
  //Load last mode selection//
  Load();
  pinSetup();
  //attachInterrupt(0,Pulse,CHANGE);
  attachInterrupt(1,button1Press,FALLING);
  //attachInterrupt(2,button2Press,LOW);
}
//Loop
void loop(){
  double currSpeed = Speed();
  Distance();
  
  switch (Setting) {
    case 0:
      printOdo(currSpeed);
      break;
    case 1:
      printRPM(currSpeed);
      break;
  }
}

void button1Press(){
  Setting++;
  Setting = (Setting % NumberofSettings);
}

//printOdo - Prints the Odometer readout to the screen.
void printOdo(double currSpeed){
  lcd.setCursor(0,0);
  lcd.print("Speed  ");
  lcd.print(currSpeed);
  lcd.print("mph");
  lcd.setCursor(0,1);
  lcd.print("Distance ");
  lcd.print(Mileage);
  lcd.print("m");
}
//printRPM - Prints the RPM readout to the screen.
void printRPM(double currSpeed){
  int revs = RPM();
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Speed  ");
  lcd.print(currSpeed);
  lcd.print("mph");
  lcd.setCursor(0,1);
  lcd.print(revs);
  lcd.print("RPM");
}

I've taken out the bits that aren't important, to make it shorter.

Anyway, I have no idea what I am doing wrong. Thanks in advance.

I am using attachInterrupt to listen for button presses to switch between modes

Why are you using an interrupt?

int Setting=0;

sp.volatile int Setting=0;

But otherwise, what CodingBadly said

I was under the impression that it was needed because the button may only be pressed for a short amount of time, so the interrupt makes sure that the button press is registered. Like I said, I am new to this, so I could be very wrong.

the button may only be pressed for a short amount of time

Your idea of a "short amount of time" and that of a processor running at 16MHz are quite different.

Even with interrupts (or maybe especially with), debouncing is required. You should also record when the interrupt occurred, and ignore the interrupt if it happens too soon after the last interrupt.

Other than me not being able to make it work, are there any downsides to using the interrupt?

Other than me not being able to make it work, are there any downsides to using the interrupt?

That's not a big enough downside?

Interrupts often lead to complex code, primarily due to the need to respond at any time to the interrupt. Your situation is properly using an interrupt handler - to do nothing more than set a value that gets used later, so there are no real downsides.

At least two…

  1. The added complexity. Getting the details correct can be extremely difficult. Even for seasoned professionals.

  2. Overhead. Entering and exiting an interrupt service handler can be an expensive journey (in CPU time and, to a lesser extent, SRAM used).

Interrupts are like a stick of dynamite. The average person will go their entire lifetime without needing one. But, if you do need one1, you can’t live without it.

1 For example, to shift an especially well anchored very large tree stump.

I'm now going to have to go into the garden and find a large, well-anchored tree stump.

XD

Well-anchored granite boulders are my bane. When it's taken all day to install three fence posts, a few sticks of dynamite would be a very welcome addition to the tool chest.

Interrupts can be tricky. However; I don't think that you should avoid them all together, because they can be useful in multitasking.

Try declaring "setting" as:

volatile int setting = 0;

[edit] Sorry, I didn't see that AWOL already suggested that in post #2 [/edit]

Personally I don't mind interrupts if used properly. As you say, they are useful for catching short button presses, maybe which might happen while updating the LCD.

  • Make sure you have the button on the right pin (D3 for interrupt 1).
  • Make sure you have correct pull-up or pull-down resistors as appropriate.
  • Some debounce code is essential (eg. check millis () and make sure something like 100 have elapsed)
  • Use "volatile" on the interrupt flag or the compiler might optimize away your check of it.

Apart from that you seem to be on the right track. :)

My introduction to using interrupts on the Arduino was using the MStimer2 library. That library make interrupts very understandable and it's a great tool to have in one's tool box.

http://www.arduino.cc/playground/Main/MsTimer2

Some debounce code is essential (eg. check millis () and make sure something like 100 have elapsed)

Do you have an example of using software debouncing with a interrupt driven button? I have not quite figured out how to do it inside a ISR where millis() is frozen.

Lefty

retrolefty: I have not quite figured out how to do it inside a ISR where millis() is frozen.

Frozenish. The value from the most recent interrupt is returned which should be good enough for debouncing.

Do you have an example of using software debouncing with a interrupt driven button?

Let's see what we can come up with using @mash4t's code...

volatile int Setting=0;

void button1Press()
{
  static unsigned long PreviousFire;
  unsigned long CurrentValue;

  CurrentValue = millis();

  if ( CurrentValue - PreviousFire >= 50 )
  {
    volatile int LocalSetting;
    LocalSetting = (Setting + 1) % NumberofSettings;
    Setting = LocalSetting;

    PreviousFire = CurrentValue;
  }
}

I can't find where he defines the "NumberofSettings" variable in his sketch?

Lefty

I could not find it either. I suspect byte is a more appropriate type…

static const byte NumberofSettings = 7;
volatile byte Setting = 0;

retrolefty: Do you have an example of using software debouncing with a interrupt driven button?

Here's an example:

http://www.gammon.com.au/forum/?id=10945

That uses a port expander, but the basics are the same.

retrolefty: I have not quite figured out how to do it inside a ISR where millis() is frozen.

"Frozen" isn't really the word. The millis () function returns the last time the internal counter was updated by an ISR. You can consult that in an ISR like anywhere else. It won't be updated in the ISR, but you shouldn't be sitting around waiting for that anyway.

In my example code in the link above, all the ISR does is set a flag. Working out what to do is done in the main loop (where the millis() is "unfrozen" so to speak). In fact, I simply did a delay(100) there - crude but effective.

retrolefty: I can't find where he defines the "NumberofSettings" variable in his sketch?

Lefty

It is there in the actual sketch, I cut it down a bit for copying and pasting, and must have forgotten it.

Also, thanks for all the help everybody, going to have another go at it today.