Random double interrupt trigger (solved)

I'm trying to measure RPM using an int0 (highest priority).
The problem is: Every few seconds I get a "spike" at exactly double the frequency.

It seems to be more apparent at low frequencies (<10Hz).
The input circuit is as in picture below.
I'm using a proper signal generator and an oscilloscope... the input signal is solid and it's a 0-10V signal.

For code, I tried different options. Right now, I reduced it to the most simple logic, but the problem still persists:

Variables Used:

volatile unsigned long t;
unsigned long RPMtimePrev;
volatile unsigned long RPMperiod;
int RpmScale=100; //constant for scaling the input
float tempRPM;

ISR:

void MeasureRPM()
{
t=micros();
RPMperiod= t-RPMtimePrev;
RPMtimePrev=t;
}

RPM formula I'm using for calculating the RPM:

tempRPM=(float)100000/((float)RPMperiod/(float)RpmScale);

Any idea what might be causing the problem?

Int.jpg

RazOn:
Any idea what might be causing the problem?

Yes. The problem is most certainly in the code you failed to post.

This should help... http://snippets-r-us.com/

I guess you did not use noInterrupts(), interrupts() around all accesses to RPMperiod.

RPMperiod is longer then a single byte, so you have to access it with interrupts disabled from main code.
To minimize the time interrupts are off, you could use something like (uncompiled, untested)

 noInterrupts();
unsigned long lastPeriod = RPMperiod;
 interrupts();
 tempRPM=(float)100000/((float)lastPeriod /(float)RpmScale);

BTW are all these float casts sensible?

Ok, see attached zipped folder the whole code/project.

Here's a video of what I'm seeing:

I have configured 2 pin interrupts. One for RPM one for SPEED
They both read from the same frequency generator and both interrupts are configured exactly the same.

int1 seems to be get bad readings more often than int0, so I assumed it has to do with the priority of the interrupts.
Half way in the video, I connect int0 input wire to ground; only int1 receives the signal, but still bad readings.

I hope this helps explaining what my problem is.

ATMEL_ICE5.zip (2.64 KB)

Whandall:
I guess you did not use noInterrupts(), interrupts() around all accesses to RPMperiod.

RPMperiod is longer then a single byte, so you have to access it with interrupts disabled from main code.
To minimize the time interrupts are off, you could use something like (uncompiled, untested)

BTW are all these float casts sensible?

I tried the noInterrupts(), interrupts() and it doesn't fix it.
As far as casts go, I tried to do it in many different ways with same results. If you have a suggestion...I'll try it!

Why do you scatter that small project over 5 files?

You have to use noInterrupt and interrupt to avoid that the vars are written while you try to read them.

I can not find a datasheet for your TPL2161 please provide a link.

I’ll try using noInterrups again, even though ... wouldn’t ignoring an interrupt create a similar issue? The next calculated “period” would be twice as long (it would half my RPM/speed reading)

I think it’s inyeresting it always display exactly double the value, not some random value.

RPMt is only used in MeasureRPM and it is only used for temporary storage. Change it to a local variable...

volatile unsigned long RPMt; // <<<<<< Remove this
void MeasureRPM() 
{
  unsigned long RPMt;  // <<<<<< Add this

  RPMt=micros();
  RPMperiod= RPMt-RPMtimePrev;
  RPMtimePrev=RPMt;
}

This is all the code coupled with MeasureRPM...

void setup() 
{ 
...
  attachInterrupt(digitalPinToInterrupt(3), MeasureRPM, RISING);
...
}
  
volatile unsigned long RPMt;
unsigned long RPMtimePrev;
volatile unsigned long RPMperiod;

void MeasureRPM() 
{
  RPMt=micros();
  RPMperiod= RPMt-RPMtimePrev;
  RPMtimePrev=RPMt;
}

void CalcRPM()
{
  float tempRPM;

  tempRPM=(float)10000/(RPMperiod/(float)RpmScale);  //calculate a temporary RPM value

  if ( tempRPM < 500.0 )          //Only consider realistic numbers. Ignore RPM readings that is too great.  
  {
    NewRpm=tempRPM;             //Valid RPM is passed downstream as the new RPM reading
  }   
  intRPM=NewRpm;
}

void Display()
{
...
  display.println(RPMperiod);
...
}

Because RPMperiod is shared with an interrupt service routine it must be volatile (which it is) and it must be protected with a critical section (which it is not). Post #2 has a method for doing that. RPMperiod has to be protected for ALL accesses outside of the interrupt service routine (CalcRPM and Display).

I have a small example that illustrates the atomic problem
(enclosing accesses to volatile variables of more than one byte in noInterrupt and interrupt).

void setup() {
  Serial.begin(250000);
  Serial.println(F("Wait for error...\r\n\r\nfrom - to - difference"));
  // set up Timer 1
  TCCR1A = 0;                        // normal operation
  TCCR1B = bit(WGM12) | bit(CS10);   // CTC, no pre-scaling
  OCR1A =  7999;                     // compare A register value for 2 kHz
  TIMSK1 = bit (OCIE1A);             // interrupt on Compare A Match
}
volatile unsigned long Counter;

ISR(TIMER1_COMPA_vect)
{
  Counter++;
}

void loop() {
  static unsigned long before;
  static unsigned long current;

  // Counter is incremented with 2 kHz
  //  noInterrupts(); // uncomment to make it work
  current = Counter;
  //  interrupts();   // uncomment to make it work

  unsigned long difference = current - before;

  // loop is so fast, you should never see a difference  bigger than 1

  if (difference > 1) {
    Serial.print(before, HEX);
    Serial.write(' ');
    Serial.print(current, HEX);
    Serial.print(F(" - "));
    Serial.println(difference, HEX);
  }
  before = current;
}
Wait for error...

from - to - difference
9FF AFF - 100
AFF A01 - FFFFFF02
14FF 15FF - 100
15FF 1501 - FFFFFF02
1501 1503 - 2
2BFF 2CFF - 100
2CFF 2C01 - FFFFFF02
2C01 2C03 - 2
49FF 4AFF - 100
4AFF 4A01 - FFFFFF02
4A01 4A03 - 2
60FF 61FF - 100
61FF 6101 - FFFFFF02

I tried using the noInterrupts() while copying the RPMperiod to a temporary variable which then gets used in the rest of the program. In my code it doesn't make any difference; still having the random double frequency reading.

The fact the "noise" it's always exactly double my normal reading, regardless of input frequency, is really puzzling me. If it was caused by interrupts writing to the variable, shouldn't we expect more random numbers?
Both the RPM and SPEED are picked up from same source. Why is the problem more visible on the SPEED (int1) signal?

I moved the bare minimum circuit to a breadboard with no optocoupler. connected 0-5V signal, straight from signal generator to arduino, and still have the same issue.

Optocoupler datasheet for reference, even though it may be irrelevant:
https://toshiba.semicon-storage.com/info/docget.jsp?did=14455&prodName=TLP2161

I have the program spread across multiple sections, because it's part of a much bigger project. The RPM and SPEED signals is mere inputs to the bulk of the program. I stripped everything else to troubleshoot this issue.

One thing that seems to make things better is to over-drive the input (use more than 5v on the input) which is bad, but makes the noise appear a lot less often.

The more i look into this the more questions I have >:(

I continued stripping the program... when I removed the OLED display library and used the serial monitor to show the values, the problem finally went away!

At this point in time I'm sure that the <Adafruit_GFX.h> and/or <Adafruit_SSD1306.h> libraries for the OLED display are not "Interrupt friendly"; or at least not the way I'm using it.

Somebody smarter then me can figure out why :slight_smile:

edit: check out this post
https://forums.adafruit.com/viewtopic.php?f=47&t=53161

Thanks everybody for your input!