Dear all,
I'm using SPLL_1ph routine for lock my sine wave to grid wave.
The lock is working, but i have a distorsion betwween two signals.
I'm using C2000 f28027 e my ADC SOC is the pwm used to create the sin-wave.
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.
Dear all,
I'm using SPLL_1ph routine for lock my sine wave to grid wave.
The lock is working, but i have a distorsion betwween two signals.
I'm using C2000 f28027 e my ADC SOC is the pwm used to create the sin-wave.
Are you observing both the voltage sensed and PLL lock through the PWM DAC's?
Is this connected to the solar explorer board or your own board?
How is the AC connected to the output?
Regards
Manish Bhardwaj
Hi Manish,
I am observing the PLL lock through EPwm3Regs.CMPA.half.CMPA with an RC low pass filter (cutoff frequency 5kHz), and the
grid voltage is a sine wave (50 Hz) obtained by a function generator (then sampled by ADC unit of the Piccolo C2000 control stick).
If you have any question I am happy to answer!
Thanks a lot
Michele,
To observe locking you will need to observe both the signals on the PWM DAC, the PWM DAC can induce some distortion,
So both should be on the PWM DAC, from your description this does not seem to be the case?
Regards
Manish Bhardwaj
Sorry, i don't understand.
Grid wave is analog voltage, so i must convert through ADC unit, on the contrary PLL lock signal
is digital voltage then i must convert through PWM DAC. am I right?
Regards
Michele to compare apples to apples, you will need to put both the signal on the PWM DAC,
see below excerpt from Solar Explorer software, {controlSUITE\development_kits\SolarExplorer_v1.0\SolarExplorer_PVInverter_F2803x}
inv_meas_vol_inst =((long)((long)Vac_FB<<12)-_IQ24(0.5))<<1; // shift to convert to Q24
PwmDacCh1 = (int16)_IQtoIQ15(spll1.sin[0]<<3);
PwmDacCh2 = (int16)_IQtoIQ15(inv_meas_vol_inst);
So put both the signals on the PWM DAC and compare, are you doing this right now?
Dear Manish,
I compared two signal on the PWM-DAC (like you suggested), but the distorsion remain.
Then I think there isn't problem into the DAC (PWM + filter), but the distorsion is caused by SPLL_1ph routine.
SPLL_1ph_MACRO(spll1);
Vac_in=(long)((long)AdcResult.ADCRESULT1<<9);
spll1.AC_input=Vac_in>>1;
InvSine = (long)(spll1.sin[0])>>6; // InvSine is in Q15
Thank you for your patience
Michele,
from the code above your AC signal is going only positive, this will add DC offset to your voltage signal, you need to subtract and make the signal go +ve value to -ve value, the way we have done it in the solar explorer kit software, this should resolve the issue.
Regards
Manish Bhardwaj
Of course. I hope it goes well
Thanks
#include "PeripheralHeaderIncludes.h"
#include "DSP2802x_EPwm_defines.h" // useful defines for initialization
#include "sgen.h"
#include "DSP28x_Project.h"
#include "IQmathLib.h"
#include "SPLL_1ph.h"
#include "SineAnalyzer_diff.h"
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// FUNCTION PROTOTYPES
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void DeviceInit(void);
void InitFlash(void);
void MemCopy(Uint16 *SourceAddr, Uint16* SourceEndAddr, Uint16* DestAddr);
void cpu_timer0_isr(void);
void ADC_init(void);
void EPWM_init(void);
//void InitPieCtrl(void);
#define PI 3.14156
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// VARIABLE DECLARATIONS - GENERAL
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Used for running BackGround in flash and the ISR in RAM
extern Uint16 RamfuncsLoadStart, RamfuncsLoadEnd, RamfuncsRunStart;
Uint16 duty_cycle_A=500; // Set duty 50% initially
Uint16 duty_cycle_B=500; // Set duty 50% initially
Uint16 duty_cycle_C=500; // Set duty 50% initially
//SGENT_1 sgen=SGENT_1_DEFAULTS;
SGENT_3D sgen=SGENT_3D_DEFAULTS;
int x11,x12,x13,x21,x22,x23;
unsigned int pippo=0, peppe;
Uint16 Adc_Results,i;
int32 Vac_in;
_iq15 InvSine,InvSine1,appoggio_InvSine1,VrmsReal, InvSine3,VavgReal,Vfreq,InvSine2,Theta_pi,sen_theta,theta,kappa,theta_old=0;
_iq16 fase;
float var_float;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// FUNCTION
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void ADC_init(void){
EALLOW;
AdcRegs.ADCCTL1.bit.ADCREFSEL = 0; // use internal band gap reference
AdcRegs.ADCCTL1.bit.ADCBGPWD = 1; // power up band gap
AdcRegs.ADCCTL1.bit.ADCREFPWD = 1; // power up reference
AdcRegs.ADCCTL1.bit.ADCPWDN = 1; // power up rest of ADC
AdcRegs.ADCCTL1.bit.ADCENABLE = 1; // enable ADC output
for(i=0; i<5000; i++){} // wait 60000 cycles = 1ms (each iteration is 12 cycles)
AdcRegs.ADCCTL1.bit.INTPULSEPOS = 1; // create int pulses 1 cycle prior to output latch
//EOC = end of conversion event, SOC = start of conversion event
AdcRegs.INTSEL1N2.bit.INT1SEL = 1; // ADCCH1 (ADC-A1) EOC causes ADCInterrupt1
// AdcRegs.INTSEL1N2.bit.INT1CONT = 1; // set ADCInterrupt 1 to auto clr (continuous conversion)
AdcRegs.INTSEL1N2.bit.INT1CONT = 0; // clear ADCINT1 flag to begin a new set of conversions
AdcRegs.ADCINTFLG.bit.ADCINT1 = 0; // clear interrupt flag for ADCINT1
AdcRegs.INTSEL1N2.bit.INT1E = 1; // enable ADCInterrupt1; 0=none, 1=ADCInt1, 2=ADCInt2
// AdcRegs.ADCINTSOCSEL1.bit.SOC1 = 1; // ADCInterrupt1 causes SOC1
AdcRegs.ADCINTSOCSEL1.all=0x0000; // No ADCInterrupt will trigger SOCx
AdcRegs.ADCINTSOCSEL2.all=0x0000;
AdcRegs.ADCSOC1CTL.bit.CHSEL= 1; // convert ADC-A1 (CH1) when SOC1 is received
AdcRegs.ADCSOC1CTL.bit.ACQPS = 6; // set S/H window to 6 clk cycles (117ns)
AdcRegs.ADCSOC1CTL.bit.TRIGSEL=9; //Epwm3 adc socA trigger source
EDIS;
AdcRegs.ADCSOCFRC1.all = 0x2; // kick start ADC by causing an ADCInterrupt1 event
}
void EPWM_init(void){
#define period 1680 // 60kHz when PLL is set to 0xC (60MHz)
// period 500 // 120kHz when PLL is set to 0xC (60MHz)
//****************************Epwm 2**************************
EPwm2Regs.TBPRD = period; // Set timer period, PWM frequency = 1 / period
EPwm2Regs.TBPHS.all = 0; // Time-Base Phase Register
EPwm2Regs.TBCTR = 0; // Time-Base Counter Register
EPwm2Regs.TBCTL.bit.PRDLD = TB_IMMEDIATE; // Set Immediate load
EPwm2Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP; // Count-up mode: used for asymmetric PWM
EPwm2Regs.TBCTL.bit.PHSEN = TB_DISABLE; // Disable phase loading
EPwm2Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_DISABLE;
EPwm2Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1;
EPwm2Regs.TBCTL.bit.CLKDIV = TB_DIV1;
// Setup shadow register load on ZERO
EPwm2Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm2Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
EPwm2Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO; // load on CTR=Zero
EPwm2Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO; // load on CTR=Zero
// Set Compare values
EPwm2Regs.CMPA.half.CMPA = duty_cycle_A; // Set duty 50% initially
EPwm2Regs.CMPB = duty_cycle_B; // Set duty 50% initially
// Set actions
EPwm2Regs.AQCTLA.bit.ZRO = AQ_SET; // Set PWM2A on Zero
EPwm2Regs.AQCTLA.bit.CAU = AQ_CLEAR; // Clear PWM2A on event A, up count
EPwm2Regs.AQCTLB.bit.ZRO = AQ_SET; // Set PWM2B on Zero
EPwm2Regs.AQCTLB.bit.CBU = AQ_CLEAR; // Clear PWM2B on event B, up count
//****************************Epwm 3**************************
EPwm3Regs.TBPRD = period; // Set timer period, PWM frequency = 1 / period
EPwm3Regs.TBPHS.all = 0; // Time-Base Phase Register
EPwm3Regs.TBCTR = 0; // Time-Base Counter Register
EPwm3Regs.TBCTL.bit.PRDLD = TB_IMMEDIATE; // Set Immediate load
EPwm3Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP; // Count-up mode: used for asymmetric PWM
EPwm3Regs.TBCTL.bit.PHSEN = TB_DISABLE; // Disable phase loading
EPwm3Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_DISABLE;
EPwm3Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1;
EPwm3Regs.TBCTL.bit.CLKDIV = TB_DIV1;
// Setup shadow register load on ZERO
EPwm3Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm3Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
EPwm3Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO; // load on CTR=Zero
EPwm3Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO; // load on CTR=Zero
EPwm3Regs.CMPA.half.CMPA = duty_cycle_C; // Set duty 50% initially
EPwm3Regs.CMPB = duty_cycle_C; // Set duty 50% initially
EPwm3Regs.AQCTLA.bit.ZRO = AQ_SET; // Set PWM2A on Zero
EPwm3Regs.AQCTLA.bit.CAU = AQ_CLEAR; // Clear PWM2A on event A, up count
EPwm3Regs.AQCTLB.bit.ZRO = AQ_SET; // Set PWM2B on Zero
EPwm3Regs.AQCTLB.bit.CBU = AQ_CLEAR; // Clear PWM2B on event B, up count
// SOC for PLL
EPwm3Regs.ETSEL.bit.SOCAEN = 1;
EPwm3Regs.ETSEL.bit.SOCASEL = 3; // Use PRD event as trigger for ADC SOC
EPwm3Regs.ETPS.bit.SOCAPRD = 2; // Generate pulse on 2nd event
//****************************Epwm 4**************************
EPwm4Regs.TBPRD = period; // Set timer period, PWM frequency = 1 / period
EPwm4Regs.TBPHS.all = 0; // Time-Base Phase Register
EPwm4Regs.TBCTR = 0; // Time-Base Counter Register
EPwm4Regs.TBCTL.bit.PRDLD = TB_IMMEDIATE; // Set Immediate load
EPwm4Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP; // Count-up mode: used for asymmetric PWM
EPwm4Regs.TBCTL.bit.PHSEN = TB_DISABLE; // Disable phase loading
EPwm4Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_DISABLE;
EPwm4Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1;
EPwm4Regs.TBCTL.bit.CLKDIV = TB_DIV1;
// Setup shadow register load on ZERO
EPwm4Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm4Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
EPwm4Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO; // load on CTR=Zero
EPwm4Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO; // load on CTR=Zero
EPwm4Regs.CMPA.half.CMPA = duty_cycle_C; // Set duty 50% initially
EPwm4Regs.CMPB = duty_cycle_C; // Set duty 50% initially
EPwm4Regs.AQCTLA.bit.ZRO = AQ_SET; // Set PWM2A on Zero
EPwm4Regs.AQCTLA.bit.CAU = AQ_CLEAR; // Clear PWM2A on event A, up count
EPwm4Regs.AQCTLB.bit.ZRO = AQ_SET; // Set PWM2B on Zero
EPwm4Regs.AQCTLB.bit.CBU = AQ_CLEAR; // Clear PWM2B on event B, up count
}
// ------------- Sine Analyzer Block to measure RMS, frequency and ZCD
SineAnalyzer_diff sine_mainsV = SineAnalyzer_diff_DEFAULTS;
SPLL_1ph spll1;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// MAIN CODE - starts here
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void main(void)
{
//=================================
// INITIALISATION - General
//=================================
DeviceInit(); // Device Life support & GPIO mux settings
// Only used if running from FLASH
// Note that the variable FLASH is defined by the compiler (-d FLASH)
#ifdef FLASH
// Copy time critical code and Flash setup code to RAM
// The RamfuncsLoadStart, RamfuncsLoadEnd, and RamfuncsRunStart
// symbols are created by the linker. Refer to the linker files.
MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);
// Call Flash Initialization to setup flash waitstates
// This function must reside in RAM
InitFlash(); // Call the flash wrapper init function
#endif //(FLASH)
//-------------------------------------------------------------
//****************************Configurazione interrupt timer0 **************************
// Step 3. Clear all interrupts and initialize PIE vector table:
// Disable CPU interrupts
DINT;
// Initialize the PIE control registers to their default state.
// The default state is all PIE interrupts disabled and flags
// are cleared.
// This function is found in the DSP2802x_PieCtrl.c file.
InitPieCtrl();
// Disable CPU interrupts and clear all CPU interrupt flags:
IER = 0x0000;
IFR = 0x0000;
// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
// This will populate the entire table, even if the interrupt
// is not used in this example. This is useful for debug purposes.
// The shell ISR routines are found in DSP2802x_DefaultIsr.c.
// This function is found in DSP2802x_PieVect.c.
InitPieVectTable();
// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file.
EALLOW; // This is needed to write to EALLOW protected registers
PieVectTable.TINT0 = &cpu_timer0_isr;
EDIS; // This is needed to disable write to EALLOW protected registers
// Step 4. Initialize the Device Peripheral. This function can be
// found in DSP2802x_CpuTimers.c
InitCpuTimers(); // For this example, only initialize the Cpu Timers
// Configure CPU-Timer 0 to interrupt every 500 milliseconds:
// 60MHz CPU Freq, 50 millisecond Period (in uSeconds)
ConfigCpuTimer(&CpuTimer0, 60, 50);
// To ensure precise timing, use write-only instructions to write to the entire register. Therefore, if any
// of the configuration bits are changed in ConfigCpuTimer and InitCpuTimers (in DSP2802x_CpuTimers.h), the
// below settings must also be updated.
CpuTimer0Regs.TCR.all = 0x4001; // Use write-only instruction to set TSS bit = 0
// Enable CPU INT1 which is connected to CPU-Timer 0:
IER |= M_INT1;
// Enable TINT0 in the PIE: Group 1 interrupt 7
PieCtrlRegs.PIEIER1.bit.INTx7 = 1;
// Enable global Interrupts and higher priority real-time debug events:
EINT; // Enable Global interrupt INTM
ERTM; // Enable Global realtime interrupt DBGM
//****************************EPWM INITIALISATION**************************
EPWM_init();
//****************************ADC INITIALISATION**************************
ADC_init();
//=================================
// Forever LOOP
//=================================
// Just sit and loop forever:
// No interrups needed in this example.
// PWM pins can be observed with a scope.
kappa=_IQ15(0.1);
sgen.offset=0;
sgen.gain=0x7fff; /* gain = 1 in Q15 */
sgen.freq=5369; /* freq = (Required Freq/Max Freq)*2^15 */
/* = (50/305.17)*2^15 = 5369 */
sgen.step_max=1000; /* Max Freq= (step_max * sampling freq)/65536 */
/* Max Freq = (1000*20k)/65536 = 305.17 */
sgen.phase=0x4000; /* Phase = (required Phase)/180 in Q15 */
/* = (+90/180) in Q15 = 4000h */
// for(;;)
SPLL_1ph_init(50,_IQ21(0.00005),&spll1);
//sine analyzer initialization
sine_mainsV.Vin=0;
sine_mainsV.SampleFreq=_IQ15(8571428.6);//(20000.0)
sine_mainsV.Threshold=_IQ15(0.5);
while(1)
{
EPwm2Regs.CMPA.half.CMPA = (x11/40)+819; // Add duty_cycle_A to watch window
EPwm2Regs.CMPB = (x12/40)+819;
// EPwm3Regs.CMPA.half.CMPA = (x13/40)+819;
EPwm3Regs.CMPA.half.CMPA = (InvSine/40)+819;
EPwm3Regs.CMPB = (x21/40)+819;
EPwm4Regs.CMPA.half.CMPA = (x22/40)+819;
EPwm4Regs.CMPB = Adc_Results/2;
if (sine_mainsV.ZCD==1){
if (x21==0){
sgen.phase++;
}
}
if (AdcRegs.ADCINTFLG.bit.ADCINT1 == 1){
pippo++;
}
// ------------------------------------------------------------------------------
// Connect inputs to the sine analyzer block , compute RMS, Freq, ZCD
// ------------------------------------------------------------------------------
sine_mainsV.Vin =(long)((long)AdcResult.ADCRESULT1<<3);//-_IQ15(0.5);
sine_mainsV.Vin = sine_mainsV.Vin <<1;
SineAnalyzer_diff_MACRO (sine_mainsV);
// VrmsReal = _IQ15mpy (KvInv, sine_mainsV.Vrms);
VrmsReal = _IQ15(sine_mainsV.Vrms);
Vfreq = _IQ15(sine_mainsV.SigFreq);
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Interrupt
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
interrupt void cpu_timer0_isr(void)
{
//GpioDataRegs.GPBTOGGLE.bit.GPIO34 = 1; // Toggle GPIO34 once per 500 milliseconds
GpioDataRegs.GPATOGGLE.bit.GPIO16=1;
CpuTimer0.InterruptCount++;
//SPLL_1ph_run(&spll1);
sgen.calc(&sgen);
x11=sgen.out11;
x12=sgen.out12;
x13=sgen.out13;
x21=sgen.out21;
x22=sgen.out22;
x23=sgen.out23;
SPLL_1ph_MACRO(spll1);
Vac_in=(long)((long)AdcResult.ADCRESULT1<<9);
spll1.AC_input=Vac_in>>1;
InvSine = (long)(spll1.sin[0])>>6; // InvSine is in Q15
InvSine2 = (long)(spll1.cos[0])>>6; // InvCos is in Q15
// InvSine3 = _IQ15(InvSine2 * _IQ15(0.1)) + InvSine;
InvSine3 = _IQ15mpy(InvSine2,kappa)+ InvSine;
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
Adc_Results = AdcResult.ADCRESULT1;
AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // Clear ADCINT1 flag
}
OK Manish, You're right!!!
The problem stay in the offset of my AC signal!!!!
Thank you very much.
I will write at the next problem!
Reguards