Edge detection without Interrupt

Hi folks !

Can someone try this sketch and help me to understand why the result on serial monitor is jumping at regular interval.

I'm feeding the input pin with a 10 Hz 0-5volts square wave (so low time is 100ms ).

I want to simulate a tachometer reading at 600 rpm. (I'know how to do with interrupt but not for now).

Thanks !


int rpm;
unsigned long tachTime;
unsigned long previousmicros;

void setup(){
  pinMode(2,INPUT);
  Serial.begin(2000000); 
}

void loop()
{
  byte n1=digitalRead(2);//read a pin and after 5 ms read that pin again
  delay(5);
  byte n2=digitalRead(2);


  //action only on a high to low transition
if (n1==HIGH & n2==LOW)
{tachTime = micros() - previousmicros;
    previousmicros = micros();rpm = 60000000 / tachTime; }         

//Serial.print("Rpm ");
Serial.println(rpm);

}

Try

if (n1==HIGH && n2==LOW)

Thanks but same behaviour ...

Seems odd that you get 5-6 Serial.prints every 35ms or so.

What happens if you put the Serial.print code inside the if? (just print when it changes)

It become a bit more stable but there is always a small jump.

It run on a genuine Uno now. But it was the same on a previous Nano.

However, you can now at least see the 10Hz signal in the timestamps.

I suspect every now and again the high to low transition occurs outside the 5ms window so you get spikes - 629 rpm is approximately an internal of 95ms.

Yes for the time stamp. I'tried a .01 capacitor between pin and ground but its not better...

Anyway thanks a lot for your help my friend. I'will continue to search.

That is not going to be reliable. What happens when the input switch states anywhere BUT between those two digitalReads? Answer: You will not see it.

Instead, read the input. Compare it to the previous read, and save the result of that compare. Then SAVE the new value as the previous value. Now look at the result of the compare, and, do whatever you need to do when you see the edge(s) of interest.

You will also need to make sure that whatever processing you do in loop() takes much less time than a single pulse cycle of the input, or, again, you will miss edges or entire pulses.

Ok, interesting. I'will work on that tomorrow with the help of Google translate Hi !

Thanks again !

Don't think that will help.

Your logic is flawed.. there are other things happening in your loop that use processing time.

You are assuming that nothing happens between reading the pin the 2nd time, and the subsequent loop cycle... that is not the case. You have other logic that takes time... and the signal could change during that period.

At 10Hz one cycle takes 100mS, and it is low during 50ms

I ran your sketch and I get sequences of 580 and 610. I don't think that logic will produce the right results.

You can set a flag when a high pulse is detected. Then, check if the flag is set and the input is low and then you have a transition. Compute the rpm and reset the flag. This is more accurate and you don't have to use delay(). I ran your sketch with this modification, and the reading at 10Hz is stable. It starts jumping at higher frequencies.

50ms low time yes , my mistake. In fact my goal is to detect only the clean part of the pulse (not the noise) by software with a simple clipper circuit. After that, i will adjust timing. Rpm range would be 500-6000 rpm (10-100hz).

A better way to find a falling edge is to take one sample per loop and compare this new sample to the previous sample.

int rpm;
unsigned long tachTime;
unsigned long previousmicros;

void setup()
{
  pinMode(2, INPUT);
  Serial.begin(2000000);
}

boolean oldState = false;

void loop()
{
  boolean newState = digitalRead(2) == HIGH;

  //action only on a transition
  if (newState != oldState)
  {
    // State has changed
    oldState = newState;

    //action only on a falling transition
    if (!newState)
    {
      // Falling (HIGH to LOW) edge
      unsigned long newMicros = micros();

      tachTime = newMicros - previousmicros;
      previousmicros = newMicros;
      rpm = 60000000 / tachTime;

      //Serial.print("Rpm ");
      Serial.println(rpm);
    }
  }
}

You got it ! It is very stable now. But i' think that my approach is bad. Because at the end of the pulse, (when the high voltage spike come back) there is also very fast high to low transitions (oscillations) that the mcu will see. So i will have to go back to the drawing board...

Thank you everybody for your help !

Are you driving an inductive load?

Here's an approach which deals with the bouncing trailing edge by requiring a stable low period before recording the time to determine the rpm period. Tested with a 10 Hz square wave and gave 600 rpm.

int rpm;
unsigned long tachTime;
unsigned long previousmicros;
unsigned long lastTimeSignalWasHigh;
unsigned long stableLowPeriod = 1000; //1 ms stable low
boolean newInterval = false;

void setup()
{
  pinMode(2, INPUT);
  Serial.begin(115200); 
}

void loop()
{
    
  if (digitalRead(2)) //signal level is high
  {
    lastTimeSignalWasHigh = micros();
    newInterval = true;
  }
  
  // will execute when signal reads low
  else if (newInterval && (micros() - lastTimeSignalWasHigh >= stableLowPeriod))
  {
    newInterval = false;//only one print per period
   
    unsigned long newMicros = micros();

    tachTime = newMicros - previousmicros;
    previousmicros = newMicros;
    rpm = 60000000 / tachTime;

    Serial.print("Rpm ");
    Serial.println(rpm);
  }
}

Simply remember when the last VALID edge occurred, and ignore any others that occur so soon after that they cannot be valid.

No John, there is no peripherals connected when i run the test. Later il will drive a mini servo as indicator.
Ray, it is exactly what i want to do.
Oh, Cattledog i'will try youre sketch tokay. You give me a good starting point.

Update I' just ran a test and it work ! I'Have a DDS signal generator to simulate the coil pack signal after clipping down to 5volts. And ajusting the stable period required do the job nicely.
Next step will be to test it on the motorcycle...

image

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.