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.

distorsion in SPLL_1ph

Other Parts Discussed in Thread: CONTROLSUITE

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.

The picture shows this.      Thanks

  • 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

  • can you post your isr code,

    with pwm dac connection etc?

  • 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