Skip to content

Using up to 24 interrupt pins on Arduino Mega.

November 29, 2015

This weekend I finally completed my several months long venture into the Atmega interrupt registry, in attempts to get more than just Arduino Megas’ 3 interrupt pins available for my project (which calls for at least 16 interrupt pins). Along with the interrupts I also tackled the Serial communication issue, in order to use the end result circuit some day as a game controller for a desktop game, but that is a topic for another blog post.

Previously I entertained the idea of using MCP23017 I/O expander for interrupt port expander, but I gave up on that avenue because:

  1. It seemed needlessly complicated to use interrupts via the IO expander
  2. I need those 7 available I/O expanders for other purposes, namely for running 16 leds each.

As usual, everything here is provided freely as is, with no warranties and in hopes it may be useful for someone.

PCINT-registries

Atmega 1280 – the IC running on Arduino Mega – provides a way to run a total of 24 interrupt pins by using PCINT0-PCINT23 -pins.

I used the interrupt pins for reading changes in 8 rotary encoders, which is why I set them up as  pairs.
Since I also needed only 16 pins, I only used PCINT0_vect and PCINT2_vect for my interrupts:

#ifndef ROTARY_ENCODER_H
#define ROTARY_ENCODER_H
struct RotaryEncoder
{
  int pin0;
  int pin1;
  volatile int rotationState;
  volatile int previousState;
};
#endif ROTARY_ENCODER_H

RotaryEncoder encoders[8];

//#define DEBUG

void pciSetup(byte pin)
{
  *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
  PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
  PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
  pinMode(pin, INPUT);
}

RotaryEncoder encoderSetup(int pin0, int pin1) {
  pciSetup(pin0);
  pciSetup(pin1);
  RotaryEncoder *encoder = (RotaryEncoder *) malloc(sizeof(RotaryEncoder));
  RotaryEncoder enc;
  enc.pin0=pin0;
  enc.pin1=pin1;
  enc.rotationState=0;
  enc.previousState=digitalRead(pin0);
  *encoder=enc;
  return *encoder;
}



void setup() {
  Serial.begin(9600);

  encoders[0] = encoderSetup(53,52);
  encoders[1] = encoderSetup(51,50);
  encoders[2] = encoderSetup(13,12);
  encoders[3] = encoderSetup(11,10);
  encoders[4] = encoderSetup(A8,A9);
  encoders[5] = encoderSetup(A10,A11);
  encoders[6] = encoderSetup(A12,A13);
  encoders[7] = encoderSetup(A14,A15);
}

That set the pins up as interrupt listeners, but it does not yet have anything that listens for the changes:

void interruptChange(RotaryEncoder *encoder) {
  int i0val = digitalRead((*encoder).pin0);
  int i1val = digitalRead((*encoder).pin1);
  if (i0val != i1val) {
    if (i0val != (*encoder).previousState) {
      (*encoder).rotationState=(*encoder).rotationState-1;
    } 
    else {
      (*encoder).rotationState=(*encoder).rotationState+1;
    }
  }
  else {
    (*encoder).previousState = i0val;
  }
#ifdef DEBUG
    Serial.println("Interrupted: ");
    Serial.println((*encoder).pin0);
    Serial.println((*encoder).pin1);
    Serial.println((*encoder).rotationState);
#endif
}

ISR(PCINT0_vect)
{
  interruptChange(&encoders[0]);
  interruptChange(&encoders[1]);
  interruptChange(&encoders[2]);
  interruptChange(&encoders[3]);
}

ISR(PCINT2_vect)
{
  interruptChange(&encoders[4]);
  interruptChange(&encoders[5]);
  interruptChange(&encoders[6]);
  interruptChange(&encoders[7]);
}

Note that this implementation means that any time any of the PCINT0_vect interrupt pins fires, the whole 8-pin vector will be observed for changes. As far as I can tell, this does not cause any problems, but no doubt there will be some. Please educate me if you know of something to beware here.

I tested this with running a single rotary encoder wired to randomly selected port pair. What I did not realize until much later is that if I did not ground the other pins, they were essentially floating, and would randomly read as high or low. Since the whole PCINTx_vect fires all at once, the floating pins caused ghost readings and major headaches. Grounding those pins solved this issue.

I debounced the rotary encoder signal, but for some reason I found that my circuit (below) worked better than the schematic I started with (https://hifiduino.wordpress.com/2010/10/20/rotaryencoder-hw-sw-no-debounce/). At this point I’m willing to concede that I did the circuit months ago and have no recollection why the wiring is like this – as opposed to the diagram in the blog.
This way my final proof of concept setup for the encoders & multiple interrupt pins looks like this:

usb_serial_duplex_bb

The resistors are 10 kohm and capacitors are 10 uF.

Advertisements

From → Arduino, Electronics

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: