Measuring phase difference between two 50Hz sine waves with the dsPIC30F4011

Reader Nand wrote in with a query about using the dsPIC30F4011 to measure the phase difference between two 50Hz sine waves. This could, for example, be used to measure the phase difference between voltage and current waveforms in a reactive electrical load. The example program below shows how this can be done using one of the dsPIC’s built-in timers. A brief explanation of the program’s operation follows after the code.

// dsPIC30F4011 phase difference measurement example
// Written by Ted Burke
// Last updated 4-3-2013
// Measure the phase difference between two 50Hz sine waves.
// The leading sine wave is assumed to be connected to AN0.
// The lagging sine wave is assumed to be connected to AN1.
// Both sine waves are assumed to vary between 0V and 5V.
// The phase difference is measured as the time delay
// between the time each signal crosses a threshold (2.5V).

#include <xc.h>
#include <stdio.h>
#include <libpic30.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

// Function prototype for analog input function
unsigned int read_analog_channel(int channel);

int main()
	// Configure AN0-AN8 as analog inputs
	ADCON3bits.ADCS = 15;  // Tad = 266ns, conversion time is 12*Tad
	ADCON1bits.ADON = 1;   // Turn ADC ON
	// Setup UART
	U1BRG = 48;            // 38400 baud @ 30 MIPS
	U1MODEbits.UARTEN = 1; // Enable UART

	// Configure Timer 1
	PR1 = 65535;          // Set the Timer 1 period (max 65535)
	TMR1 = 0;             // Reset Timer 1 counter
	T1CONbits.TCKPS = 2;  // Prescaler (0=1:1, 1=1:8, 2=1:64, 3=1:256)
	T1CONbits.TON = 0;    // Disable Timer 1
		// Reset timer to zero
		TMR1 = 0;
		// Wait until AN0 is below voltage threshold
		while (read_analog_channel(0) >= 512);
		// Wait for AN0 to rise above voltage threshold
		while (read_analog_channel(0) < 512);
		// Start timer to measure phase difference
		T1CONbits.TON = 1;
		// Wait for AN1 to rise above voltage threshold
		while (read_analog_channel(1) < 512);
		// Stop timer
		T1CONbits.TON = 0;
		// Print time delay. Note 64:1 timer prescaling.
		// Also note that 30000 clock cycles is 1ms at 30 MIPS.
		printf("Phase diff = %4.2f ms\n", 64.0 * TMR1 / 30000.0);
	return 0;

// This function reads a single sample from the specified
// analog input. It should take less than 5us when the
// microcontroller is running at 30 MIPS.
// The dsPIC30F4011 has a 10-bit ADC, so the value
// returned is between 0 and 1023 inclusive.
unsigned int read_analog_channel(int channel)
	ADCHS = channel;          // Select the requested channel
	ADCON1bits.SAMP = 1;      // Start sampling
	__delay32(30);            // 1us delay @ 30 MIPS
	ADCON1bits.SAMP = 0;      // Start Converting
	while (!ADCON1bits.DONE); // Should take 12 * Tad = 3.2us
	return ADCBUF0;

I built this program using Microchip’s XC16 C compiler. I don’t use MPLAB, so I used the following build script to invoke the compiler. To use it, save the C code as “main.c” and the build script as “build.bat” in the same folder. Then just open a command window, navigate to that folder and type “build.bat” to compile the program.

xc16-gcc main.c -mcpu=30F4011 -Wl,--script=p30F4011.gld
if errorlevel 0 xc16-bin2hex a.out

The output file is “a.hex”. I use the PICkit 2 software to download the compiled program onto the dsPIC.

Code explanation

There’s not much to it really:

  • Lines 29-31 enable 8 analog inputs (although only AN0 and AN1 are actually used).
  • Lines 33-35 enable the UART, which is used to transmit the measured phase difference as plain text to be displayed on the PC (as a time in ms).
  • Lines 37-41 configure Timer 1 to run with a prescaler value of 64:1. This means that the timer counter TMR1 increments once every 64 instruction cycles. With the dsPIC running at 30 MIPS, that means TMR1 increments once every 2.1333us. This is what determines the time resolution of the phase measurement. TMR1 is a 16-bit register, so its maximum value is 65535, after which is resets to zero. The longest phase difference that can be measured at this prescaler setting is therefore 139.8ms.
  • The main loop of the program (lines 43-66) simply measures the time delay beween the voltage on AN0 crossing a threshold (in the positive-going direction) and the voltage on AN1 crossing the same threshold. I have set the threshold to 2.5V. If each sine waves varies between 0V and 5V, this corresponds to the what I would regard as the zero-crossing point in the waveform.
  • Each time the phase difference is measured, it is printed to the screen via the UART (line 65). The result is converted from timer ticks into milliseconds using the timer prescaler ratio (64:1) and the number of clock cycles per millisecond (30,000).
  • I’m using the PICkit 2 software’s UART tool to display the result (see image below).

Here’s a screenshot of a few phase difference measurements displayed in the PICkit 2 software’s UART tool. Since I don’t have two 50Hz waveforms on hand to test the program, I used an RC circuit and a switch to create a reasonably consistent time difference between two rising voltage signals. Obviously, since 50Hz waveforms have a period of 20ms, actual phase difference measurements will be considerably shorter than these example readings.

Screenshot of phase difference measurement displayed in the PICkit 2 software's UART tool

This entry was posted in Uncategorized. Bookmark the permalink.

4 Responses to Measuring phase difference between two 50Hz sine waves with the dsPIC30F4011

  1. M S SHREEDHAR says:

    Please.. Can you tell me how to measure phase difference for pic 16f877A.?

    Thank you..

  2. larry says:

    If i want to develop a similar system to measure the phase difference between two 5 kHz waveforms, do you think this would be sufficient for this or should i find a faster processor?

    • batchloaf says:

      Hi Larry,

      The dsPIC30F4011 is probably adequate to do what you need. However, but if you require a very precise phase measurement, you might want to use a slightly different approach to what I did in the example above.

      Because I was measuring the phase difference between 50Hz waveforms, the time resolution of my measurement could be quite low (on the time scale of the microcontroller, the 20ms period of a 50Hz waveform is very large). I therefore used two analog inputs to measure the waveforms and compared each one against a constant value (512 digital units) to find the zero crossing points. I used a timer to measure the time delay between the zero crossing in waveform 1 and the zero crossing in waveform 2. The primary limitation on the precision of this measurement is the relatively long time for each analog-to-digital conversion (around 5us according to my comments).

      To measure the phase difference between the two waveforms with much higher precision, the easiest option is probably to use a comparator to “rectangularise” each of the waveforms before inputtting them into digital inputs on the microcontroller. Using this approach, it should be possible to measure the phase difference with a precision of 60ns or less. Presumably, you would want to consider using an external crystal (rather than the internal fast RC oscillator which I used in this example) to ensure accuracy.

      What are the characteristics of your waveforms?

      • Are the waveforms sinusoidal?
      • Is the 5kHz frequency constant?
      • Are the two waveforms of equal amplitude?
      • If so, what is the max and min voltage of the waveforms?
      • Are the waveforms “clean”, or is there a lot of noise / interference? If so, the phase measurement should probably be recorded several times and an average taken.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s