Go Down

Topic: 3 interrupts on Uno (Read 581 times) previous topic - next topic

asuryan

Hi there!

Im currently working on a project where I need a rotary encoder and an RPM sensor. Both of them need interrupts so they need to be 3 interrupts but the Arduino UNO only supports 2.

Any ideas for a workaround?

Best and thanks in advance!
Visit my blog! http://electronics.scriblab.de

wvmarle

The Uno supports way more than two interrupts. That's just the two "external interrupts". All pins have pin change interrupts, for starters.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

asuryan

Thanks for your reply!

I didnt know that!

So I can just use attachInterupt with "CHANGE" to any digital pin of the UNO?

I think the encoder library uses "CHANGE" but for the RPM I would need "RISING" or "FALLING" so these have to be on digital pin 2 or 3 right?
Visit my blog! http://electronics.scriblab.de

wvmarle

No, pin change interrupt is a different thing than regular interrupt. Tutorial here.

Two major differences:
- these interrupts are enabled per pin, but work on a pin group basis. So one for PORTA, one for PORTB and one for PORTD. Total three interrupt vectors.
- you have to keep track of pin state yourself as they get triggered both rising and falling, and if you have multiple pins with interrupt within one of those ports, you have to check which one it was that triggered the interrupt, so choose your pins carefully.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

ChrisTenone

#4
Jun 25, 2018, 09:11 am Last Edit: Jun 25, 2018, 09:12 am by ChrisTenone Reason: I forgot to type the word "Mega:.
Or you could use a Leonardo, Micro or other board that uses the ATMega 32u4 processor. That processor has 5 external interrupts, or an ATMega 2560 board, such as the Arduino Mega which has 6.

Review the Arduino reference page for attachInterrupt().
Atmosphere carries combustion vapors to places where they will do good instead of harm - Mike Faraday's 'History of a Candle': https://www.youtube.com/watch?v=6W0MHZ4jb4A

Whoops ::)

wvmarle

Come to think of it, this is one of the few things where the ESP8266 beats even the ATmega2560 - it has external interrupts for all pins except GPIO16 and the analog pin, so 10 total.

Pin change interrupts will do very nicely for the encoder. Just make sure they're on the same port, and when either of the pins changes value your ISR gets called. That one ISR can then take care of both pins, may make your code a bit simpler even.

Use one of your external interrupts for the RPM sensor.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

hammy

Connect two of your lines via diodes to one interrupt pin - then either of the two devices will generate an interrupt. Upstream of one Diode connect that to another digital pin - then when an interrupt occurs , in your service routine you can check which device caused it by looking at the other digital pin .

MorganS

Use the Encoder library for the encoder. It does not require either pin to be interrupt pins. It does work a little better if one pin is an interrupt pin.
"The problem is in the code you didn't post."

wvmarle

Won't work.
1) you need a pull-up resistor as well.
2) you can't read the status of the pin that's connected to the interrupt only, and for proper quadrature encoder reading you need to know the status of both pins.

If you're suggesting to connect both the RPM and encoder to one interrupt pin, it's even worse:
3) when the encoder pin is low, you can't read RPM any more.
4) while the RPM signal is in its low state, you can't read the encoder.

(depending on the direction of the diodes the high/low may be reversed, this doesn't solve any of these issues).

Multiplexing pins can sometimes be done, but you have to be really careful about what you're doing on the same pin. On an ATtiny85 project I ran out of pins, needed an extra one, in the end used one pin as both a digital output and analog input. Works like a charm, but only because both parts are fully tolerant of the other: the digital output is for the EC pin of my EC sensor, the analog in on the same pin is an NTC. It's so far the only time I successfully multiplexed a single pin to handle two sensors.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

hammy

Yep fair enough , you do need the pull up.
You are right I hadn't considered the case of two simulatanious interrupts which might both be valid .

Ho
Hum
Lol

asuryan

#10
Jun 25, 2018, 04:28 pm Last Edit: Jun 25, 2018, 04:30 pm by asuryan
Thanks guys for all the input you gave me.

I decided to go this route:

1. Im going to use the Encoder library on pin 2 & 3 (because I tried eg. 1 & 3 and the encoder didnt really work)
2. The RPM sensor comes on pin 9

Im going to setup and use pin 9 like this:

Code: [Select]
volatile int rpmCounter = 0;
int rpm = 0;
unsigned long lastMillis = 0;

void setup()
{
  interruptPin(9);
}

void loop()
{
  if (millis() - lastMillis == 1000)
  {
    rpm = rpmCounter * 30;
    rpmCounter = 0;
    lastMillis = millis();
  }
}

void interruptPin(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
}

ISR (PCINT0_vect) // handle pin change interrupt for D8 to D13 here
{    
   rpmCounter++;
}



Is this correct?!


Visit my blog! http://electronics.scriblab.de

PaulS

Quote
Is this correct?!
You realize you are not counting RPMs, right? You are counting pulses, which may, or may not, correspond directly to revolutions. So, rpmCounter is a lousy name.

You are also counting every CHANGE, not every RISING edge or every FALLING edge. So, your reading is likely to be double what you expect.

Finally, you could get an interrupt between setting the low order byte of the poorly named rpmCounter and setting the high order byte. Before resetting rpmCounter, you should disable interrupts, and re-enable them after resetting rpmCounter.
The art of getting good answers lies in asking good questions.

asuryan

#12
Jun 25, 2018, 05:06 pm Last Edit: Jun 25, 2018, 05:07 pm by asuryan
Thanks!

Quote
You realize you are not counting RPMs, right? You are counting pulses, which may, or may not, correspond directly to revolutions. So, rpmCounter is a lousy name.
Im going to rename rpmCounter to rpmSensorPulses ;)

Before resetting rpmCounter, you should disable interrupts, and re-enable them after resetting rpmCounter.
The problem is that interrupts need to be enabled because I need to read the encoder at the same time. Is it possible to just temporaliy disable the interrupts for PORTD?

Visit my blog! http://electronics.scriblab.de

PaulS

Quote
The problem is that interrupts need to be enabled because I need to read the encoder at the same time.
How long do you think it will take to set the high order byte of the int to 0 and then set the low order byte to 0?

In other words, just how long will interrupts need to be disabled?
The art of getting good answers lies in asking good questions.

asuryan

#14
Jun 25, 2018, 05:13 pm Last Edit: Jun 25, 2018, 05:13 pm by asuryan
Quote
How long do you think it will take to set the high order byte of the int to 0 and then set the low order byte to 0?
Not very long maybe?! ;)


So would this be more correct?


Code: [Select]
volatile int rpmSensorPulses = 0;
int rpm = 0;
unsigned long lastMillis = 0;

void setup()
{
  interruptPin(9);
}

void loop()
{
  if (millis() - lastMillis == 1000)
  {
    rpm = rpmSensorPulses * 30;
    noInterrupts();
    rpmSensorPulses  = 0;
    interrupts();
    lastMillis = millis();
  }
}

void interruptPin(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
}

ISR (PCINT0_vect) // handle pin change interrupt for D8 to D13 here
{    
   rpmSensorPulses ++;
}
Visit my blog! http://electronics.scriblab.de

Go Up