chipKIT® Development Platform

Inspired by Arduino™

How accurate is the Delay() function?

Created Mon, 23 Mar 2015 18:24:10 +0000 by tbvalex


tbvalex

Mon, 23 Mar 2015 18:24:10 +0000

Hey!

I'm using a chipkitUno32 and I need my board to produce a triggering signal for two cameras. The signal need to be as accurate as possible. (30,3 fps = 33 ms interval) The duration of the trigger should be at least 1ms. Do anyone know how accurate the Delay function is? The trigger is started from a C++ software (using the digit '1') My code:

int Trigger = 13;   // select the pin for the Trigger (Digital Out)

char c;       // variable to store the data from the serial port

void setup() {
  pinMode(Trigger,OUTPUT);    // declare the Triggers pin as output
  Serial.begin(9600);        // connect to the serial port
}

void loop () {
  
  
  c = Serial.read();      // read the serial port

  if (c == '1') {
    while (Serial.available() == 0){
      digitalWrite(Trigger,HIGH);
      delay(2);
      digitalWrite(Trigger, LOW);
      delay(31);
      }
    }
    
    else {
     digitalWrite(Trigger, LOW); 
      }
    
  
}

majenko

Mon, 23 Mar 2015 19:02:10 +0000

delay() is pretty accurate (more accurate than on an Arduino, certainly). However, how you're using it is not accurate.

One thing that people often forget is that every instruction takes a finite amount of time to execute. That includes things like digitalWrite().

So although you are delaying for 33ms the time taken to execute one iteration of your while() loop will take longer than 33ms to execute. Not a huge amount longer, but over time it adds up to a noticeable drift in the timing.

Instead you should be using the millis() function to examine the current time and decide if you want to switch on or switch off the trigger output. Even better, use the micros() function and work in even higher resolution.

The trick is, when you turn on or off the trigger you record the current time, and then when the current time minus the recorded time reaches the target time (2000µS for instance) you know it's time to change the state of your output.

Something along the lines of (though I haven't tested this):

void loop() {
    static uint32_t ts = 0;
    boolean running = false;
    boolean state = false;

    if (Serial.available()) {
        char inch = Serial.read();
        if(!running && inch == '1') {
            ts = micros();
            running = true;
            state = 1;
            digitalWrite(trigger, HIGH);
        } else if (running) {
            running = false;
            digitalWrite(trigger, LOW);
        }
    }

    if (running) {
        if (state && (micros() - ts >= 2000)) {
            ts = micros();
            digitalWrite(trigger, LOW);
            state = false;
        } else if (!state && (micros() - ts >= 31000)) {
            ts = micros();
            digitalWrite(trigger, HIGH);
            state = true;
        }
    }
}

tbvalex

Tue, 24 Mar 2015 13:24:52 +0000

Thanks for input!

But shouldnt these declarations be outside of the loop(): static uint32_t ts = 0; boolean running = false; boolean state = false;

Another thing is that when I upload the sketch it immediately goes in to HIGH and wait for my trigger init.

I will run my camera software with both solutions and report back with the statistics.

I guess you can also use direct port manipulation instead of DigitalWrite to increase the speed further?


majenko

Tue, 24 Mar 2015 14:35:11 +0000

Actually, the booleans should have been "static boolean", and no, they stay in the loop.

Yes, you can use direct port manipulation for the best speed possible.


tbvalex

Thu, 26 Mar 2015 08:09:42 +0000

Thanks!

Say if you want to produce pulses in the KHz or even MHz region, is that possible? Or do you have to use some other way of producing pulses?


majenko

Thu, 26 Mar 2015 14:24:04 +0000

For those kind of frequencies you might be better off using PWM or timers.