3
\$\begingroup\$

I have 6 buttons on my printed board, and I'd like to use interrupts on them all, but they don't correspond to the interrupts listed on the specs.

Is it true that you cannot choose arbitrary pins to use as interrupts on Arduino boards (specifically the Mega 2560)? If not, I am a bit sadface - any pragmatic workarounds?

\$\endgroup\$

4 Answers 4

4
\$\begingroup\$

You can do what Majenko suggested, but all it takes is a diode per button to make the ORed line. I'm assuming the buttons are normally open and tied between ground and a pullup resistor. The ORed line is in addition to the individual button lines, which still need to be wired to processor pins. However, the advantage is that the ORed line will go low when any of the buttons are pressed. This can be used to wake the processor or cause a interrupt, which then has to look at the other lines to see which buttons are really pressed.

Another way is to not use button interrupts at all. Just scan the button lines every few ms. The rough rule of thumb is that 50 ms or less feels instantaneous to a human user. In other words, if you press a button and whatever it does is delayed by 50 ms, it still feels instantaneous to you.

If the point is to save power, you can actively drive the high side of the pullups. Keep them low most of the time so there will be no current even if a button is pressed. Turn on the pullups for maybe 10 µs before sampling the buttons, then turn them off again. This can be done in a short routine the processor wakes to every 10 ms, for example, when nothing is otherwise going on. The average power will be low since it will only be on for a few 10s of µs every few 10s of ms.

\$\endgroup\$
1
  • \$\begingroup\$ Cheers Olin! Yes the motivation is the apparent latency. When I press a button, I draw some pixels on the LCD. While the redraw is in progress, it's quite easy to hit the button again and have it ignored. Interesting about the power consumption idea! \$\endgroup\$
    – aaaidan
    Commented Nov 28, 2011 at 3:04
3
\$\begingroup\$

I hope I am not to late with that "black magic" solution :)

First of all, to set things clear, what you need is possible and not hard to achieve, still there is some good news and bad news. Bad news is that your choice can't be completely arbitrary and the good news is that you can choose from an additional 24 pins (the ones that are marked with PCINTx in the data sheet, PCINT0 all the way PCINT23)

Also bad news is that, some of the following may seem a little foggy at first (it the case is that your not so experimented with micro controllers) but good news is that you are going to a have a much better view of what your dealing with once you get the job done :)

A few basics regarding this matter, which you probably already know, or heard of: PCINTx stands for "Pin Change INTerrupt" and if enabled, on a certain pin, will call an interrupt each time the state on that pin has changed (from high to low/ from low to high).

Second of all, your approach should look something like this:

  1. enable pin change interrupt on the 6 pins that you need
  2. enable global interrupts
  3. when an interrupt is called, check the state of your input to determine the change and save the button state

You're gonna need the datasheet, so here's a shortcut: http://www.atmel.com/dyn/resources/prod_documents/doc2549.PDF :)

for step 1: see register PCICR on page 115 this is Pin Change Interrupt Control Register and also registers PCMSK2, PCMSK1, PCMSK0 to specify exactly which pins are enabled for this special function

for step 2: I'm pretty sure you know how to do this since you posted this question :) - if not call: sei()

for step 3: This is the tricky part. You have 3 interrupt vectors (or you can call them callbacks) one for each port (each port has 8 pins). If multiple pins of the sam port are set as triggers (from the mask registers PCMSKx, at step 1) the same callback will be called, so, be sure to check register PINx to see exactly which one has changed.

That's about it :) a few more things though: this is also applicable for ArduinoUno (atMega328p). This is a very useful feature - if you consider power consumption, you can hold the controller in sleep and perform wake up by interrupt. I have used this feature for the reading input from an RC receiver and you can get real good timings (interrupt driven, so d'oh :P )

Here's a link with GREAT INFO regarding this matter, probably should have started from here: http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=71109

I, myself always prefer a software solution instead of a hardware solution, it's usually more flexible and most of the time the results provided are cheaper and faster to get at than hardware changes (also less messy) :). But keep in mind that anything that is hardware is MUCH MUCH MUCH more faster (thus justifies the costs and messiness and lack of flexibly). However, in this case right here, I don't really think you need that kind of speed, so go for SW :)

Hope I'm not to late, but just in case I am, I hope it will be useful maybe another day :) You can find a nice implementation for what I believe you need on the second link (the one from avrfreaks)

This is a link I picked up from another thread on stackexchange, sums up the whole idea pretty well http://www.me.ucsb.edu/~me170c/Code/How_to_Enable_Interrupts_on_ANY_pin.pdf

Cheers, Dan

\$\endgroup\$
1
  • \$\begingroup\$ Oh great! I will work through this approach as soon as possible! Chur! \$\endgroup\$
    – aaaidan
    Commented Jan 12, 2012 at 3:02
2
\$\begingroup\$

You could wire all your buttons through an OR gate (for active-high) or AND gate (for active-low) to an interrupt pin as well as to the normal non-interrupt enabled pins, so that when any button is pressed the interrupt is triggered.

Once that interrupt has been triggered you can then examine the other inputs to determine which button it was that caused the interrupt.

You could (if you are running active-low) use some diodes for the OR-gate (wired-OR) - (thanks Olin).

Simple diode logic

In this one the ANY signal goes low when any switch is closed, along with that switch's IN line, and stays low until all switches are open. It's impossible to tell if a switch is closed when one is already closed.

If you want to get even more complex, and improve the usability of the system even more, you could implement "change notification" on all the switches. This is essentially what is inside the AVR to trigger the interrupt, but there is no reason you couldn't implement it outside and use an interrupt pin as a change notification pin.

This would have the big advantage that it would trigger a pulse on the interrupt pin, and hence the interrupt, when any of the inputs changes state - be that high-to-low or low-to-high - so you can know when any of the buttons has been pressed or released - and it would also handle combinations of buttons - press one and you get a pulse; press a second and you get another pulse; release the first and you get a third pulse.

Change notification takes 2 D-type flip-flops and an XOR gate per input, and they are all tied together with a big OR gate. It also needs some form of clock signal - the slower the clock the wider the pulses. Yes, bulky to do with individual chips, but could be worth it for the added benefits it gives you.

For an example circuit you could look at the PIC's input change notification circuit diagram - page 7 of this document:

Change notification circuit

You can ignore the AND gate - that's just to allow the enabling/disabling of change notification on a per-pin basis. The ----|> C lines come from the clock I mentioned above.

I don't know about the Mega, but on the UNO you get 2 interrupt pins - pins 2 and 3. All others require polling.

\$\endgroup\$
1
  • \$\begingroup\$ Wow, that change notification design is awesome! \$\endgroup\$
    – aaaidan
    Commented Nov 28, 2011 at 3:06
0
\$\begingroup\$

Yes, that's true. These are hardware interrupts and as such they are tied to specific pins. On the Mega 2560 these are pins 2, 3, 18, 19, 20 and 21 as stated in the specs.

If you wanted to save interrupt pins or had more buttons you could go with Majenko's or Olin's adivce. Those require you to reconfigure your circuit, and in that case you might as well do the simplest reconfiguration of all - just route your button wires to the correct pins.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for confirming the limitation. I guess I was secretly hoping someone would say you can use <black magic> to reassign the interruptness to (an)other pin(s)... \$\endgroup\$
    – aaaidan
    Commented Nov 28, 2011 at 3:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.