This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/LAUNCHXL-F28027F: SINGLE-PHASE PLL - (WRONG OUTPUT)

Part Number: LAUNCHXL-F28027F
Other Parts Discussed in Thread: CONTROLSUITE

Tool/software: Code Composer Studio

Dear TI members,

I am trying to implement a single phase PLL to determine the phase of an ac signal. I have managed to make my system measure an AC signal, and output a phase (using CCSUITE 1ph_PLL template) however the output phase of the PLL has been moving up and down and sometimes displaying terrible transients as seen in the next figs:

I also included an offset with the signal generator so that the ADC captures both positive and negative values

My ADC also displays wrong results at some values of my grid frequency (it works for 100Hz, 500Hz, 1000Hz), but it shows terrible results for 60Hz for example. 

ISR:

interrupt void ADCINT1_ISR(void)				
{
static Uint16 *AdcBufPtr1 = Va;				// Pointer to buffer - static so never lost
static Uint16 *AdcBufPtr2 = Vb;
//static Uint32 *AdcBufPtr3 = Vc;
static volatile Uint16 GPIO34_count = 0;		// Counter for pin toggle
static SPLL_1ph_IQ spll1;

	//ADC has 12 bits, values range from 0 to 4095

	PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;		// Must acknowledge the PIE group

//--- Manage the ADC registers     
	AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;		// Clear ADCINT1 flag

//--- Read the ADC result
	*AdcBufPtr1++ = 	AdcResult.ADCRESULT0;     	// Read the result

	spll1.AC_input =(long)(AdcResult.ADCRESULT0<<3);
	SPLL_1ph_IQ_FUNC(&spll1);

	//*AdcBufPtr2++ = 	AdcResult.ADCRESULT1;
	*AdcBufPtr2++ = 	spll1.theta[1];
	//*AdcBufPtr3++ = 	AdcResult.ADCRESULT2;

//--- Brute-force the circular buffers
	if( AdcBufPtr1 == (Va + ADC_BUF_LEN) )
	{
		AdcBufPtr1 = Va;						// Rewind the pointer to beginning
	}
	if( AdcBufPtr2 == (Vb + ADC_BUF_LEN) )
	{
		AdcBufPtr2 = Vb;						// Rewind the pointer to beginning
}

	if(DEBUG_TOGGLE == 1)
	{
		GpioDataRegs.GPATOGGLE.bit.GPIO18 = 1;		// Toggle the pin
	}

//--- Example: Toggle GPIO34 at a 0.5 sec rate (connected to the LED on the ControlSTICK).
//             (1/50000 sec/sample)*(1 samples/int)*(x interrupts/toggle) = (0.5 sec/toggle)
//             ==> x = 25000
	if(GPIO34_count++ > 25000)					// Toggle slowly to see the LED blink
	{
		GpioDataRegs.GPBTOGGLE.bit.GPIO34 = 1;	// Toggle the pin
		GPIO34_count = 0;						// Reset the counter
	}
}

}

MAIN:

#include "Lab.h"
	#include "SPLL_1ph_IQ.h"
	#include "Solar_IQ.h"
	#include "IQmathLib.h"

	#define GRID_FREQ    100
	#define PI 3.141592653589
	#define ISR_FREQUENCY 250000.0
	Uint16 DEBUG_TOGGLE = 1;					// Used for realtime mode investigation test

	Uint16 Va[ADC_BUF_LEN];
	Uint16 Vb[ADC_BUF_LEN];
	//Uint32 Vc[ADC_BUF_LEN];

	//DEFINE STRUCTS

	SPLL_1ph_IQ spll1;
	SPLL_1ph_IQ_NOTCH_COEFF spll_notch_coef1;

	void main(void) {


	//--- CPU Initialization

	InitSysCtrl();						// Initialize the CPU (FILE: SysCtrl.c)
	InitGpio();							// Initialize the shared GPIO pins (FILE: Gpio.c)
	InitPieCtrl();						// Initialize and enable the PIE (FILE: PieCtrl.c)
	InitWatchdog();						// Initialize the Watchdog Timer (FILE: WatchDog.c)
	
	//--- Peripheral Initialization
	InitAdc();							// Initialize the ADC (FILE: Adc.c)
	InitEPwm();							// Initialize the EPwm (FILE: EPwm.c)


	//--- Enable global interrupts
	asm(" CLRC INTM, DBGM");			// Enable global interrupts and realtime debug

	//PLL STARTS HERE
	// ------------- Software PLL for Grid Tie Applications ----------


	#define B0_LPF SPLL_Q(166.877556)
	#define B1_LPF SPLL_Q(-166.322444)
	#define A1_LPF SPLL_Q(-1.0)

	SPLL_1ph_IQ_init(GRID_FREQ,_IQ21((long)(1.0/ISR_FREQUENCY)),&spll1);
	SPLL_1ph_IQ_notch_coeff_update(((long)(1.0/ISR_FREQUENCY)),(long)(2*PI*ISR_FREQUENCY*2),(long)0.00001,(long)0.1,&spll1);

	//--- Main Loop
	while(1)							// endless loop - wait for an interrupt
	{
		asm(" NOP"); //program works only with interruptions ISR

	}
}

PLL (FROM controlSUITE):

#ifndef SPLL_1ph_IQ_H_
#define SPLL_1ph_IQ_H_

#define SPLL_Q _IQ21
#define SPLL_Qmpy _IQ21mpy

//*********** Structure Definition ********//
typedef struct{
	int32	B2_notch;
	int32	B1_notch;
	int32	B0_notch;
	int32	A2_notch;
	int32	A1_notch;
}SPLL_1ph_IQ_NOTCH_COEFF; 

typedef struct{
	int32	B1_lf;
	int32	B0_lf;
	int32	A1_lf;
}SPLL_1ph_IQ_LPF_COEFF;

typedef struct{
	int32	AC_input;
	int32	theta[2];
	int32	cos[2];
	int32	sin[2];
	int32	wo;
	int32	wn;
	
	SPLL_1ph_IQ_NOTCH_COEFF notch_coeff;
	SPLL_1ph_IQ_LPF_COEFF	lpf_coeff;
		
	int32	Upd[3];
	int32	ynotch[3];
	int32	ylf[2];
	int32	delta_t;
}SPLL_1ph_IQ;

//*********** Function Declarations *******//
void SPLL_1ph_IQ_init(int Grid_freq, long DELTA_T, SPLL_1ph_IQ *spll);
void SPLL_1ph_IQ_notch_coeff_update(long delta_T, long wn,long c2, long c1, SPLL_1ph_IQ *spll_obj);
void SPLL_1ph_IQ_FUNC(SPLL_1ph_IQ *spll1);

//*********** Macro Definition ***********//
#define SPLL_1ph_IQ_MACRO(v)						 																																															\
	/* Phase Detect*/								 																																															\
	v.Upd[0]=SPLL_Qmpy(v.AC_input,v.cos[1]);		 																																															\
	/* Notch Filter*/								 																																															\
	v.ynotch[0]=-SPLL_Qmpy(v.notch_coeff.A1_notch,v.ynotch[1])-SPLL_Qmpy(v.notch_coeff.A2_notch,v.ynotch[2])+SPLL_Qmpy(v.notch_coeff.B0_notch,v.Upd[0])+SPLL_Qmpy(v.notch_coeff.B1_notch,v.Upd[1])+SPLL_Qmpy(v.notch_coeff.B2_notch,v.Upd[2]);	\
	/* Update Upd Array for future use*/			 																																															\
	v.Upd[2]=v.Upd[1];								 																																															\
	v.Upd[1]=v.Upd[0];								 																																															\
	/* LPF*/										 																																															\
	v.ylf[0]=-SPLL_Qmpy(v.lpf_coeff.A1_lf,v.ylf[1])+SPLL_Qmpy(v.lpf_coeff.B0_lf,v.ynotch[0])+SPLL_Qmpy(v.lpf_coeff.B1_lf,v.ynotch[1]);																											\
	/* Update Array for future use*/				 																																															\
	v.ynotch[2]=v.ynotch[1];						 																																															\
	v.ynotch[1]=v.ynotch[0];						 																																															\
	v.ylf[1]=v.ylf[0];								 																																															\
	/*VCO*/											 																																															\
	v.wo=v.wn+v.ylf[0];								 																																															\
	/* integration process */						 																																															\
	v.sin[0]=v.sin[1]+SPLL_Qmpy((SPLL_Qmpy(v.delta_t,v.wo)),v.cos[1]);																																											\
	v.cos[0]=v.cos[1]-SPLL_Qmpy((SPLL_Qmpy(v.delta_t,v.wo)),v.sin[1]);																																											\
	if(v.sin[0]>SPLL_Q(0.99))						 																																															\
		v.sin[0]=SPLL_Q(0.99);						 																																															\
	else if(v.sin[0]<SPLL_Q(-0.99))					 																																															\
		v.sin[0]=SPLL_Q(-0.99);						 																																															\
	if(v.cos[0]>SPLL_Q(0.99))						 																																															\
		v.cos[0]=SPLL_Q(0.99);						 																																															\
	else if(v.cos[0]<SPLL_Q(-0.99))					 																																															\
		v.cos[0]=SPLL_Q(-0.99);						 																																															\
	v.theta[0]=v.theta[1]+SPLL_Qmpy(SPLL_Qmpy(v.wo,SPLL_Q(0.159154943)),v.delta_t);																																								\
	if(v.sin[0]>SPLL_Q(0.0) && v.sin[1]<=SPLL_Q(0.0))					 																																										\
	{																	 																																										\
		v.theta[0]=SPLL_Q(0.0);											 																																										\
	}																	 																																										\
	v.theta[1]=v.theta[0];												 																																										\
	v.sin[1]=v.sin[0];													 																																										\
	v.cos[1]=v.cos[0];													 																																										\
		

#endif /* SPLL_1ph_IQ_H_ */

I suspect the following (but I am not sure how to address it)

1- I need to insert the offset to the signal inside the code  instead of in the signal generator 

2- I need to send a sinusoid signal with 0 offset to the PLL.

update -> I did that my subtracting the signal by its average, but I still get the same output phase

 

3- Also, I noticed that my graph function works for 100Hz when I have a buffer of size 500, but as I change the size of the buffer, the graph also changes, so I am thinking that maybe the system does work, but I will only see the right output at certain buffer sizes.

Any help/advice would be welcome.

Thank you,

Victor

  • you should plot something that repeats,

    also the buffer that you are using is it triggered?
  • Dear Manish, thank you for your reply!

    My buffer is updated inside the ISR:

    *AdcBufPtr1++ = AdcResult.ADCRESULT0; // Read the result

    spll1.AC_input =(long)(AdcResult.ADCRESULT0<<3);
    SPLL_1ph_IQ_FUNC(&spll1); //calls the PLL function

    *AdcBufPtr2++ = spll1.theta[1]; //sends the new theta output of the PLL to the buffer

    Whenever the pointer to the buffer reaches the end value, pointer is reset to the initial value. The system works only with interruptions, so my main is a NOP command.

    I don't do anything else to the buffer besides what is shown here. What would I need to do to "trigger" my buffer?
  • Victor,

    Just like a scope, if your data is not triggered you will see runny waveforms, which may look glitchy.

    You need to trigger the buffer for your to make sense of the data, we have serveral modules that do so. 

    you can use the mdoule 

    C:\ti\controlSUITE\libs\app_libs\solar\v1.2\IQ\source

    DLOG 1ch or 4ch 

    refer to the user guide for solar lib on steps to include it in your system 

  • Dear Manish,

    I appreciate your assistance in this matter. I am currently trying to implement the triggered scope using the DLOG_4CH module.

    Should I call the function  DLOG_4CH from inside the ISR from my ADC?  (DLOG_4CH_IQ_FUNC(&dlog_4ch1) 

    When I try to call the function from inside the ISR I complains that dlog_4ch1 has not been defined. And if I try to define it inside the ISR, then I lose the initialization parameters that I set for it in my main. 

  • you will need to declare a global that is visible inside the the ISR and the main.c, we have several examples that use this,

    see

    C:\ti\controlSUITE\development_kits\TMDSSOLARUINVKIT_v100\MicroInv_F2803x
  • I see. I managed to do that with the command extern (so that ISR.c could use same variables from main.c). Thank you! 

    Below is the graph of the PLL working at 60Hz 

    One last question that I had is related to the ADC values. I am working with an ADC of 12 bits, so values range from 0 - 4095.

    When sending the measured values to the D LOG _4CH I am just sending dval1= AdcResult.ADCRESULT0<<3; However, if I wanted to send negative signals as well, instead of only positive values, what would be the best approach in this case? I thought about subtracting the average (2047) from the ADC measurements, but I am wondering if there is a better way to do this.

    Thank you,

    Victor