Measuring "phase" difference between two square waves

I need to output a dc voltage based on the phase difference between two signals. The input is two square waves with the same frequency but one of them will either lead or lag the other. How can I generate a dc voltage corresponding to the difference in phase between the two signals? The dc output voltage needs to be able to either be positive or negative depending on whether the second signal is leading or lagging the first.

I have tried measuring the time that each signal goes high using if(digitalRead(2) == HIGH), t1 = micros(); and same thing for the other input. Then the output voltage would be a function of the difference. i.e. vout = (t2-t1)*5/1000000 (equation of a line with slope of 5 and divided by 1000000 since t is in microseconds. This method is giving seemingly random outputs.

Any suggestions or ideas on why this doesn't work and/or how I can accomplish this?

The problem is in the code you didn't post.

Also tell us which Arduino you're using and how it's wired up.

It would also be a good idea to state the frequency of the pulses. If it can vary, what's the range?

BTW, you cannot directly generate a variable dc output. PWM yes. You can convert that to variable dc with an rc filter but that may or may not work in your application, it depends on what you want to control with the dc.

I'm using an Arduino Uno. Using digital pins 12 and 13 as the input channels.

Here is my code:

#include <math.h>

void setup() {
pinMode(12, INPUT);
pinMode(13, INPUT);

void loop()
unsigned long t1, t2;

if (digitalRead(12) == HIGH)
t1 = micros();
if (digitalRead(13) == HIGH)
t2 = micros();

float vout = 5*(t2-t1)/1000000;
Serial.println(" = t1");
Serial.println(" = t2");
Serial.println(" = difference");
Serial.println(" = vout");

The frequency of the input square waves is 40kHz. The output dc voltage is going to control a voltage controlled oscillator. I need to be able to either subtract or add (based on whether the phase difference is positive or negative) the dc voltage to a set "control voltage" of 5V.

At 40 kHz I'd be using interrupts and counting clock cycles through timer2 for a much more accurate timing.

You're using the micros() function to do timing of a wave that lasts 25 microseconds, while that function has a resolution of 4 microseconds.

For your analog out: the best you can do is put out a PWM signal, smooth that to a 0-5V signal, and as you need negative as well you'll have to apply a -2.5V offset to that. PWM has 8-bit resolution, so you have 256 total steps in your output.

One of the hardest parts you have to deal with is code efficiency. You have 25 microseconds, that's 400 clock cycles, in which you get two interrupts which you have to process plus calculating the difference plus setting the output ports.

So you'll have to start by dropping all those digitalRead() and analogWrite() functions, and go for direct register reading/writing (you don't need to read any ports if you're using interrupts anyway).

You'll also have to drop all Serial.print() calls, as those are way too slow, even if you set your baud rate to something not so sluggish as the default 9600, such as 115200 bps. You may get away with printing 1-2 characters each cycle but still it's not too good an idea to do it each cycle - which is 40,000 times per second - even Superman will have a hard time reading that fast.

Anyway, you're approaching the limits of what an Arduino can do here, but with efficient programming you should be able to get it done.

Otherwise you may consider using a faster processor like the ESP8266 (80 MHz, giving you five times the number of clock cycles to do stuff - it'll happily handle this 40 kHz signal, I've been timing much higher frequencies than this), or the even faster Teensy (no experience with that one).

One more thing: no floating point maths allowed in this, it's too slow. Integer math only, and where possible try to use bit shifts instead (so make sure you divide by nice round numbers like 64 or 128, not by 100, so instead of doing a division in code you do a bitwise right shift - or a left shift for multiplications)

You are communication at 9600 baud, that means you can send 960 characters per second, or one character every 1mS. As your input frequency is 40KHz the one cycle is 25uS. So in effect you are not looking at your input signal fast enough to measure the time.

So write the code to measure the time first and then display it.

It dosn't help that you are not measuring the time correctly anyway. You want to set t1 when pin 12 becomes high not when it is high, the same goes for pin 13. This is known as a state change and their is an example of how to do this in the Arduino's IDE.

I didn't even analyse the code that deeply, but indeed no proper time measurement done there.

40 kHz, digitalRead(), Serial.print() - that are things that simply don't go well together. No need to go further. Conclusion is simple: the code will have to be rewritten from scratch.

Low pass filtering of output of XOR gate gives voltage proportional to magnitude of phase difference. But I don't know if there is an easy way to get sign of the difference too. Maybe with help of a latch?

Also if you want to make a PLL you may look at HC4046 chip.

I need to output a dc voltage based on the phase difference between two signals.

This sounds like a job for superman!!!

Actually..... you are probably referring to what they call a 'phase detector' of some sort.

Some kind of asynchronous logic circuit that outputs a positive square pulse when a slave signal lags the reference (up to some amount). The width of pulse equal to the time difference between signals. And output a negative pulse if slave signal leads (up to some amount). This is assuming that one of those square wave signals can always be the reference......while the other one is a slave signal.

I don't know if anybody has suggested this to you but what you want is called a phase discriminator and a seriously crazy easy thing to do in hardware. Even if you can't take the hardware road have a look at them if you can as background material.

The reason your code doesn't work is because you are not hunting the edges and therefore you are not getting the time difference between two rising edges (for example) but just a time between when they are both logic 1.
If your first test waited for a logic 0 and then took a time snapshot at the first logic 1 reading and then started looking for the same thing on the second input using the same method you'd get better results (although at 40kHz I would expect a fair amount of jitter in the readings still and maybe even a complete miss if the phase angle is small.)