Delay measurement using due

im trying to measure delay between two peaks of two different sinewaves, but there seems to be some logic i seem to miss, please can u verify

type or pasteconst int inputPin = A0;
const int outputPin = A1;
float inputVoltage;
float outputVoltage;
float inpeak = 0;
float outpeak = 0;
String input;
unsigned long startTime = 0, endTime = 0, Delay = 0;
 
void setup() {
  Serial.begin(9600);
}
 
void loop() {
  float inputRaw = analogRead(inputPin);
  float outputRaw = analogRead(outputPin);
  input = Serial.readString();
 
  if (input == "stop") {
    Serial.println(inpeak);
    Serial.println(outpeak);
    Serial.println(startTime);
    Serial.println(endTime);
    Delay = endTime - startTime;
    Serial.println(Delay);
    inpeak = 0;
    outpeak = 0;
  } else {
    inputVoltage = (inputRaw * 5.0) / 1023.0;
    outputVoltage = (outputRaw * 5.0) / 1023.0;
 
    if (inputVoltage > inpeak) {
      inpeak = inputVoltage;
      startTime = micros();
    }
 
    if (outputVoltage > outpeak) {
      outpeak = outputVoltage;
      endTime = micros();
    }
  }
} 

In embedded programming, code is only half the story. The other half is the hardware.

So to make sense of the software we need to know how your hardware is wired. That involves posting a schematic. Hand drawn is fine, a collection of pictures linked with lines is not.

Having said that your code is all over the place and makes little sense.

Is just a nonsense. There is no need to use a floating point number, or to convert it into voltages, just the raw numbers will do. Also read String takes at least a second to execute, do not use this.

hey sorry for this , i will upload a flowchart for clarity

You were asked for a schematic not a flow chart.

If I was a moderator I would ban you for a time until you learned how to behave on this forum.

It is the same set of the usual helpers that read almost all the posts so we get to read virtually all the posts.

this is how the overall system works, the arduino is just used to calculate peaks of the input and output wave and then take the timestamps of those peaks and compare , the data is converted to voltages just to get more clarity

bro i cannot upload the schematic due to some confidentiality concerns

I'd already set up my function generator, oscilloscope and Arduino by the time I read Mike's comments about double posting, so I'll continue.

unsigned long startTime = 0, endTime = 0, Delay = 0;

Whilst startTime, and endTime should be unsigned long, it is better to have delay just as long.
The value of delay can be positive or negative, depending upon the phases of the two signals.

Due to the delay between taking samples, and any noise on the signals it is not guaranteed that the two peaks detected are on adjacent peaks.
The peaks detected could be many cycles apart.

I don't have an Arduino Due, so I used an Uno R3 to do some tests on.
I realise the Due will be somewhat faster.

During testing, I made some changes to your code:

  • I changed Delay from unsigned long to long
  • I changed the string to be read from 'stop' to 's' to save typing
  • Using direct port manipulation, I made 2 pins go high whilst the ADC was sampling.
  • I printed an extra blank line to make the results easier to read on the Serial Monitor.
code used
const int inputPin = A0;
const int outputPin = A1;
const int monitorPin = 12;
const int monitorPin2 = 8;
float inputVoltage;
float outputVoltage;
float inpeak = 0;
float outpeak = 0;
String input;
unsigned long startTime = 0, endTime = 0;
long Delay = 0;

void setup() {
  Serial.begin(9600);
  pinMode(monitorPin, OUTPUT);
  pinMode(monitorPin2, OUTPUT);
}

void loop() {
  PORTB = B00010000;
  float inputRaw = analogRead(inputPin);
  PORTB = B00000000;
  PORTB = B00000001;
  float outputRaw = analogRead(outputPin);
  PORTB = B00000000;
  input = Serial.readString();

  if (input == "s") {
    Serial.println("");
    Serial.println(inpeak);
    Serial.println(outpeak);
    Serial.println(startTime);
    Serial.println(endTime);
    Delay = endTime - startTime;
    Serial.println(Delay);
    inpeak = 0;
    outpeak = 0;
  } else {
    inputVoltage = (inputRaw * 5.0) / 1023.0;
    outputVoltage = (outputRaw * 5.0) / 1023.0;

    if (inputVoltage > inpeak) {
      inpeak = inputVoltage;
      startTime = micros();
    }

    if (outputVoltage > outpeak) {
      outpeak = outputVoltage;
      endTime = micros();
    }
  }
}

The input and output signals came from a function generator.
The signals were 1kHz sinewaves, amplitude 4V pk-pk, with a 2.5V offset, and a phase difference of 90°.

Here are some results I got on the serial monitor:

Here is an oscilloscope trace, showing what is happening:

  • Channel 1 - yellow trace - signal on 'inputPin'.
  • Channel 2 - red trace - high whilst ADC reads A0 input.
  • Channel 3 - blue trace - signal on 'outputPin'.
  • Channel 4 - green trace - high whilst ADC reads A1 input.

You can see that the ADC only samples the 'inputPin' and 'outputPin' once during the trace.

When do you think the next sample takes place?

Click here to find the answer.

The next sample takes place nearly 1 second later.

How long will it take to detect a peak at that rate?

At a slower time base, the pulses on channels 2 & 4 are very narrow, but can be seen if you look carefully. The measurement shown on the screen indicates a period of 998ms.

(The frequency of the signal has been reduced to 300mHz for clarity.)

Does that help to explain why you get poor results?

1 Like

Removing the line:

input = Serial.readString();

makes the sample rate to go up from 1Hz to 4.16kHz.

This means that we need a different way to stop the measurements and print the results.

I used the techniques from the BlinkWithoutDelay example from the IDE, to print the results at regular intervals.
Also I didn't convert the raw ADC data to volts every reading. I just did it once, after the peak was detected, ready for printing.

const int inputPin = A0;
const int outputPin = A1;
const int monitorPin = 12;
const int monitorPin2 = 8;

float inputVoltage;
float outputVoltage;
float inpeak = 0;
float outpeak = 0;

unsigned long startTime = 0, endTime = 0;
long Delay = 0;
unsigned long previousMillis = 0;
int interval = 20;

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

void loop() {
  PORTB = B00010000;
  float inputRaw = analogRead(inputPin);
  PORTB = B00000000;
  PORTB = B00000001;
  float outputRaw = analogRead(outputPin);
  PORTB = B00000000;
  
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = previousMillis + interval;

    Serial.println("");
    inputVoltage = (inpeak * 5.0) / 1024.0;
    outputVoltage = (outpeak * 5.0) / 1024.0;
    Serial.println(inputVoltage);
    Serial.println(outputVoltage);
    Serial.println(startTime);
    Serial.println(endTime);
    Delay = endTime - startTime;
    Serial.println(Delay);
    inpeak = 0;
    outpeak = 0;
  } else {    

    if (inputRaw > inpeak) {
      inpeak = inputRaw;
      startTime = micros();
    }

    if (outputRaw > outpeak) {
      outpeak = outputRaw;
      endTime = micros();
    }
  }
}

Here are two 100Hz signals with a 90° phase difference (delay =2.5ms).
The serial monitor indicates a delay of 2488µs = 2.488ms.


The green trace is now displaying the serial monitor data.
I've increased the Baud rate, so we spend less time printing.

hey i tried to run your code in a proteus simulation but there seems to be some fluctuations in delay readings .


The sampling period is not synchronised with the input signal.

This means that the sampling starts at some random part of the input waveform,

Depending upon which of the two signals gets it's peak detected first, you will get results of around 2500µs or -7500µs, for the particular case of 100Hz signal (period 10000µs) and phase difference of 90°.
The results could also be influenced by any noise on the signals.

sorry this may seem like too stupid question, but how can i improve it ?

You were only looking at the positive peaks,

Maybe an improved method would wait until it came to a negative peak on the input signal before starting to look for the positive peaks on the two signals, and then stop when it sees the next negative peak. That way you should be sure that the two peaks were from the same cycle of the waveform.
That might get rid of the is it +90° or -270° issue.

There will always be some variance in the timing depending on whether you hit the exact peak, or not.
Take as many ADC readings as you can, as fast as possible, don't do unnecessary calculations.

I don't know how the Due will perform, compared with the Uno R3 that I used.

The direct port manipulation that I put in as part of the debugging process is specific to the ATmega328P/AVR microcontroller used by the Uno, and will need removing for the Due.

hey this is the logic i came up with , do let me know your feedback


const int inputPin = A0;
const int outputPin = A1;
const int negativeThreshold = 50;
int inPeak = 0;
int outPeak = 0;
unsigned long startTime = 0;
unsigned long endTime = 0;
enum State { WAIT_FOR_NEGATIVE,
             MEASURE_PEAKS };
State state = WAIT_FOR_NEGATIVE;
void setup() {
  Serial.begin(115200);
}
void loop() {
  int readingA0 = analogRead(inputPin);
  int readingA1 = analogRead(outputPin);
  switch (state) {
    case WAIT_FOR_NEGATIVE:
      if (readingA1 == negativeThreshold) {
        state = MEASURE_PEAKS;
        inPeak = 0;
        outPeak = 0;
        startTime = 0;
        endTime = 0;
        Serial.println("Cycle started (negative peak detected)");
      }
      break;
    case MEASURE_PEAKS:
      if (readingA0 > inPeak) {
        inPeak = readingA0;
        startTime = micros();
      }
      if (readingA1 > outPeak) {
        outPeak = readingA1;
        endTime = micros();
      }
      if (readingA1 == negativeThreshold) {
        long delayTime = (long)endTime - (long)startTime;
        float voltageA0 = inPeak * 5.0 / 1024.0;
        float voltageA1 = outPeak * 5.0 / 1024.0;
        Serial.print("Cycle ended. Positive peak on A0: ");
        Serial.print(voltageA0, 3);
        Serial.print(" V at ");
        Serial.print(startTime);
        Serial.print(" us; A1: ");
        Serial.print(voltageA1, 3);
        Serial.print(" V at ");
        Serial.print(endTime);
        Serial.print(" us; Delay: ");
        Serial.print(delayTime);
        Serial.println(" us");

        state = WAIT_FOR_NEGATIVE;
      }
      break;
  }
}
```case MEASURE_PEAKS:
      if (readingA0 > inPeak) {
        inPeak = readingA0;
        startTime = micros();
      }
      if (readingA1 > outPeak) {
        outPeak = readingA1;
        endTime = micros();
      }
      if (readingA1 == negativeThreshold) {
        long delayTime = (long)endTime - (long)startTime;
        float voltageA0 = inPeak * 5.0 / 1024.0;
        float voltageA1 = outPeak * 5.0 / 1024.0;
        Serial.print("Cycle ended. Positive peak on A0: ");
        Serial.print(voltageA0, 3);
        Serial.print(" V at ");
        Serial.print(startTime);
        Serial.print(" us; A1: ");
        Serial.print(voltageA1, 3);
        Serial.print(" V at ");
        Serial.print(endTime);
        Serial.print(" us; Delay: ");
        Serial.print(delayTime);
        Serial.println(" us");

        state = WAIT_FOR_NEGATIVE;
      }
      break;
  }
}

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