Interrupt on pin 0 and 1 (INT2/INT3) on Pro Micro

Hi,

I am doing a project to monitor and control my PC fans using a Pro Micro board. As I understand pin 0, 1, 2, 3 and 7 can be used with interrupts using attachInterrupt. I can get pin 7 to work but when i try to use pin 1 or 0 I get incorrect rpm values if the fan is not running at full speed. The fans I am using are of the exact same type. I have not tried with pin 2 and 3 since I use these for a display.

Since the same pins are used for Serial1 some posts suggest disabling Serial1 but I cannot find any information about how to do this.

volatile int revolutions = 0;
volatile int revolutions2 = 0;
unsigned long lastTime = 0;
unsigned long lastTime2 = 0;

void rpmFunction()
{
  ++revolutions;
}

void rpmFunction2()
{
  ++revolutions2;
}

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

  pinMode(7, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(7), rpmFunction, RISING);

  pinMode(1, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(1), rpmFunction2, RISING);

  pinMode (5, OUTPUT);
  pinMode (6, OUTPUT);
  analogWrite (5, 128);
  analogWrite (6, 128);

  Serial.println("Fan Control");
}

void loop() {
  unsigned long now = millis();
  if(now - lastTime >= 1000)
  {
    int rpm = 30000 / (now - lastTime) * revolutions;
    Serial.print("RPM: ");
    Serial.println(rpm, DEC);
    revolutions = 0;
    lastTime = now;
  }

  if(now - lastTime2 >= 1000)
  {
    int rpm2 = 30000 / (now - lastTime2) * revolutions2;
    Serial.print("RPM 2: ");
    Serial.println(rpm2, DEC);
    revolutions2 = 0;
    lastTime2 = now;
  }
}

Serial output with the code above:
RPM: 750
RPM 2: 7710
RPM: 750
RPM 2: 7740
RPM: 750
RPM 2: 7830

Serial output without the analogWrite (100% PWM for fans):
RPM: 1320
RPM 2: 1320
RPM: 1350
RPM 2: 1320
RPM: 1350
RPM 2: 1320

Pro Micro Pinmapping: pighixxx.com

You can use pin 0 and 1. As long as you don't use Serial1, they are just like any other normal digital pin. The Serial1 is not enabled by default.
Some pins are PCINT, you can use those as interrupts with a library (for example "EnableInterrupt").

Please provide a full sketch, this one is not compiling. There should be the word 'volatile' in your sketch, twice.

Thanks for the response.

I obviously failed to copy the entire sketch, I have now updated the original post with the two missing lines.

I have checked the pinout which also collaborates with the information on the attachInterrupt documentation attachInterrupt() - Arduino Reference that you should be able to use pins 0, 1, 2, 3, 7 as interrupt pins.

However the problem I am experiencing is that something seems to trigger the interrupt additional times when comparing pin 7 and pin 0. This only seems to happen at lower rpm for some reason.

This line: int rpm = 30000 / (now - lastTime) * revolutions; "should" be okay. But the more I keep staring at it, the more it gives me a funny feeling. I prefer to tell the compiler what to do, instead of hoping that the calculation will not go wrong by using 16-bit integers combined with unsigned long.

The "(now - lastTime)" is about 1 second or a little more. By doing "30000/1000" the result is 30 and you are losing some accuracy.

The line "unsigned long now = millis();" grabs millis() at that moment. You use that moment to calculate the revolution. But when you use the variable 'revolutions', you use that variable at a later moment. Meanwhile an interrupt could have happened. You also lose some accuracy there.

The 'revolutions' and 'revolutions2' are two bytes in a ATmega32U4, but the microcontroller is a 8-bit microcontroller. That means when you use 'revolutions' in the loop(), an interrupt might occur while the 'revolutions' is only half read or half written. That interrupt might update the 'revolutions' and you could have something wrong. The chance that this will happens is almost never, but perhaps you can make a copy of 'revolutions' with the interrupts turned off.

I don't think these minor software problems are the cause of the wrong readings.
I think you have a hardware problem.
Why pin 0 and 1 are different from pin 7 is a mystery to me. But with a bad GND anything is possible.
The ATmega32U4 has low level digital inputs. It recognises a HIGH level at a lower voltage and the signal must be extra low for a valid LOW level.
Perhaps the GND is not well connected, or the signal is noisy.
Are you sure the GND of the signal is connected properly ?
Can you add a RC-filter for both rpm signals ? Depending on the voltage levels of the rpm signal, a RC-filter and a transistor might be better to be sure that the ATmega32U4 gets a good LOW level.

When the rpm is maximum 4000 (per minute), that is 67Hz. You could try a resistor of 4k7 or 10k with 100nF.

Do you have a Arduino Uno, to compare the results ? The Arduino Uno has other voltage levels for a digital LOW and HIGH at a digital input pin.

The best way to find the problem is to measure the rpm signal. Do you have a oscilloscope ?
I think it is a good idea to try a RC-filter.
Fall back to something that is known to work. Could you try the FreqMeasure ? Make a new sketch and use the FreqMeasure by Paul Stoffregen. It is in the Library Manager (in the Arduino IDE). It uses pin 13.

Koepel:
"should" be okay. But the more I keep staring at it, the more it gives me a funny feeling. I prefer to tell the compiler what to do, instead of hoping that the calculation will not go wrong by using 16-bit integers combined with unsigned long.

The "(now - lastTime)" is about 1 second or a little more. By doing "30000/1000" the result is 30 and you are losing some accuracy.

The line "unsigned long now = millis();" grabs millis() at that moment. You use that moment to calculate the revolution. But when you use the variable 'revolutions', you use that variable at a later moment. Meanwhile an interrupt could have happened. You also lose some accuracy there.

The 'revolutions' and 'revolutions2' are two bytes in a ATmega32U4, but the microcontroller is a 8-bit microcontroller. That means when you use 'revolutions' in the loop(), an interrupt might occur while the 'revolutions' is only half read or half written. That interrupt might update the 'revolutions' and you could have something wrong. The chance that this will happens is almost never, but perhaps you can make a copy of 'revolutions' with the interrupts turned off.

Yes, this is clear. Just wanted to create a quick example. I completely agree that it is better to tell the compiler what to do :slight_smile:

Tried changing these things but it did not have any effect.

Koepel:
I don't think these minor software problems are the cause of the wrong readings.
I think you have a hardware problem.
Why pin 0 and 1 are different from pin 7 is a mystery to me. But with a bad GND anything is possible.
The ATmega32U4 has low level digital inputs. It recognises a HIGH level at a lower voltage and the signal must be extra low for a valid LOW level.
Perhaps the GND is not well connected, or the signal is noisy.
Are you sure the GND of the signal is connected properly ?
Can you add a RC-filter for both rpm signals ? Depending on the voltage levels of the rpm signal, a RC-filter and a transistor might be better to be sure that the ATmega32U4 gets a good LOW level.

When the rpm is maximum 4000 (per minute), that is 67Hz. You could try a resistor of 4k7 or 10k with 100nF.

Do you have a Arduino Uno, to compare the results ? The Arduino Uno has other voltage levels for a digital LOW and HIGH at a digital input pin.

The best way to find the problem is to measure the rpm signal. Do you have a oscilloscope ?
I think it is a good idea to try a RC-filter.
Fall back to something that is known to work. Could you try the FreqMeasure ? Make a new sketch and use the FreqMeasure by Paul Stoffregen. It is in the Library Manager (in the Arduino IDE). It uses pin 13.

Both fans and the board are connected directly to the power supply and using the same ground. Could of course be some ripple effect but my assumption is that it should affect both inputs in the same way. Also tried using pin 2 with the same effect as for pin 0 and 1.

At the moment I don't have a spare board (I have a Mega 2560 but it is currently tied up in my autonomous RC vehicle and somewhat difficult to disassemble). Don't have any capacitors at the moment so cannot add an RC filter and no oscilloscope. :frowning:

Since FreqMeasure uses pin 13 I don't think I can use it with the Pro Micro board since as far as I understand it doesn't have a pin 13.

Thanks a lot for the support! I will look into getting another board to verify with, might need one with more connections anyway to run additional fans, sensors, etc.

Update
While writing this I tested with different values for analogWrite and observed that when I set it to 0 or 255 then everything works correct. It also seemed to be connected to analogWrite on pin6 being set to those values.

But then my board stopped working. I cannot connect to it and it does not show up as a COM port anymore. So it might have been a hardware problem with the board or something might have got short circuited while I was trying different connections. So now I have to get a new board anyway.

I want to thank you for your input and might post again if I have any problems in the future, it feels like this is a good place to get help.

That smells like a hardware problem indeed.
At 0 and 255 there is no PWM signal, it is flat 0V or 5V. That means as soon as a PWM signal is used, you get into trouble with extra pulses (electrical noise).

Oops :blush: you are right, there is no pin 13 on a Pro Micro board.

I think you can not use the bare rpm signal. You probably need a transistor and a filter and perhaps some extra level (a diode) and use the output of that for the Arduino pin. But you have to know what the signal is, you have to measure it.

There is an other option. The ATmega32U4 is capable of comparing two analog signals and generate an interrupt. In that case, a RC filter is enough and you can set the trigger level with a potentiometer. There is a library for it: GitHub - leomil72/analogComp: analogComp is a library to manage the integrated analog comparator of some Atmel MCUs But I have never tried it.

About your broken Pro Micro. Sometimes the bootloader gets corrupted. You can try with a programmer (or a Arduino Uno as a programmer) to burn a new bootloader onto it. When a pin is damaged by 12V, then probably more things are damaged inside the chip, and you should no longer use it.

I have now bought a Uno and everything works as expected. So probably had some hardware error or similar with the pro micro before it died.

"The ATmega32U4 has low level digital inputs. It recognises a HIGH level at a lower voltage and the signal must be extra low for a valid LOW level"

It is called "LVTTL", Low Voltage TTL.

According to the datasheets:
The ATmega328P needs 0.6VCC for a digital input to be seen as high, that is 3.0V for a 5V Arduino board.
The ATmeag32U4 needs 0.2
VCC + 0.9, that is 1.9V.

I think that is a big difference.