Code flow question for buttons: Interrupts vs Polling

I am learning the pros and cos of coding with interrupts on buttons VS coding with polling of buttons; I'm trying to decide which one is more reliable/appropriate for my specific practical situation.

Situation
In my current experiment, I'm working with:
--an Xbee: Receives data from a remote Xbee every N seconds (at constant intervals; can be set anywhere from 100 millisec up to 1 hour)
--an SD card: Stores the wirelessly received data
--an LCD: Displays the last five datapoints received/stored, as well as menus to browse or do miscellaneous things with the data.
--SIX buttons: Four for up-down-left-right navigation, One for select, and One for power on-off.

I want to implement the Xbee data to be read reliably and constantly, but also I want the LCD display to be updated based on any buttons pressed at a given moment.

QUESTION
I am trying to figure out what would be the ideal way to implement the code flow for this Buttons-and-Data combination.
Which of the below 3 approaches would you go for?:
Polling the buttons constantly; or Interrupts on the buttons; or Something else?

(Option A) Polling the buttons constantly
--Constantly read data from Xbee if available on the serial line and store it to the SD card, in the main loop.
--Constantly poll the 6 buttons with digitalRead(), also in the main loop.
--If any of the 6 buttons digitalReads HIGH, then the main loop runs the appropriate function corresponding to button pressed and based on which menu the LCD interface is in right now.
--Main loop continues to next iteration of reading data from Xbee and polling the buttons.

(Option B) Interrupts on the buttons
--Attach interrupts to each of the 6 button pins with a separate ISR for each.
--Constantly read data from Xbee if available on the serial line and store it to the SD card, in the main loop.
--Upon any of the 6 button pins interrupted, the ISR for each toggles a global boolean variable, one corresponding to each of the button states.
--Main loop checks if any of the 6 global boolean variables of the button states are TRUE.
--If any of those state variables have a value of TRUE, the main loop runs the appropriate function corresponding to which button state and based on which menu the LCD interface is in right now.
--Main loop continues to next iteration of collecting data from Xbee and checking for TRUE on the button state global variables.

(Option C) Something else?
--Something even better/cleaner perhaps?

The difference between polling and interrupts in your case is that the interrupt method will have the ISR triggered immediately (within 100 nanoseconds, anyway) when the switch is pressed, while the polling method will need to wait for loop to run again.

In either case, the LCD display will not be updated any sooner.

As long as loop() executes often enough, polling is good enough. How often is "often enough", of course, is the critical question.

From your description, it appears that loop() will execute many, many times per second. Serial data arrival will be orders of magnitude less frequent than loop() will run, so most iterations of loop will find that there is no serial data to process.

As long as the code to update the LCD display does not take too long, polling will be good enough.

In my opinion, of course.

Another thing to consider is if you're using a standard Uno/Duemilanove/etc. you only get interrupt support on 2 pins (2 & 3); you'll need a Mega to support interrupts on all 6 buttons.

Chris

@PaulS: Thank you very much for the comparison of the two for this situation.
One other question/doubt:
If I were to go with the interrupt approach instead, would the steps I described in Option B by correct/optimal? Specifically... After an interrupt happens on a pin, the ISR merely sets TRUE for a global boolean variable corresponding to that button. And the loop, every iteration, checks if any of the 6 global boolean variables (one for each button) are TRUE.
Generally speaking, Is the advantage of the interrupt method because of the fact that I'm simply checking for TRUE on the values of these boolean variables every time, instead of checking/polling for a HIGH on digitalRead of pins every time... I'm guessing the latter is more computationally time-intensive?

@Chris: Thanks; I'm using a Mega indeed. Although I suppose I could also use hardware interrupts for additional ones.

Specifically... After an interrupt happens on a pin, the ISR merely sets TRUE for a global boolean variable corresponding to that button. And the loop, every iteration, checks if any of the 6 global boolean variables (one for each button) are TRUE.
Generally speaking, Is the advantage of the interrupt method because of the fact that I'm simply checking for TRUE on the values of these boolean variables every time, instead of checking/polling for a HIGH on digitalRead of pins every time... I'm guessing the latter is more computationally time-intensive?

The code for the digitalRead function is available for you to look at. The function needs to map the pin number that you specify to a particular bit on a port, and then test that port, and set an int to the bit in the port variable. It's not the fastest function in the world, and there have been plenty of attempts to speed up digitalRead. Portability becomes an issue. If that is not an issue for you, direct port manipulation is faster than digitalRead.

Interrupts have advantages and disadvantages. For fastest response, when you don't need to do anything else, a tight loop polling the pin would be fastest.

However when displaying data on an LCD, probably a microsecond response time is not required.

This was covered at some length here:

http://arduino.cc/forum/index.php/topic,70625

To test multiple pins, whilst doing something else useful, probably interrupts are the way to go. The possibly fairly length delays whilst writing to the LCD, or updating the SD card, might cause button presses to be missed.

Option A is OK if you can fairly easily poll the buttons sufficiently often in loop(), but gets cumbersome if you are doing a lot of work in loop() and want to split it out into separate functions that take a while to execute, because you will have to call the polling function in those functions too. As has been pointed out, if you call library functions that take a long time to return, you might miss some pushes, although this is more of a problem with rotary encoders than push buttons.

Option B isn't really practical because you would need 6 interrupts.

Option C is to set up a regular timer interrupt and poll the buttons in that interrupt. In systems that use multiplexed LEDs or 7 segment displays, the same interrupt can be used to do the multiplexing. You can also save pins by sharing some of the multiplex pins between the displays and the buttons.

I use option A in simple sketches, and option C otherwise.

1 Like

dc42:
Option B isn't really practical because you would need 6 interrupts.

You've got a few interrupts on the Mega haven't you? In any case you can do a wired-or to share one interrupt amongst multiple wires:

1 Like