I'm building kinda "ball game" for a total of 6 players based on these per-player specs:

  • 1 stepper motor with 4 pins
  • 2 end stops (start & finish) per motor
  • 1 finish LED
  • 3 holes to score, resulting in slow, medium or fast motor movement

Additionally, there's a "master reset" to move all motors back to their starting end stops.

Because of my initial assumption, 54 pins would be enough, I didn't realize actually requiring 60 pins (24 for motors, 12 for end stops, 6 for LED, 18 for holes). I'm also neither an expert at microelectronics nor C programming language, but my understanding of the Mega's pins is that I need 12 + 18 PCINT capable pins (end stops + holes) while only 16 pins are available.

So far, the only information I could find suggests buying an Arduino Shield to resolve this by providing additional 16 PCINT pins. But being connected via I2C, I suspect delays resulting in unequal treatment of player scores.

How to get at least 30 PCINT capable pins, treated equally by Arduino? In case there's no need for the PCINT requirement, which pins can be used as an alternative?

  • If you write good code you won’t need an interrupt for any of that. Do you think that switch can make and break in less than a millisecond? For comparison blinking your eyes takes about 200ms. Don’t use delay for timing and you won’t need any interrupt for this.
    – Delta_G
    Sep 9 '20 at 16:01
  • If you want more PCINT you can do it yourself outside the chip. Just AND or OR (depending on if you want active LOW or HIGH) multiple signals together and feed the result into a single interrupt pin in addition to each signal into its own digital pin, then when the single interrupt occurs poll the other digital pins to see which changed.
    – Majenko
    Sep 10 '20 at 9:05
  • @Delta_G How to? The current code within Arduino's loop() actually does use delay between setting e. g. the steppers to allow for kinda "cooldown". It's not that I want 1 ms response time, it's more in terms of "chance equality" between all players.
    – Yoda
    Sep 14 '20 at 17:54
  • @Majenko So, you're suggesting me to, e. g. combine all 6 PCINT inputs into 1 and upon the interrupt, poll all 6 to find the right one? What about player #2 hitting the same hole (on his table, not player #1's table)? Wouldn't that be swallowed in vain then?
    – Yoda
    Sep 14 '20 at 18:00
  • I wouldn't know. I have no idea what holes you're talking about, nor how a player "hits" them.
    – Majenko
    Sep 14 '20 at 18:03

The Atmega2560 has a total of 24 pins associated to the PinChange interrupt. You cannot change that. It is an internal property of the microcontroller.

As you already have seen yourself, you need to use extra chips to extent the available IO pins. There are different options to consider:

  • IO-Port-Expanders (available for I2C or SPI) (for example the PCF8574. Don't know, if that is the best, just the port expander, that I also used myself)
  • Shift-In (for inputs) or Shift-Out (for outputs) registers (often used are the 74HC595 (Shift-out register) and the 75HC165 (Shift-In register). I often used this tutorial as reference)

As you don't want to poll these chips all the time, I suggest, that you use one, which provides an additional interrupt pin. That pin will change its value, when one of the input changes. You could then connect this pin to one of the interrupt pins of the Atmega2560. When the interrupt triggers, you can read the states of the input pins.

But being connected via I2C, I suspect delays resulting in unequal treatment of player scores.

Indeed that wouldn't be good. So you should divide the pins up by their purpose, not by their association to a player. For example all end stop inputs, or all outputs for the stepper motors. Then you need to access, what timing requirements each group has. Then you can take the group with the lowest requirements and check, if and which chip would be able to handle the data fast enough. For example the "finish LED". You didn't specify, what exactly it does, but I guess, that it is not really time critical. So you could handle all finish LEDs via a single shift-out register.

Or take the inputs for the holes: I guess here you have some sensor, that triggers, when a ball passes it. Check how long the sensor get's triggered. Most likely (depending on the type of sensor) this depends on the speed of the ball. Take a speed, that you want to consider maximum (for example the pitching speed of a good baseball player, as these are really high), and calculate, how long the resulting pulse will be. The chip, that you choose, might be fast enough for this. So you could handle all ball hole inputs over extra chips (meaning chips providing extra input chips, like a Shift-In register or a port expander).

In case there's no need for the PCINT requirement, which pins can be used as an alternative?

Every pin capable of digital IO. That's nearly every pin on the Arduino Mega. Though that means, that you cannot use interrupts for them and you have to poll then constantly. That might be ok for you. It really depends on your exact timing requirements.

I thought polling was a "avoid where possible"-area?!

Yes and no. First you asked for the possibility of using other pins, so I mentioned that above. Though doing that means using no PinChange interrupt. Generally polling is bad, when

  1. you spend too much time on it, leaving no time for other functions of the code, or
  2. if the events, that you are polling, are too short for your polling code.

That can happen in the cases, that

  1. you use blocking code to poll (like waiting with a while loop for a button press), which blocks all other functions from executing.
  2. your code needs a long enough event to register it (for example the signal from your hole sensors might be very short. If your polling interval is bigger than that, you will loose some events)

If you stay inside of the confinements and use non-blocking code, there is nothing bad with polling.

Let's take the often used Bounce2 library for Arduino. It helps you with reading buttons and handles the debouncing for you. It does not use any interrupts. You are expected to call Bounce.update() often enough to catch all button presses, which is typically a few ms at max. But since the Arduino Mega runs on 16MHz, it is capable of way faster speeds than the few ms. Calling Bounce.update() often enough is easy, when you don't write code, that stays a long time to execute. So no delays, except for maybe very short ones. Instead you can use the coding style of the BlinkWithoutDelay example, that comes with the Arduino IDE. It uses the millis() function as a time base to check repeatedly, if it is time to do something, and only do so, if it really is time for it. Much like you are baking a pizza by putting it into the oven and then regularly watch at your clock, if it is time to get the pizza out of the oven. You don't want to sit in front of the oven waiting for pizza. The analog of doing that is writing blocking code with delay().

So all in all about polling vs interrupts: It depends on your requirements. Fast response times or short events often need interrupts, though you need to cope with limited usable pin numbers and different interrupts (which is a bit more complex to code). Polling - when done right - can give you big freedom and rather easy handling, while saving interrupt sources for more important functions. Though it poses bigger limits on response times and the duration of events. Also it depends strictly on the rest of your code.

Note, that in the above, terms like "time critical", "fast response times" and such are more meant for the region of a low number of microseconds. Timings in the millisecond range can easily be done by using the mentioned non-blocking coding style (either with millis(), or for greater resolution and accuracy at low numbers of milliseconds micros()). The principle is always the same and there are tons of tutorials about it on the web and even many questions and answers on this site. So you can google for this, to get a more detailed tutorial.

  • Sorry, my obligations prevented a sooner reply.. As I've got zero knowledge about I2C/SPI beyond their basic working, can you please recommand such an IO expander and/or a shift-in/-out register with such an additional interrupt pin? Regarding the "finish LED", the winning player's LED shines after his avatar crosses the finish line (and all 6 LED together upon reset-button press) - so yes, not time-critical at all. :) Yes, hole inputs work via sensor triggers. But I don't get what you mean by "extra chips" :( And regarding polling: I thought polling was a "avoid where possible"-area?!
    – Yoda
    Sep 14 '20 at 18:16
  • 1
    @Yoda I added more information to my answer, including examples for possible parts (IO expander, Shift-in register, Shift-out register) and a section about polling vs interrupts. With "extra chips" I meant chips like the above, to get more IO pins.
    – chrisl
    Sep 14 '20 at 20:37
  • thanks for your help, time and especially your useful explanations! I'll go for polling then, trying millis() & micros() first & falling back to 75HC165/74HC595 or PCF8574 if that doesn't work out. Again, thanks a lot for your valuable help! 👏🏻👍🏻
    – Yoda
    Sep 15 '20 at 5:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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