1

I'm trying to use pulseIn with elapsed time using millis(), I don't want to use interrupts instead of pulseIn (I need pulseIn because I need accurate pulse time), the elapsed time doesn't need to be very accurate, so to calculate the elapsed time I've added to the elapsed time the time the arduino is blocked, The problem is the calculated elapsed is very inaccurate, I suspect it sometimes never enters if(elapsed > 1000*60*2), Why is that? How can I fix that?

Thanks.

Code

unsigned long startTime = 0;
unsigned long elapsed = 0;
unsigned int val = 0;
uint32_t time_wait_pulse = 0;

void setup(){
  startTime = millis();
}

void f(){
  uint32_t time = 0;
  time += pulseIn(pin, LOW);
  if(time > 0) {
   ...
   time_wait_pulse += time;
  } else {
   time_wait_pulse += 1000000; //default pulseIn timeout (micros)
  }
}

void loop(){
    elapsed = millis() - startTime + (unsigned long)(time_wait_pulse/1000);
    if(elapsed > 1000*60*2){
      val++;
      startTime = millis();
      time_wait_pulse = 0;
    }

     noInterrupts();
     f();
     interrupts();
}

2 Answers 2

3

millis() is using interrupts to work. When deactivating interrupts for your function f(), you prevent that the value returned by millis() can actually change in that time.

So if the interrupt associated with millis() occurs during the execution of f(), you will lose time in your measurement and it will be inaccurate. It will get worse the more time the code spends with interrupts off vs on. micros() will still be updated without interrupts on (since it directly reads the register of Timer0), but it will still lose time after about 500ms, since then it cannot compensate for multiple overflows in that time period.

The way to go forward depends on your requirements. Is it really needed, that you disable interrupts for the pulseIn() measurement? What accuracy do you get in both cases (interrupts on vs off) and what accuracy do you need? If you absolutely need to turn off interrupts, only do it for the shortest time (only around pulseIn()). You can built your own time keeping analog to millis() using a 16bit Timer (you didn't specify what Arduino you are using), which would give you way more time, that the microcontroller can spend without interrupts on before loosing time in the measurement.You can also use a different prescaler to further increase this time, though sacrificing precision for it.

1

You can time reasonably long intervals using Timer 2 (on the Atmega328) as described in my page about timers.

The example there is this:

void startTimer1 ()
  {
  // reset Timer 1
  TCCR1A = 0;
  TCCR1B = 0;
  // zero it
  TCNT1 = 0; 
  TIFR1 |= bit (TOV1);  // clear overflow flag
  // start Timer 1
  TCCR1B =  bit (CS10) | bit (CS12);  //  prescaler of 1024
  }  // end of startTimer1
 
unsigned long getTimer1Reading ()
  {
  unsigned long elapsed = TCNT1;
  if (TIFR1 &  bit (TOV1))
     elapsed += 65536;
  return elapsed;   
  }  // end of getTimer1Reading
 
void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  }  // end of setup

void loop ()
  {
  Serial.println ("Starting ...");
  Serial.flush ();

  startTimer1 ();
 
  delay (7560);
  float secs = (1.0 / F_CPU * 1024) * getTimer1Reading ();

  Serial.print ("Time taken = ");
  Serial.print (secs);
  Serial.println (" seconds.");

  } // end of loop

The code above uses Timer 1 to time an interval up to 8.388 seconds. It users a prescaler of 1024, which means each "count" of timer 1 is 1/16e6 * 1024 of a second (0.000064 seconds, or 64 µs). Since it can count up to 65536 we can time 0.000064 * 65536 = 4.194304 seconds. Then the timer overflows, but we can test the "overflow flag" and know the overflow occurred. That lets us time up to 8.388608 seconds before we miss the fact that there was a second overflow.

This code works even if interrupts are off, because the timing is done by the hardware timer.

Output on my Uno:

Starting ...
Time taken = 7.56 seconds.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.