Arduino Forum

Using Arduino => Programming Questions => Topic started by: dlloyd on Apr 06, 2016, 05:34 am

Title: [SOLVED] Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 06, 2016, 05:34 am
EDIT: Latest Results and Code at Reply #47


I wrote this code to test a nasty interrupt signal and create a clean output signal. It can be used to test your own interrupt routines to see how it would respond. I used an Uno ... all that's needed is one jumper wire from pin 13 to pin 2. View the results on the Serial Plotter.

(http://i.imgur.com/wFoIN7z.png)
Blue trace: Noisy signal with unwanted interrupts (2 on rising and 2 on falling). Signal alternates between normally high and normally low.
Orange trace: Debounced output signal with instantaneous response after clearing timeout
Red trace: Timeout signal

I've tried using the method "previousTime += minElapsed" which would have no time slippage, but there was no way I could get perfectly clean results ... looking at the signal, I thought this could work.

Not really important though, because I prefer resetting the interval after each interrupt which causes the overall elapsed time to expand. This means the interval will represent a stable period of time before the next valid interrupt is detected.

Here's the code:

Code: [Select]
int ledPin = 13;
int inputPin = 2;
byte inputState;

//----- pattern generator -----
unsigned long pattern;
unsigned long timeShiftPrev;
byte shiftCount = 0, signalSelect;

//----- ISR variables ---------
volatile unsigned long minElapsed = 100;
volatile unsigned long maxElapsed = 2500;
volatile unsigned long elapsedTime;
volatile unsigned long previousTime;
volatile byte cleanOutput;
volatile byte timeout;

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(115200);
  attachInterrupt(0, onPulse, CHANGE);
}

void loop() {
  patternGenerator();
  inputState = digitalRead(inputPin);
}

void onPulse()
{
  elapsedTime = millis() - previousTime;
  if (elapsedTime < minElapsed )  //false interrupt
  {
    return;
  }
  if (elapsedTime >= minElapsed && elapsedTime <= maxElapsed )  //in range
  {
    previousTime = millis();
    timeout = 0;
    if (inputState)               //falling
    {
      cleanOutput = 0;
    }
    else                          //rising
    {
      cleanOutput = 1;
    }
    return;
  }
  if (elapsedTime > maxElapsed )  //timeout
  {
    previousTime = millis();
    timeout = 1;
  }
}

void patternGenerator()
{
  const unsigned long normallyLowSignal = 0x000AF500;
  const unsigned long normallyHighSignal = 0xFFF50AFF;
  const unsigned long shiftInterval = 100;

  unsigned long timeShiftNow = millis();
  if (timeShiftNow - timeShiftPrev > shiftInterval)
  {
    timeShiftPrev += shiftInterval;
    pattern = pattern >> 1;
    shiftCount++;
    if (shiftCount >= 32)
    {
      shiftCount = 0;
      signalSelect++;
      (signalSelect & 2) ? pattern = normallyHighSignal : pattern = normallyLowSignal;
    }
    digitalWrite(ledPin, (pattern & 1));
    Serial.print((pattern & 1) + 1);
    Serial.print(" ");
    Serial.print(cleanOutput);
    Serial.print(" ");
    Serial.println(timeout - 1);
  }
}

I guess it's my inexperience with coding, but if anyone knows why "previousTime += minElapsed" wouldn't work or has any suggestions for improving the ISR ... actually all comments are welcome!

EDIT: Code updated (as per reply#1).
Title: Re: Dealing with a noisy interrupt signal
Post by: Coding Badly on Apr 06, 2016, 07:23 am

What is supposed to happen in the ISR when elapsedTime == minElapsed?

Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 06, 2016, 07:31 am
Heh, thanks for catching that.
Title: Re: Dealing with a noisy interrupt signal
Post by: PaulMurrayCbr on Apr 06, 2016, 07:48 am
My debouncer at github (https://github.com/PaulMurrayCbr/DebounceInput/wiki).
Title: Re: Dealing with a noisy interrupt signal
Post by: Coding Badly on Apr 06, 2016, 07:51 am

Why are you reading the pin state in loop?

Title: Re: Dealing with a noisy interrupt signal
Post by: GoForSmoke on Apr 06, 2016, 08:38 am
Use if-else logic to slice away one condition after the other.


Code: [Select]

void onPulse()
{
  elapsedTime = millis() - previousTime;

  if (elapsedTime < minElapsed )  //   ALL too soon cases are handled here
  {
    return;                //
  }
  else if (elapsedTime <= maxElapsed )  //   ALL cases left within max time are  handled here
  {
    previousTime = millis();
    timeout = 0;
    if (inputState)               //falling
    {
      cleanOutput = 0;
    }
    else                          //rising
    {
      cleanOutput = 1;
    }
    return;
  }
  else                                          // ALL cases left (timeout) are handled here
  {
    previousTime = millis();
    timeout = 1;
  }
}


BTW, the ISR should only save millis (if it has to) and set a flag. The rest should inside of loop().

Code: [Select]

void onPulse() // the IRQ
{
  interruptMillis = millis();  // interruptMillis must be a volatile variable
  interruptFlag = 1; // the flag should be a byte -- bytes run faster on 8-bit machines
}

............. snipped code

void loop()
{
............. snipped code

  if ( interruptFlag ) // this is a task inside of loop(), it always runs at least this check
  {
    interruptFlag = 0;    
    elapsedTime = interruptMillis - previousTime;

    if (elapsedTime > maxElapsed )  //   ALL cases over max time
    {
      previousTime = millis();
      timeout = 1;
    }
    else if (elapsedTime >= minElapsed )  //   ALL cases left >= min time
    {
      previousTime = millis();
      timeout = 0;
      if (inputState)               //falling
      {
        cleanOutput = 0;
      }
      else                          //rising
      {
        cleanOutput = 1;
      }
    }
    // no empty else is needed, this is ALL cases left, the < min time case
  }  // end of task

............. snipped code

}


I wouldn't save millis in the interrupt but then I wouldn't use an interrupt to time events on the millis scale.
Title: Re: Dealing with a noisy interrupt signal
Post by: aarg on Apr 06, 2016, 09:11 am
My debouncer at github (https://github.com/PaulMurrayCbr/DebounceInput/wiki).
How would you implement your digital filter in an interrupt situation? Does it not depend on a constant sample rate? The interrupts are called randomly.
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 06, 2016, 02:11 pm
Why are you reading the pin state in loop?
I did this to get the pin state just previous to the interrupt to determine rising or falling state change and to keep the ISR as quick as possible.

I suppose this is OK if the loop iteration speed is faster than the input rate. I was a hesitant to try checking the pin state inside the ISR as I was initially thinking that it could be during a noisy period and get an invalid reading.

Ah, OK ... when servicing an ISR, interrupts are disabled. I was initially not sure if registers are held constant (saved), but after re-reading Nick Gammons article, I see they are. (I have to stop thinking how register's work in FPGAs where everything is affected every clock cycle).

Thanks again ... I'll work on an updated version.
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 06, 2016, 02:38 pm
Use if-else logic to slice away one condition after the other.
Yes, I do like the strategy and did try something similar (didn't have success ... my final "else" for timeout was prevented the "in range" condition from triggering), but I'll give it another go.

I wouldn't save millis in the interrupt but then I wouldn't use an interrupt to time events on the millis scale.
Yes, micros() is definitely preferred ... just used millis while debugging the logic. I'll change this in my next update.

Thank you for your comments and strategy. I'll also be trying to get the output to respond on the same interrupt as when timeout is detected (on the graph, the output won't respond until the next interrupt ... the new conditional logic should resolve this).

Title: Re: Dealing with a noisy interrupt signal
Post by: GoForSmoke on Apr 06, 2016, 04:07 pm
Strategy, technique, whatever.

The output to graph takes far longer than you think. You do a lot of 32-bit work, AVR is 8-bit machine.

A little more technique for speed, time can be type byte, unsigned int or unsigned long. They all roll over, they all work as time between = now - start, one difference is how long an interval you can time and the other is how fast they run. The bytes run Much Faster.

byte is good to 255 units (millis or micros) but best to keep <= 200 or 250 and it is up to your code to not let it go farther. I use bytes with millis to debounce, usually 2 to 5 millis is enough.

unsigned int is good to 60000 or 65000, one full minute in millis.

unsigned long is good to 49.7-some days.

Morris Dovey left a time library for Arduino that uses unsigned long long good to billions of years.

byte quickMillis;
.....
quickMillis = ( millis() & 0xFF );


Fingers that learn code on computers often type 'int' when making a counting variable. Break the habit. When did you have an AVR pin number > 255? The byte runs quicker by at least 2x.


I would be surprised if all of that will have the interrupt and graph match but it's possible since both use serial print. At 115200 baud, serial chars send and arrive 1388 cycles apart, almost 87 micros. That is per character, your messages have a few.

Serial reporting can screw up fast timing, I haven't used the graph thing but I see no reason why it should be different. Strategy this time: make short runs that save data as you can and then output to serial.

Welcome to serial processing. We can make it look parallel but only down to a few micros without resorting to special near-machine-code-or-machine-code measures and then only with tight algorithms. That's a trade-off with these cheap yet versatile chips.

You are an exception to what I mostly see, people who code like the chip runs on a 1KHz clock.

Keep in mind that the function you call may run many cycles, the bit length of the variables you use may add many more (a 16-bit integer multiply takes more than a few) and floats are about 100x slower than longs.

If you keep the interrupt line normally HIGH and pull it LOW to signal, would that lessen the noise?
Title: Re: Dealing with a noisy interrupt signal
Post by: GoForSmoke on Apr 06, 2016, 04:26 pm
Yes, I do like the strategy and did try something similar (didn't have success ... my final "else" for timeout was prevented the "in range" condition from triggering), but I'll give it another go.
Yes, micros() is definitely preferred ... just used millis while debugging the logic. I'll change this in my next update.
Look at the difference between the second example and the first. The order you have in the first runs marginally quicker in cases where minimum is not reached which may be desirable in tight code.

This is more like that without the returns.

if ( less than min )    // has no code, needs no {}, branches to the end
else if ( up to max )
{
   // code
}
else
{
   // code
}

How you order the conditional branches (aka if's) prioritizes execution in serial execution land.
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 06, 2016, 07:06 pm
Yeah, the plotter is a speed hog and so is generating the signal which is why I kept the frequency low for starts. So far so good with the plotter for crude troubleshooting ... it lacks a way to set the timebase though.

I learned recently about atomic readings ... I'll test using a byte counter for timekeeping once I get things cleaned up.

That's bothered me too about using 'int' for pin numbers. I've read somewhere that it ends up just using a byte when compiled. Wherever I look (even Arduino's reference) int seems to be the standard. I've never had use (yet) for using a signed integer for a counter.

Quote
If you keep the interrupt line normally HIGH and pull it LOW to signal, would that lessen the noise?
It seems to be more of a standard to do this. Even with I2C, SPI, RX/TX the lines default HIGH or require pullups.

In this case, I want all the noise I can get to see what can be achieved in software. Once I get things cleaned up, I'll test using a timer output (non-blocking) connected to an RC circuit driving a transistor and relay. The relay's contacts should produce some serious bounce ... I'll have it switch a 5V signal and connect it back to the interrupt. I have a new Saleae logic analyzer I'll try out and get some measurements and traces.

Here's the code cleaned up a bit (a little more to do yet):
Note: had to swap the rising and falling condition so as not to produce an inverted output. The plot now looks the same as posted earlier.

Code: [Select]
byte ledPin = 13;
byte inputPin = 2;
byte inputState;

//----- pattern generator -----
unsigned long pattern;
unsigned long timeShiftPrev;
byte shiftCount = 0, signalSelect;

const unsigned long minElapsed = 120000;
const unsigned long maxElapsed = 2500000;
unsigned long elapsedTime, previousTime;
byte cleanOutput, timeout;

//----- ISR variables ---------
volatile unsigned long isrTime;
volatile byte interruptFlag;

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(115200);
  attachInterrupt(0, onPulse, CHANGE);
}

void loop() {
  patternGenerator();
  processSignal();
}

void onPulse()
{
  isrTime = micros();
  interruptFlag = 1;
}

void processSignal()
{
  if ( interruptFlag )
  {
    inputState = digitalRead(inputPin);
    interruptFlag = 0;
    noInterrupts ();
    unsigned long interruptTime = isrTime;
    interrupts ();
    elapsedTime = interruptTime - previousTime;

    if (elapsedTime > maxElapsed )        // ALL cases over max time
    {
      previousTime = interruptTime;
      timeout = 1;
    }
    else if (elapsedTime >= minElapsed )  // ALL cases left >= min time
    {
      previousTime = interruptTime;
      timeout = 0;
      if (inputState)                     // Rising
      {
        cleanOutput = 1;
      }
      else                                // Falling
      {
        cleanOutput = 0;
      }
    }
      // no empty else is needed, this is ALL cases left, the < min time case
  }   // end of task
}

void patternGenerator()
{
  const unsigned long normallyLowSignal = 0x000AF500;
  const unsigned long normallyHighSignal = 0xFFF50AFF;
  const unsigned long shiftInterval = 100000;

  unsigned long timeShiftNow = micros();
  if (timeShiftNow - timeShiftPrev > shiftInterval)
  {
    timeShiftPrev += shiftInterval;
    pattern = pattern >> 1;
    shiftCount++;
    if (shiftCount >= 32)
    {
      shiftCount = 0;
      signalSelect++;
      (signalSelect & 2) ? pattern = normallyHighSignal : pattern = normallyLowSignal;
    }
    digitalWrite(ledPin, (pattern & 1));
    Serial.print((pattern & 1) + 1);
    Serial.print(" ");
    Serial.print(cleanOutput);
    Serial.print(" ");
    Serial.println(timeout - 1);
  }
}
Title: Re: Dealing with a noisy interrupt signal
Post by: GoForSmoke on Apr 06, 2016, 07:27 pm
A HIGH wire has the pressure of voltage keeping it stable, yes?

You haven't tried using less than 32-bit time variables yet. Fear?
And still the pin variables are int.

One thing about global variables is that they only get allocated once.

Those local constants in the pattern generator, maybe the optimizer catches them otherwise they get initialized on the stack every time the function runs.

Also timeShiftNow is only used once after it gets set to micros(). Maybe the optimizer catches that otherwise you don't need the variable added to the stack, just use micros() in the time check if().
 
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 06, 2016, 09:56 pm
I guess I should clarify that I'd like to end up with an optimized, efficient and universal function that can clean up most any noisy signal. A function that works with ISR's and external interrupt inputs and keeps time measurements as accurate as possible.

I'd like it to work with touching a bare wire to ground (or vcc), relay contact and switch bounce up to 30ms, noisy ignition pulses, reed switches, interference over long cable runs, encoders, optic switching, electricity meter pulses, flow meters, rpm signals, pulse measurement (duty cycle and period), counting, frequency measurement.

I'll still have to deal with deglitching .. later I'll be attempting to clean up a single glitch occurring during a high and low stable period.

Goal: Frequency range of the function 0.01Hz - 100kHz (fully debounced and deglitched). The user would configure the function to suit the specifications of device connected.

Quote
You haven't tried using less than 32-bit time variables yet. Fear?
At this point ... yes. I'm starting with slower signals. An 8-bit timer will overflow about 117 times during 30ms of invalid interrupts (bounce). But I'll give it a go when I get further along.

Quote
And still the pin variables are int.
Done.

Quote
A HIGH wire has the pressure of voltage keeping it stable, yes?
Yes.

The pattern generator is just temporary, I'll be using it along with a timer output and relay driver on a separate Arduino sending pulses to pin2 on the UNO. This will avoid any synchronization with the UNO's clock and allow it to run full out.
Title: Re: Dealing with a noisy interrupt signal
Post by: Robin2 on Apr 06, 2016, 11:31 pm
I have read this Thread looking for inspiration.

I am trying to make a small (physically) rev counter (i.e. count the revolutions) for a small 130 size DC motor. In the space available the only solution I have thought of is to use a UV led and a photo transistor organized so that a small piece of rotating metal (white one side, black the other) reflects the light onto the sensor.

Initial tests suggest that it is bouncing and returning too many counts. I think the max RPM will be 12000 or about 200 per sec but I was getting counts near 1000.

...R
Title: Re: Dealing with a noisy interrupt signal
Post by: Coding Badly on Apr 06, 2016, 11:56 pm
Here's the code cleaned up a bit (a little more to do yet):
You have to put the digitalRead inside the interrupt service routine.  Separating the interrupt handling from the digitalRead creates a race condition that will leave you pulling your hair out.  (Technically, the race condition is always present.  Moving the digitalRead to the top of the interrupt service routine dramatically reduces the chances of trouble.)

Code: [Select]
void processSignal()
{
  if ( interruptFlag )
  {
    inputState = digitalRead(inputPin);
    interruptFlag = 0;


I commend you for trying to handle the interrupt the "right way".  However, in this situation, doing that opens up a second race condition.  (There is actually a third but the third one is trivial to fix.)  I strongly suggest, for this application, your processSignal code belongs in the interrupt service routine as you had it originally.

Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 07, 2016, 02:53 am
Quote from: GoForSmoke
One thing about global variables is that they only get allocated once.
Those local constants in the pattern generator, maybe the optimizer catches them otherwise they get initialized on the stack every time the function runs.
Didn't know that ... thank you.

Quote from: GoForSmoke
Also timeShiftNow is only used once after it gets set to micros(). Maybe the optimizer catches that otherwise you don't need the variable added to the stack, just use micros() in the time check if().
Done ... thanks again.

Quote from: Coding Badly
You have to put the digitalRead inside the interrupt service routine.  Separating the interrupt handling from the digitalRead creates a race condition that will leave you pulling your hair out.  (Technically, the race condition is always present.  Moving the digitalRead to the top of the interrupt service routine dramatically reduces the chances of trouble.)
I seriously appreciate this (I've already lost enough hair). Later, I may end up doing a direct register read as a speedup measure.

Quote from: Coding Badly
I commend you for trying to handle the interrupt the "right way".  However, in this situation, doing that opens up a second race condition.  (There is actually a third but the third one is trivial to fix.)  I strongly suggest, for this application, your processSignal code belongs in the interrupt service routine as you had it originally.
Done. I've found a workaround to set the plot's timebase. Now we can see the transitions more clearly. The first set of traces were with the signal processing in the loop, the second set is with the processing in the ISR. There is a difference.

(http://i.imgur.com/Vw7FmDJ.png)

I'll be adding a "glitch" signal circled in red ... this one is going to be difficult to eliminate without affecting timing, but I have some ideas (my next challenge).

Some notes on the Serial Plotter: It seems to work quite well for me at this stage. I'm able to stretch the timebase by x2 or higher by repeating the print action.

Latest code:
Code: [Select]
byte ledPin = 13;
byte inputPin = 2;
byte inputState;

//-------pattern generator---------------
unsigned long pattern;
unsigned long timeShiftPrev;
byte shiftCount, signalSelect;

//-------ISR constants and variables-----
const unsigned long minMicros = 120000;
const unsigned long maxMicros = 2500000;
volatile unsigned long nowMicros, prevMicros, elapsedMicros;
volatile byte cleanOutput, timeout;

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(115200);
  attachInterrupt(0, onPulse, CHANGE);
}

void loop() {
  patternGenerator(100000, 4);  // shiftInterval (µs), plotTimebase (x4)
}

void onPulse()
{
  nowMicros = micros();
  inputState = digitalRead(inputPin);
  elapsedMicros = nowMicros - prevMicros;
  if (elapsedMicros >= minMicros)  // ALL cases >= min time
  {
    prevMicros = nowMicros;
    (elapsedMicros >= maxMicros) ? timeout = 1 : timeout = 0;  // timeout flag
    if (inputState)      // Rising
    {
      cleanOutput = 1;
    }
    else                 // Falling
    {
      cleanOutput = 0;
    }
  }
}

void patternGenerator(unsigned long shiftInterval, byte plotTimebase)
{
  if (micros() - timeShiftPrev > shiftInterval)
  {
    timeShiftPrev += shiftInterval;
    pattern = pattern >> 1;
    shiftCount++;
    if (shiftCount >= 32)
    {
      shiftCount = 0;
      signalSelect++;
      //                             Normally High Signal   Normally Low Signal
      // (signalSelect & 2) ? pattern = 0xFBF50AFF : pattern = 0x040AF500; // bounce with glitch
      (signalSelect & 2) ? pattern = 0xFFF50AFF : pattern = 0x000AF501; // bounce
    }
    digitalWrite(ledPin, (pattern & 1));

    for (int i = 0; i < plotTimebase; i++) {
      Serial.print((pattern & 1) + 1);
      Serial.print(" ");
      Serial.print(cleanOutput);
      Serial.print(" ");
      Serial.println(timeout - 1);
    }
  }
}

Results (debouncing completed):
(http://i.imgur.com/n43Mtq5.png)
Title: Re: Dealing with a noisy interrupt signal
Post by: aarg on Apr 07, 2016, 03:33 am
Yes, high speed pulse debouncing is a completely different animal than low speed switch debouncing. The premise, in it's simplest form, is that the bounce impulses are higher in frequency than the signal. I think it is hard to refute this. So the idea of low pass filtering and applying a threshold makes good sense.

However, in order to properly implement the low pass function, the digital filter must be sampling at precise, periodic intervals. This seems incompatible with the mechanism of a pin state interrupt. Furthermore, it will be difficult to run the filter at extremely high frequencies without incurring significant demand on the processor.

There isn't much doubt that it can be made to work well. A good question is, what is a useful upper limit on the signal frequency?

A corresponding question would be, can a simple hardware RC outperform it by a large margin?
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 07, 2016, 04:18 am
There isn't much doubt that it can be made to work well. A good question is, what is a useful upper limit on the signal frequency?
Yes, good question. Well, I'm hoping for at least 10kHz for a noisy signal, but there'll be a point where jumping into the ISR with needless false interrupts will consume too many cycles. I'm trying just the timing approach for now ... at least its very fast with the least demand on the processor.
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 07, 2016, 04:21 am
@Robin2

I think something like this could work for your sensor because (at this stage) it should debounce both edges of the pulses, so it won't matter which side of the pulse the false transitions occur. (I assume you'll be using an interupt for this). Just use CHANGE mode for your interupt. Then, for example, you could use  minMicros = 4000 (4ms, 250Hz) and maxMicros = 4000000 (4sec, 0.25Hz) to set the input timing range.
Title: Re: Dealing with a noisy interrupt signal
Post by: GoForSmoke on Apr 07, 2016, 04:30 am
The interrupt occurs because of pin change so yup read the pin in the ISR. I missed that.
When timing is (was) in millis I don't worry about taking < 100 micros to read the pin.

Should it do the time checks in the IRQ as well?

How fast are these spikes? The original code measured millis. When the goalposts move to spikes less than 50 micros long then that's a different ballpark however...

const unsigned long minMicros = 120000;
const unsigned long maxMicros = 2500000;

Those are the goalposts right there. 120 to 2500 milliseconds.

Quote
Yes, high speed pulse debouncing is a completely different animal than low speed switch debouncing.
My debounce switch tester/calibration catches bounces 12 to 20 micros in length while reporting them to look for N micros with no change. Grounding a jumper on the USB port box to make dirty switching, I only need 2000 micros to debounce that and I don't use interrupts to do it. Want the sketch?

Are you debouncing or making an oscilloscope? Both?
Title: Re: Dealing with a noisy interrupt signal
Post by: aarg on Apr 07, 2016, 04:31 am
Yes, good question. Well, I'm hoping for at least 10kHz for a noisy signal, but there'll be a point where jumping into the ISR with needless false interrupts will consume too many cycles. I'm trying just the timing approach for now ... at least its very fast with the least demand on the processor.
Sure, but I'm still wondering how it can possibly work with an ISR. Or, I guess, what would be the point of using an ISR when you have to keep sampling going with a timing process anyway. In that case, you could just as easily read the input port.
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 07, 2016, 05:54 am
Sure, but I'm still wondering how it can possibly work with an ISR. Or, I guess, what would be the point of using an ISR when you have to keep sampling going with a timing process anyway. In that case, you could just as easily read the input port.
The interrupt is in CHANGE mode ... there's no sampling, just checking elapsed time, edge to edge of input signal transitions.
Title: Re: Dealing with a noisy interrupt signal
Post by: GoForSmoke on Apr 07, 2016, 07:27 am
You can knock a few micros off those digital reads and writes using port manipulation.

Uno pin 2 is PINSD pin 2 for read and PORTD pin 2 for write or write 1 to PINSD pin 2 to change whatever state pin 2 is.

pinTwoState = ( PINSD & 4 ) >> 2; // fast read pin 2

PORTD = PINSD & 0xFB; // fast set pin 2 to 0
PORTD = PINSD | 4; // fast set pin 2 to 1

PINSD |= 4; // fast invert pin 2


 
Title: Re: Dealing with a noisy interrupt signal
Post by: GoForSmoke on Apr 07, 2016, 07:54 am

void patternGenerator(unsigned long shiftInterval, byte plotTimebase)
{
  if (micros() - timeShiftPrev > shiftInterval)
  {
    timeShiftPrev += shiftInterval;
    pattern = pattern >> 1;
    shiftCount++;
    if (shiftCount >= 32)
    {
      shiftCount = 0;
      signalSelect++;
      //                             Normally High Signal   Normally Low Signal
      // (signalSelect & 2) ? pattern = 0xFBF50AFF : pattern = 0x040AF500; // bounce with glitch
      (signalSelect & 2) ? pattern = 0xFFF50AFF : pattern = 0x000AF501; // bounce
    }
    digitalWrite(ledPin, (pattern & 1));

    for (int i = 0; i < plotTimebase; i++) {
      Serial.print((pattern & 1) + 1);
      Serial.print(" ");
      Serial.print(cleanOutput);
      Serial.print(" ");
      Serial.println(timeout - 1);
    }

  }
}


Is this really about writing to the led pin (jumpered to the interrupt pin) and having the IRQ change CleanOutput and timeout before they are printed on all 4 lines? A race between serial print and the IRQ?
Title: Re: Dealing with a noisy interrupt signal
Post by: Coding Badly on Apr 07, 2016, 07:59 am
PINSD |= 4; // fast invert pin 2
Oops.

Code: [Select]
PORTD = PINSD | 4; // fast set pin 2 to 1

That one is trouble as well.

Title: Re: Dealing with a noisy interrupt signal
Post by: GoForSmoke on Apr 07, 2016, 08:56 am
Oops.

Code: [Select]
PORTD = PINSD | 4; // fast set pin 2 to 1

That one is trouble as well.

PORTD |= 4; // right, PIND is inputs, no need to use that... fast set pin 2 to 1
PORTD &= 0xFB; // much better

Well the real name is PIND and AVR's have this feature you can say oops about all over again.

Quote
Three I/O memory address locations are allocated for each port, one each for the Data Register - PORTx, Data
Direction Register - DDRx, and the Port Input Pins - PINx. The Port Input Pins I/O location is read only, while the
Data Register and the Data Direction Register are read/write. However, writing a logic one to a bit in the PINx Register,
will result in a toggle in the corresponding bit in the Data Register.
In addition, the Pull-up Disable - PUD bit
in MCUCR disables the pull-up function for all pins in all ports when set.
PIND |= 4; // does toggle PORTD bit 2.

First time I saw this, Nick Gammon used it. It's a neat trick, TY ATMEL!
IIRC he used it in a video-signal generator.
Title: Re: Dealing with a noisy interrupt signal
Post by: Coding Badly on Apr 07, 2016, 09:48 am
PIND |= 4; // does toggle PORTD bit 2.
I agree.  But your version does more than that.  You have too many operators.

Title: Re: Dealing with a noisy interrupt signal
Post by: aarg on Apr 07, 2016, 10:27 am
The interrupt is in CHANGE mode ... there's no sampling, just checking elapsed time, edge to edge of input signal transitions.
Sorry, I got was getting your code mixed up with the code linked to in reply #3. So everything I said applies to that, not what you have done (except for general theoretical comments). Oops.

Doesn't letting the ISR handle the noise get a little CPU intensive? Because the ISR gets called on every transition?
Title: Re: Dealing with a noisy interrupt signal
Post by: GoForSmoke on Apr 07, 2016, 11:00 am
Quote
from: GoForSmoke on Today at 02:56 am

    PIND |= 4; // does toggle PORTD bit 2.
I agree.  But your version does more than that.  You have too many operators.


http://www.arduino.cc/en/Reference/BitwiseCompoundOr

Syntax:

x |= y;   // equivalent to x = x | y;

Parameters

x: a char, int or long variable
y: an integer constant or char, int, or long

PIND is a register that is read/write, just like PORTD and DDRD


Tested & working sketch to blink led13 using PINB |= 0x20;

Code: [Select]

void setup()
{
  Serial.begin( 115200 );
  pinMode( 13, OUTPUT );
}

byte run20;
unsigned int tStart;


void loop()
{
  if (( millis() & 0xFFFF ) - tStart >= 0x1FF )
  {
    PINB |= 0x20;
    tStart += 0x1FF;
    Serial.print( tStart, HEX );
    Serial.print( "    " );
    Serial.println( PORTB, HEX );

    if ( ++run20 >= 20 )    while (1);
  }

}


Quote
1FF    20
3FE    0
5FD    20
7FC    0
9FB    20
BFA    0
DF9    20
FF8    0
11F7    20
13F6    0
15F5    20
17F4    0
19F3    20
1BF2    0
1DF1    20
1FF0    0
21EF    20
23EE    0
25ED    20
27EC    0
I could have used PINB5 |= 1; but where's the fun in that?
Title: Re: Dealing with a noisy interrupt signal
Post by: Coding Badly on Apr 07, 2016, 11:12 am
Quote
Tested & working sketch to blink led13 using PINB |= 0x20;
You tried it (in a very limited test).  It did what you thought it would do.  It is correct.  Specious reasoning. 

Code: [Select]
PINB |= 0x20;

Let's expand that...

Code: [Select]
PINB = PINB | 0x20;

Let's say PINB is 0x1F before that line runs.  Substituting the value...

Code: [Select]
PINB = 0x1F | 0x20;

Reducing the logical-or...

Code: [Select]
PINB = 0x3F;

Instead of toggling one output you toggled six.  Thank goodness you were just blinking an LED instead of trying to control a jet engine.

The correct use of PINB to toggle an LED on pin 13 is...

Code: [Select]
PINB = 0x20;

See the difference?  Do you now understand what I meant by, "you have too many operators"?

Title: Re: Dealing with a noisy interrupt signal
Post by: GoForSmoke on Apr 07, 2016, 11:45 am
Ahhhhhh. And the correct way is quicker too!

If I hadn't been so lazy and used 4 or 6 leds I might have noticed in time.
TKS for the explanation!
Title: Re: Dealing with a noisy interrupt signal
Post by: Coding Badly on Apr 08, 2016, 12:29 am

You are welcome.

Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 08, 2016, 04:29 am
Added new features, the code is ready (I believe) for real hardware testing. Quite busy right now, but will report back with more results at a later date.

Debounce Interval Calculated Automatically:

No need to determine required debounce interval. This is determined by DUTY and MAX_HZ varibles. Debounce interval is calculated to occupy 50% of the HIGH period of the pulse. This interval expands until bounce has settled.
 
Three modes of operation:

1. pulseConditioner(DEBOUNCE);
Debounces/deglitches unwanted transitions at each end of the pulse, can be false triggered by noise during stable periods, near instantaneous response. Clean output follows input.

(http://i.imgur.com/J29GVyH.png)

2. pulseConditioner(STABLE_HIGH);
Debounces/deglitches unwanted transitions at each end of the pulse, can be false triggered by noise during stable high periods, immune to noise anywhere else in the signal. Near instantaneous response, output toggles at the first falling edge after any stable high period.

(http://i.imgur.com/scKLrt1.png)

3. pulseConditioner(STABLE_LOW);
Debounces/deglitches unwanted transitions at each end of the pulse, can be false triggered by noise during stable low periods, immune to noise anywhere else in the signal. Near instantaneous response, output toggles at the first rising edge after any stable low period.

(http://i.imgur.com/JtcJ4QH.png)

Latest code:

Code: [Select]
byte ledPin = 13;
byte inputPin = 2;
byte outputPin = 12;

const byte DEBOUNCE = 2;
const byte STABLE_HIGH = 1;
const byte STABLE_LOW = 0;

const byte DUTY = 50;                // 10-90%
const unsigned long MAX_HZ = 1000;   // 1-100000Hz
const unsigned long TIMEOUT = 5000;  // 1-4000000ms

byte inputState;
unsigned long minMicros, maxMicros;
volatile unsigned long nowMicros, prevMicros, elapsedMicros;
volatile byte cleanOutput, toggleOnFall, toggleOnRise, timeoutFlag;

void setup() {
  minMicros = (1000000 * DUTY) / (MAX_HZ * 100 * 2); // debounce interval
  maxMicros = TIMEOUT * 1000;
  pinMode(ledPin, OUTPUT);
  pinMode(outputPin, OUTPUT);
  Serial.begin(115200);
  attachInterrupt(0, onPulse, CHANGE);
}

void loop() {
  pulseConditioner(DEBOUNCE);
}

void onPulse()
{
  nowMicros = micros();
  elapsedMicros = nowMicros - prevMicros;
  prevMicros = nowMicros;

  if (elapsedMicros >= minMicros)  // ALL cases >= min time
  {
    inputState = PIND & 0x4;  // read pin 2
    (elapsedMicros >= maxMicros) ? timeoutFlag = 1 : timeoutFlag = 0;
    if (inputState)
    {
      cleanOutput = 1;               // debounced output
      toggleOnRise = !toggleOnRise;  // toggle on rising input
    }
    else
    {
      cleanOutput = 0;               // debounced output
      toggleOnFall = !toggleOnFall;  // toggle on falling input
    }
  }
}

void pulseConditioner(byte mode)
{
  if (mode == DEBOUNCE)
  {
    (cleanOutput) ? PORTB |= 0x10 : PORTB &= 0xEF; // pin12 HIGH : LOW
  }
  else if (mode == STABLE_HIGH)
  {
    (toggleOnFall) ? PORTB |= 0x10 : PORTB &= 0xEF; // pin12 HIGH : LOW
  }
  else  // mode == STABLE_LOW
  {
    (toggleOnRise) ? PORTB |= 0x10 : PORTB &= 0xEF; // pin12 HIGH : LOW
  }
}
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 08, 2016, 05:58 am
Doesn't letting the ISR handle the noise get a little CPU intensive? Because the ISR gets called on every transition?
Yes ... I'll need to see how it responds with external hardware. I see various threads where users usually get 2-4 extra interrupts with various devices. Whatever the practical frequency limit is, it gets reduced the noisier the signal is. A good thing is that many sensors send low frequency pulses and there's more than enough CPU cycles available.

My next testing (at a later date) will be using the pattern generator at high frequencies on a separate Arduino so it will be non-synchronous. I'll recheck the signals with a logic analyzer. Another test will be with a noisy relay that has some severe "bounce" issues.

My debounce switch tester/calibration catches bounces 12 to 20 micros in length while reporting them to look for N micros with no change. Grounding a jumper on the USB port box to make dirty switching, I only need 2000 micros to debounce that and I don't use interrupts to do it. Want the sketch?
Are you debouncing or making an oscilloscope? Both?
Sounds interesting ... I think I may have seen it a while back, but I wouldn't mind taking another look.
My intentions here are to see what can be accomplished signal conditioning interrupts in software by ignoring the unwanted ones. I would basically like to create a universal function that would be practical to use for low to mid-range frequencies from a wide range of sensors and devices.
Title: Re: Dealing with a noisy interrupt signal
Post by: GoForSmoke on Apr 08, 2016, 09:37 am
Sounds interesting ... I think I may have seen it a while back, but I wouldn't mind taking another look.
My intentions here are to see what can be accomplished signal conditioning interrupts in software by ignoring the unwanted ones. I would basically like to create a universal function that would be practical to use for low to mid-range frequencies from a wide range of sensors and devices.
I'm not finding that sketch under the name I remember and what's more I can't make one detect changes that short and print at the same time. Printing gets change detects closer to 300 us.

So I wrote something that collects change detects in an array and then spent a while trying to make it prettier and may still have a non-critical bug (finds no-bounce presses too often, not always but these are jumper buttons) but when it shows data that should be good and yes it gets sub-20us changes.

Maybe if your sketch makes data tables and prints those after collection you will get a tighter picture?

Code: [Select]

// bounce --- ground the jumper as a dirty button and see bounce
// some buttons are just as dirty, especially with a hard press
// if button state is stable >= 100ms it will print results

// it is possible to get 1 change per contact and break but no guarantee

byte jumperPin = 7; // using an UNO and a jumper I ground on the USB.
byte lastState = 0x80;

unsigned int tStart, tWait = 1000U; // wait is 1 second
byte dontPrintExtraLines;

unsigned long bounceMicroStart, bounceMicros, nowMicros, stableMicros = 100000UL;
unsigned int bounceTbl[ 250 ];
byte bounceCntr;
byte printFlag;


void setup( void )
{
  Serial.begin( 115200 );
  Serial.println( "\nserial connected..." );
  pinMode( jumperPin, INPUT_PULLUP );
}

void loop( void )
{
  if ( lastState != ( PIND & 0x80 ))
  {
    dontPrintExtraLines = 0;
    lastState ^= 0x80;
    nowMicros = micros();
    if ( !bounceCntr )      bounceMicros = 0;
    else  bounceMicros = nowMicros - bounceMicroStart;
    if ( bounceMicros >= stableMicros )   printFlag = 1;
    else
    {
      bounceTbl[ bounceCntr++ ] = bounceMicros & 0xFFFF;
      if ( bounceCntr >= 250 )    printFlag = 1;
    }
    bounceMicroStart = nowMicros;
    tStart = millis();
  }

  if ( printFlag || (( millis() & 0xFFFF ) - tStart >= tWait ))
  {
    if ( !dontPrintExtraLines )
    {
      dontPrintExtraLines = 1;

      Serial.println( "\n--------\n" );
      if ( bounceCntr >= 250 )
      {
        Serial.println( "full table, no stable!" );
      }
      else if ( !bounceCntr )
      {
        Serial.println( "no bounces? really?" );
      }

      if ( bounceCntr )
      {
        for ( byte i = 0; i < bounceCntr; i++ )
        {
          Serial.println( bounceTbl[ i ] );
        }
      }
    }
    printFlag = bounceCntr = 0;
    tStart = millis();
  }
}
Title: Re: Dealing with a noisy interrupt signal
Post by: Robin2 on Apr 08, 2016, 05:53 pm
I have just now found time to look at this more closely. The stuff I was testing earlier was just not working properly - it was responding to electrical noise rather than optical changes. I think I have now got past that stage. But, at the moment the hardware I have is far from ideal. I may get a specialist reflective optical detector on Monday.

My initial thoughts on this subject are what @aarg mentioned
Quote
Doesn't letting the ISR handle the noise get a little CPU intensive?
I only need a RISING interrupt which should reduce the load (I think).

As far as I can see the code in Reply #33 works by ignoring interrupts during a pre-determined interval after the first interrupt - which is how I would debounce a switch if I was polling it. But the difference is that if I was using polling I just would not check the I/O pin during the interval.

My inclination with the interrupts is to switch off the interrupt after the first trigger and not re-instate it until the interval has elapsed (and clearing out any interrupts-in-waiting first). I think that could significantly reduce the load on the Arduino. But the part I have not figured yet is how to re-instate the interrupt if the interval is short. However for my application I should not be getting more than 250 useful interrupts per second - i.e. one every 4 millisecs so there should be plenty of time for the code in loop() to re-instate the interrupt.

I guess it would not be too difficult to arrange for the first interrupt to switch of future external interrupts on that pin and start a timer interrupt that would re-instate the external interrupt after the interval. That should take ALL the debounce load off the CPU.

One thing I have not seen mentioned in the discussion (apart from @aarg's comment) is the need to use a few CPU cycles as possible so the Arduino has time to other stuff. The best debounce system is useless if there is no time left to make use of the debounced input.

I probably won't get time to do any more with this today.

...R
Title: Re: Dealing with a noisy interrupt signal
Post by: GoForSmoke on Apr 08, 2016, 07:19 pm
In the Mitsubishi white paper on led to led wireless (range 2cm) they got reliable serial using short and long pulses for 0 and 1 by taking around 4 (don't remember the range) HIGH reads in a row to establish a good short pulse and 7 to 10 to establish a long pulse.
They did that at 50K reads/second at less than 3V operation. I was unable to match it using Arduino regular commands, more like 22K. I tried that back in 2012.
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 08, 2016, 11:47 pm
@GoForSmoke: Thank you for your code and suggestions.

@Robin2:

Quote
As far as I can see the code in Reply #33 works by ignoring interrupts during a pre-determined interval after the first interrupt - which is how I would debounce a switch if I was polling it. But the difference is that if I was using polling I just would not check the I/O pin during the interval.
It checks the I/O pin only if the interval has expired, not during. For any interrupt within the interval, only the time variables are updated, then the CPU exits the ISR.

Quote
One thing I have not seen mentioned in the discussion (apart from @aarg's comment) is the need to use a few CPU cycles as possible so the Arduino has time to other stuff. The best debounce system is useless if there is no time left to make use of the debounced input.
Yes, this is true, however with my testing I'll be hogging as many CPU cycles as possible with really messy signals to get a better idea of what can be achieved.

Quote
I guess it would not be too difficult to arrange for the first interrupt to switch of future external interrupts on that pin and start a timer interrupt that would re-instate the external interrupt after the interval. That should take ALL the debounce load off the CPU.
I wouldn't worry about CPU load for your application. You mentioned earlier that at about 200Hz you were getting about 1000 counts (per sec I presume). This would create no more CPU load than measuring a 1kHz signal at the interrupt pin. If you use CHANGE mode, there will be twice as many interrupts and the CPU load will be similar to measuring a 2kHz signal at the interrupt pin.

Here's what I think is happening ... you getting several extra interrupts for each switching transition of the photo sensor. With 2 extra interrupts on each side of the pulse, and assuming roughly 50% duty cycle, the signal may look something like this: (http://i.imgur.com/vSmyn7h.png)
Notice there are 5 rising edges and 5 falling edges for a single pulse your trying to count. This would give 1000 counts when 200 is expected. Rather difficult to debounce using RISING or FALLING mode because the debounce interval would need to cover most of the signal's period.

With debounce or filtering, I believe there are valid cases for a hardware solution or a software solution or even both. I think in your case, if 2000 interupts per second is too high, then your signal should be very easy to filter with a 1ms RC circuit like this:
(http://i.imgur.com/F2f82Cb.png)

Below, I've attached an example you might want to try.
Title: Re: Dealing with a noisy interrupt signal
Post by: Robin2 on Apr 09, 2016, 10:15 am
I wouldn't worry about CPU load for your application. You mentioned earlier that at about 200Hz you were getting about 1000 counts (per sec I presume). This would create no more CPU load than measuring a 1kHz signal at the interrupt pin. If you use CHANGE mode, there will be twice as many interrupts and the CPU load will be similar to measuring a 2kHz signal at the interrupt pin.
I don't want to waste CPU cycles on 1000 interrupts if I can  reduce it to 200 - the Atmega 328 will be working at 8MHz and it has other stuff to do.

It is too early for either of us to speculate about the possible causes of too many interrupts on my project. Your little diagram would require two extra components - I have very limited space so I want to avoid that if I can.

Quote
Notice there are 5 rising edges and 5 falling edges for a single pulse your trying to count. This would give 1000 counts when 200 is expected. Rather difficult to debounce using RISING or FALLING mode because the debounce interval would need to cover most of the signal's period.
I'm not clear if your diagrams of the bouncing are to scale, and if so, what is the scale. The bouncing seems to take up a huge proportion of the cycle time.

...R
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 09, 2016, 03:26 pm
Quote
I don't want to waste CPU cycles on 1000 interrupts if I can  reduce it to 200 - the Atmega 328 will be working at 8MHz and it has other stuff to do.
Some options?

Quote
I'm not clear if your diagrams of the bouncing are to scale, and if so, what is the scale. The bouncing seems to take up a huge proportion of the cycle time.
Its to illustrate a few points. The scale of the bouncing doesn't matter very much if it occurs on each end of the pulse. If your pulses are much more sharp with only 10µs bounce at each end and you have 50% duty cycle, you'll still need a debounce interval > 50% of the signal's period (same as for the diagram). What if the duty is 60/40, 70/30 or higher?

I'm not sure how you've designed your optic sensor, but if you made the black/blocking portion or clear portion much greater than the other so you get (for example) 25% duty, then software debounce would be more practical and easier.

Also, if the signal is open-collector or open-drain type, then there should be an external pullup resistor somewhere. Adding a very physically small capacitor from the signal to GND might be all that's needed. In this case, FALLING mode is more appropriate.

Quote
It is too early for either of us to speculate about the possible causes of too many interrupts on my project. Your little diagram would require two extra components - I have very limited space so I want to avoid that if I can.
Why not use an analog input and run the Serial Plotter to get a picture of the signal?
Title: Re: Dealing with a noisy interrupt signal
Post by: Robin2 on Apr 09, 2016, 07:04 pm
Why not use an analog input and run the Serial Plotter to get a picture of the signal?
It does not exist in IDE 1.5.6 AFAIK

You seem to be doing what I suggested not to do " too early for either of us to speculate about the possible causes ..."

It turned out that the entire problem was caused by a lack of supression capacitors on my motor which was causing electrical rather than optical interference.

...R
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 10, 2016, 05:16 am
EDIT:

Initial test results for pulseConditioner(DEBOUNCE) measured on Saleae LogicPro8 Logic Analyzer.
Arduino Pro Mini uploaded with debounce code, Arduino UNO generates the signal.

With 1.255kHz input frequency, 17.92µs latency, 12,550 interrupts per second.
With 10.629kHz input frequency, 31.36µs latency, 63,776 interrupts per second.

For now, I'm going to continue working with CHANGE mode to improve the logic and latency. What I'd like to accomplish is having an efficient function for this that eliminates all noise. I'll also investigate using a simple method of monitoring CPU load.

I know it seems impractical using a dirty signal like this, but as long as the code can handle the noise correctly, then a simple solution for high CPU loading would be to use an RC filter in combination with the debounce code.

An advantage of CHANGE mode is that duty cycle information is preserved and timing slow pulse rates with 50% duty can be done in 1/2 cycle.

... any comments welcomed!
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 16, 2016, 03:16 pm
As far as I can tell, this debouncer in software prevents wasted CPU cycles caused by noisy interrupts. It uses timer2 with extended compare to give a debounce interval range of 1µs to 240 seconds. The main loop is empty. Test by touching ground with a jumper wire connected to pin2 and the built-in LED will toggle once per touch.

Code: [Select]
const byte inputPin = 2;
volatile word extendedCompare, extendedCounter;
unsigned long stableWidth = 125000; // µs (debounce interval)

ISR(TIMER2_COMPA_vect)
{
  extendedCounter++;
  if (extendedCounter >= extendedCompare)
  {
    TIMSK2 &= ~(1 << OCIE2A);  // disable timer compare interrupt
    EIFR = (1 << INTF0);       // clear pending inputPin interrupt
    EIMSK |= (1 << INT0);      // enable inputPin interrupts
    extendedCounter = 0;
  }
}

void inputPin_ISR()
{
  PINB |= _BV (5);          // toggle pin 13
  EIMSK &= ~(1 << INT0);    // disable inputPin interrupt
  TCNT2  = 0;               // reset timer counter
  TIFR2 = (1 << OCF2A);     // clear pending timer interrupt
  TIMSK2 |= (1 << OCIE2A);  // enable timer compare interrupt
}

void timer2Init() {
  stableWidth = constrain(stableWidth, 1, 240000000); // 1µs-4min
  const float clockResolution = 1000000.0 / F_CPU;
  unsigned long timerCycles, prescaledCycles;
  word prescaler;
  timerCycles = (stableWidth / clockResolution) - 1;
  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2  = 0; // reset counter
  TCCR2A |= (1 << WGM21); // turn on CTC mode
  if (timerCycles < 2048) { // 1-128µs, 1µs resolution
    TCCR2B |= (0 << CS22) | (1 << CS21) | (0 << CS20);
    prescaler = 8;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = prescaledCycles;
  } else if (timerCycles < 16384) { // 129-1024µs, 4µs resolution
    TCCR2B |= (1 << CS22) | (0 << CS21) | (0 << CS20);
    prescaler = 64;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = prescaledCycles;
  } else if (timerCycles < 65536) { // 1025-4096µs, 16µs resolution
    TCCR2B |= (1 << CS22) | (1 << CS21) | (0 << CS20);
    prescaler = 256;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = prescaledCycles;
  } else if (timerCycles < 262144) { // 4097-16384µs, 64µs resolution
    TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
    prescaler = 1024;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = prescaledCycles;
  } else if (timerCycles < 1048576) {  // 16385-65536µs, 256µs resolution
    TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
    prescaler = 1024;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = 3;
    extendedCompare = prescaledCycles >> 2;
  } else if (timerCycles < 4194304) { // 65537-262144µs, 1024µs resolution
    TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
    prescaler = 1024;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = 15;
    extendedCompare = prescaledCycles >> 4;
  } else if (timerCycles < 16777216) { // 262145-1048576µs, 4096µs resolution
    TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
    prescaler = 1024;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = 63;
    extendedCompare = prescaledCycles >> 6;
  } else { // 1048577-240000000µs, 16384µs resolution
    TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
    prescaler = 1024;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = 255;
    extendedCompare = prescaledCycles >> 8;
  }
  TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt
}

void setup()
{
  pinMode(inputPin, INPUT_PULLUP);
  DDRB |= _BV (5);  // pinMode (13, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(inputPin), inputPin_ISR, CHANGE);
  timer2Init();
}

void loop()
{
}


Results on UNO with clean input signal:


(http://i.imgur.com/0SHEF9q.png)



Results on UNO with noisy input signal:


(http://i.imgur.com/8e1SvEq.png)


Another test:


(http://i.imgur.com/3yBo0oi.png)
Title: Re: Dealing with a noisy interrupt signal
Post by: Robin2 on Apr 16, 2016, 05:00 pm
As far as I can tell, this debouncer in software prevents wasted CPU cycles
Is that using the concept I suggested in Reply #36 ?

...R
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 17, 2016, 12:49 am
@Robin2: Yes, thanks, your post got me researching the possibility of using a timer to temporarily turn off the input interrupt. Then I stumbled upon this thread  (http://forum.arduino.cc/index.php?topic=175874.0)that was attempting a similar approach but was incomplete. However, I needed a wider timing range and needed to use the timer as a monostable multivibrator (one-shot timer). More specifically, a retriggerable one-shot timer because the debounce timing re-starts after each invalid interrupt. This is why I called the timing interval "stableWidth" as it waits for a period of stability in the signal.
Title: Re: Dealing with a noisy interrupt signal
Post by: Robin2 on Apr 17, 2016, 10:24 am
I have bookmarked your Reply #43 for future reference.

...R
Title: Re: Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 20, 2016, 04:26 pm
The "debouncer" code now works automatically for all interrupt modes and external interrupt INT0 and INT1. The maximum input frequency for all modes is around 38 kHz when timer0 interrupt is disabled. With timer0 interrupt enabled, the maximum frequency is about 32kHz. Latency for all modes is about 3.6 µs.

Tested with an SPI pattern generator uploaded to an Arduino Pro Mini. The signal is squarewave with multiple transitions on each edge and other areas (34 total / cycle). The signal has frequency of 38.3 kHz and is connected to an UNO external interrupt pin (2 or 3).

For CHANGE mode, the clean output toggles.

(http://i.imgur.com/rtKtD1d.png)

For RISING and FALLING modes, the clean output toggles once for the required mode. Noise on both edges and in between is eliminated even though the opposite edge extends well beyond the stableWidth interval.

Noise on the opposite edge is a common problem that is difficult to eliminate. For a squarewave, a simple ignore interval will not work unless it extends beyond 50% of the waveform which severely limits the usable frequency range when debouncing. By knowing the previous stable state, this issue has been resolved.

Noise in between the signals is also ignored providing there is at least one high and one low stable interval.

FALLING mode:

(http://i.imgur.com/DHaQDoW.png)]

RISING mode:

(http://i.imgur.com/88dxqjd.png)

The code:

Code: [Select]
const byte intPin = 2; // can use pin 2 or 3
volatile byte intNum, notIntNum, intSense, intState, clockSelect;
volatile byte extendedCompare, extendedCounter;

unsigned long stableWidth = 125000; // 2-1000000µs interval

void setup()
{
  pinMode(intPin, INPUT_PULLUP);
  pinMode (LED_BUILTIN, OUTPUT);
  //pinMode (11, OUTPUT); // for monitoring OC2A output
  //TIMSK0 = 0;           // for testing with timer0 disabled
  attachInterrupt(digitalPinToInterrupt(intPin), inputPin_ISR, CHANGE);
  stabilizerInit();
}

void loop()
{
}

ISR(TIMER2_COMPA_vect)
{
  extendedCounter++;
  if (extendedCounter >= extendedCompare)
  {
    extendedCounter = 0;                    // reset extended counter
    TCCR2B = 0;                             // stop timer clock
    TCNT2 = 0;                              // reset timer counter
    intState = (PIND & _BV (intPin)) == 0;  // read intPin
    EIFR = intNum;                          // reset pending intPin interrupt
    EIMSK |= intNum;                        // enable intPin interrupt
  }
}

void inputPin_ISR()
{
  if (intSense == 1) {                      // if CHANGE mode
    // your code here
    PINB |= _BV (5);                        // toggle pin 13

  } else if (intSense == 2 && intState) {   // if FALLING mode and previously stable high)
    // your code here
    PINB |= _BV (5);                        // toggle pin 13

  } else if (intSense == 3 && !intState) {  // if RISING mode and previously stable low)
    // your code here
    PINB |= _BV (5);                        // toggle pin 13
  }
  TCCR2B = clockSelect;                     // start timer clock with required prescaler
  TCNT2 = 0;                                // reset timer counter
  EIMSK &= notIntNum;                       // disable intPin interrupt
  EIFR = intNum;                            // reset pending intPin interrupt
}

void stabilizerInit() {
  const float clockResolution = 1000000.0 / F_CPU;
  unsigned long timerCycles, prescaledCycles;
  word prescaler;

  intNum = digitalPinToInterrupt(intPin) + 1; // get interrupt mask
  notIntNum = ~intNum;
  intSense = (EICRA >> (intNum - 1) * 2) & 3; // get interrupt mode
  stableWidth = constrain(stableWidth, 2, 1000000); // 2-1000000µs
  timerCycles = (stableWidth / clockResolution) - 1;
  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2  = 0; // reset counter
  extendedCounter = 0; // reset extended counter
  TCCR2A |= (1 << WGM21) | (1 << COM2A0); // CTC mode | toggle OC2A on compare match
  if (timerCycles < 2048) { // 1-128µs, 0.5µs resolution
    TCCR2B |= (0 << CS22) | (1 << CS21) | (0 << CS20);
    clockSelect = 2;
    prescaler = 8;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = prescaledCycles;
  } else if (timerCycles < 16384) { // 129-1024µs, 4µs resolution
    TCCR2B |= (1 << CS22) | (0 << CS21) | (0 << CS20);
    clockSelect = 4;
    prescaler = 64;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = prescaledCycles;
  } else if (timerCycles < 65536) { // 1025-4096µs, 16µs resolution
    TCCR2B |= (1 << CS22) | (1 << CS21) | (0 << CS20);
    clockSelect = 6;
    prescaler = 256;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = prescaledCycles;
  } else if (timerCycles < 262144) { // 4097-16384µs, 64µs resolution
    TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
    clockSelect = 7;
    prescaler = 1024;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = prescaledCycles;
  } else if (timerCycles < 1048576) {  // 16385-65536µs, 256µs resolution
    TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
    clockSelect = 7;
    prescaler = 1024;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = 3;
    extendedCompare = prescaledCycles >> 2;
  } else if (timerCycles < 4194304) { // 65537-262144µs, 1024µs resolution
    TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
    clockSelect = 7;
    prescaler = 1024;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = 15;
    extendedCompare = prescaledCycles >> 4;
  } else { // 262145-1000000µs, 4096µs resolution
    TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
    clockSelect = 7;
    prescaler = 1024;
    prescaledCycles = timerCycles / prescaler;
    OCR2A = 63;
    extendedCompare = prescaledCycles >> 6;
  }
  TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt
}
Title: Re: [SOLVED] Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 21, 2016, 12:46 am
Analog signal connected to input test: PWM (490.5 Hz) with 50%  duty, RC filter = 1K/0.1µF.
Software set to FALLING mode, 400 µs stableWidth.

Note - can use: stableWidth (µs) = 200,000 / 490

Note: the trigger level on the analyzer does not match the input pin trigger level.

(http://i.imgur.com/fibfStT.png)

Input zoomed in on each edge to reveal transients:

(http://i.imgur.com/vhoW47X.png)(http://i.imgur.com/msWC3Ja.png)

Output pin 13 zoomed:

(http://i.imgur.com/MExQj7T.png)(http://i.imgur.com/PjfBBrL.png)
Title: Re: [SOLVED] Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 28, 2016, 03:32 am
This is an attempt to see what the top end input frequency could be for debouncing CHANGE mode interrupts.

In the previous code, I focused more on expanding the range of the 8-bit timer, providing automatic pin and mode detection and on providing debouncing for all modes. The maximum input frequency was 25kHz with 3.6µs latency.

To find the top end frequency, I've stripped out all conditional logic and bit shifting. Discovered an improved way to work with the timer - starting and stopping the clock rather than enabling and disabling the timer2 compare match interrupt.

Clean signal for testing highest input frequency and measuring latency:
(http://i.imgur.com/6Rk0vTv.png)

Input signal with 22 transitions per cycle having "bouncing" on leading and trailing edge of signal: 
(http://i.imgur.com/FZgYSak.png)

Input signal with 34 transitions per cycle having "bouncing" on leading and trailing edge and also during high and low states of the signal:
(http://i.imgur.com/fIpxUlm.png)

This exceeded my expectations ... 53kHz input frequency with about 3.2µs latency. Incredibly messy signal completely recovered!

Note: timer0 interrupt was disabled. When enabled, could still get 53kHz with the odd dropout of a pulse, or around 42kHz without any dropout when the timer0 interrupt fires.

Pattern Generator:
Code: [Select]
#include <SPI.h>
//byte pattern[] = {B01010000, B00000010, B10010110, B10111111, B11110101, B11111111, B11110101, B01010101, B01000000, B00000000}; // 34 transitions
//byte pattern[] = {B00000000, B00000010, B10010110, B10111111, B11111111, B11111111, B11111111, B01010101, B01000000, B00000000}; // 22 transitions
byte pattern[] = {B00000000, B00000000, B11111111, B11111111, B11111111, B11111111, B11111111, B00000000, B00000000, B00000000};   // 2 transitions

void setup() {
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV2);
  SPI.setDataMode(SPI_MODE1);
}

void loop() {
  for (int i = 0; i < 10; i++) {
    SPI.transfer(pattern[i]);
  }
}

Debouncer Code:
Code: [Select]
const byte intPin = 2;
byte stableWidth = 3;  // 2-128µs interval

void setup()
{
  pinMode(intPin, INPUT_PULLUP);
  pinMode (LED_BUILTIN, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(intPin), inputPin_ISR, CHANGE);
  stabilizerInit();
}

void loop()
{
}

ISR(TIMER2_COMPA_vect)
{
  TCCR2B = 0;        // stop timer counter
  TCNT2 = 0;         // clear timer counter
  EIFR = 1;          // clear pending inputPin interrupt
  EIMSK |= 1;        // enable inputPin interrupt
}

void inputPin_ISR()
{
  // your code here
  PINB |= _BV (5);   // toggle pin 13
  // required code
  TCCR2B = 2;        // start timer counter with prescaler = 8
  TCNT2 = 0;         // clear timer counter
  EIMSK &= 0;        // disable inputPin interrupt
}

void stabilizerInit() {
  const float clockResolution = 1000000.0 / F_CPU;
  unsigned long timerCycles, prescaledCycles;
  byte prescaler;
  TIMSK0 = 0; // turn off timer0 (optional)
  stableWidth = constrain(stableWidth, 2, 128);
  timerCycles = (stableWidth / clockResolution) - 1;
  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2  = 0; // reset counter
  TCCR2A |= (1 << WGM21); // turn on CTC mode
  TCCR2B |= (0 << CS22) | (1 << CS21) | (0 << CS20);
  prescaler = 8;
  prescaledCycles = timerCycles / prescaler;
  OCR2A = prescaledCycles;
  TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt
}
Title: Re: [SOLVED] Dealing with a noisy interrupt signal
Post by: dlloyd on Apr 29, 2016, 12:27 am
Code and waveforms for post 47 have been updated.

Updated performance: Up to 38kHz external interrupt signal with severe noise can be fully recovered. Works for all modes (CHANGE, FALLING, RISING) and INT0 or INT1. Only 3.6µs latency. Debouncing done in hardware (uses timer2) and ISRs, main loop is empty.

To test, use stableWidth = 200000/Maximum Input Hz (µs)