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.

TMS320F28335: CMPA value not changing according to ADC input.Close loop PI control

Part Number: TMS320F28335
Other Parts Discussed in Thread: C2000WARE, SYSCONFIG

Hi,

I am using PI control logic,through function DCL.h.

After debugging the code,the Duty cycle of ePWM is not changing as per the given ADC to ADCRESULT0.

Pl find below code for reference.

//###########################################################################
//
// FILE:   Example_2833xEPwmDeadBand.c
//
// TITLE:  ePWM Deadband Generation Example
//
//! \addtogroup f2833x_example_list
//! <h1> ePWM Deadband Generation (epwm_deadband)</h1>
//!
//! This example configures ePWM1, ePWM2 and ePWM3 for:
//!   - Count up/down
//!   - Deadband
//! 3 Examples are included:
//!   - ePWM1: Active low PWMs
//!   - ePWM2: Active low complementary PWMs
//!   - ePWM3: Active high complementary PWMs
//!
//! Each ePWM is configured to interrupt on the 3rd zero event
//! when this happens the deadband is modified such that
//! 0 <= DB <= DB_MAX.  That is, the deadband will move up and
//! down between 0 and the maximum value.
//!
//! \b External \b Connections \n
//!  - EPWM1A is on GPIO0
//!  - EPWM1B is on GPIO1
//!  - EPWM2A is on GPIO2
//!  - EPWM2B is on GPIO3
//!  - EPWM3A is on GPIO4
//!  - EPWM3B is on GPIO5
//
//
//###########################################################################
// $TI Release: $
// $Release Date: $
// $Copyright:
// Copyright (C) 2009-2023 Texas Instruments Incorporated - http://www.ti.com/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//   Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
//
//   Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the
//   documentation and/or other materials provided with the
//   distribution.
//
//   Neither the name of Texas Instruments Incorporated nor the names of
//   its contributors may be used to endorse or promote products derived
//   from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//###########################################################################

//
// Included Files
//
#include "DSP28x_Project.h"


// Device Headerfile and Examples Include File
#include "DCL.h"
#include "DSP2833x_Device.h"
#include "DSP2833x_Examples.h"
#include "DSP2833x_GlobalPrototypes.h"

#include "DCLF32.h"

#include "DCL_fdlog.h"



//#include "DCLC28.h"

#include "math.h"
//
// Function Prototypes
//
float rk = 20.0f;

float yk;

float lk;

float uk;

float Duty;

DCL_PI pi1 = PI_DEFAULTS;



//

// Function Prototypes

//

Uint16 LoopCount;

Uint16 ConversionCount;

void adc_isr(void);

void gpio_select(void);


void InitAdc(void);
void InitEPwm1(void);

void control_Isr(void);





float voltage1;

float cmpa;





//

// Main

//

void main(void)

{

    InitSysCtrl();

    DINT;

    InitPieCtrl();

    EALLOW;

        #if (CPU_FRQ_150MHZ)     // Default - 150 MHz SYSCLKOUT

            //

            // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3)   = 25.0 MHz

            //

            #define ADC_MODCLK 0x3

        #endif

        #if (CPU_FRQ_100MHZ)

            //

            // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2)   = 25.0 MHz

            //

            #define ADC_MODCLK 0x2

        #endif

    EDIS;

    EALLOW;

        SysCtrlRegs.HISPCP.all = ADC_MODCLK;

    EDIS;

    IER = 0x0000;

    IFR = 0x0000;

    InitPieVectTable();



    EALLOW;         // This is needed to write to EALLOW protected registers

        PieVectTable.ADCINT = &adc_isr;

    EDIS;    // This is needed to disable write to EALLOW protected registers





    gpio_select();

    InitEPwm1();
    InitAdc();



    PieCtrlRegs.PIEIER1.bit.INTx6 = 1;

    IER |= M_INT1;







       /* initialise controller variables */

       pi1.Kp=2.0f;

       pi1.Ki=1.0f;

       pi1.Umax=0.8f;

       pi1.Umin=0.2f;



       rk = 20.0f;                             // initial value for control reference

       lk = 1.0f;                              // control loop not saturated



       //

       // Enable SOCA from ePWM to start SEQ1

       //
       // control loop not saturated
       AdcRegs.ADCMAXCONV.all = 0x0001;       // Setup 2 conv's on SEQ1

       AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x3; // Setup ADCINA3 as 1st SEQ1 conv.

       AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x2; // Setup ADCINA2 as 2nd SEQ1 conv.
       AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1;


                AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1;  // Enable SEQ1 interrupt (every EOS)


    EINT;           // Enable Global interrupt INTM

    ERTM;           // Enable Global realtime interrupt DBGM

    for (;;) ;

}



//

// cpu_timer0_isr -


void gpio_select()


{


        EALLOW;

        GpioCtrlRegs.GPAPUD.bit.GPIO0 = 0;   // Enable pullup on GPIO0

        GpioCtrlRegs.GPAPUD.bit.GPIO1 = 0;   // Enable pullup on GPIO0

        GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1;  // GPIO0 = PWM1A

        GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1;  // GPIO0 = PWM1A

        EDIS;

}

void InitEPwm1()

{

        EPwm1Regs.TBCTL.bit.CTRMODE = 0; // Count up down

        EPwm1Regs.TBPRD = 37500;       // Set timer period

        EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;    // Disable phase loading

        EPwm1Regs.TBPHS.half.TBPHS = 0x0000;       // Phase is 0

        EPwm1Regs.TBCTR = 0x0000;                  // Clear counter

        EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0x001;   // Clock ratio to SYSCLKOUT

        EPwm1Regs.TBCTL.bit.CLKDIV = 000;

        EPwm1Regs.TBCTL.bit.SYNCOSEL=1;

        EPwm1Regs.CMPA.half.CMPA =18750;    // Set compare A value

        EPwm1Regs.CMPB = 18750;              // Set Compare B value

        EPwm1Regs.CMPCTL.bit.LOADAMODE=1;

        EPwm1Regs.CMPCTL.bit.SHDWAMODE=0;

/*        EPwm1Regs.DBCTL.bit.OUT_MODE=3;

        EPwm1Regs.DBCTL.bit.POLSEL=2;

        EPwm1Regs.DBCTL.bit.IN_MODE=0;

        EPwm1Regs.DBRED=100;

        EPwm1Regs.DBFED=100;*/

        EPwm1Regs.AQCTLA.bit.ZRO=2;    // Set PWM1A on Zero

        EPwm1Regs.AQCTLA.bit.CAU=1;



            EPwm1Regs.ETSEL.bit.SOCAEN = 1;     // Enable SOC on A group

            EPwm1Regs.ETSEL.bit.SOCASEL = 1;    // Select SOC from from CPMA on upcount

            EPwm1Regs.ETPS.bit.SOCAPRD = 1;     // Generate pulse on 1st event



}

__interrupt void adc_isr(void)

{

    yk=((int)AdcRegs.ADCRESULT0 >>4)*300.0f/4095.0f;



    //Voltage2[ConversionCount] = AdcRegs.ADCRESULT1 >>4;

    //DCL_runClamp_C1(float *data, float Umax, float Umin);

    //





    lk=0.0f;

    uk = DCL_runPI_C2(&pi1, rk, yk);

    if (uk>0.8f){

        uk=0.8f;

    }

    else if (uk<0.1f){

        uk=0.1f;

    }

    //uk=0.5f;



    EPwm1Regs.CMPA.half.CMPA =(EPwm1Regs.TBPRD)*uk;    // Set compare A value

    EPwm1Regs.CMPB = EPwm1Regs.TBPRD*uk;              // Set Compare B value

    // If 40 conversions have been logged, start over

    //

    //

    // Reinitialize for next ADC sequence

    //
 if(ConversionCount == 9)
    {
        ConversionCount = 0;
    }
    else
    {
        ConversionCount++;
    }

    AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;         // Reset SEQ1

    AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;       // Clear INT SEQ1 bit

    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;   // Acknowledge interrupt to PIE



    return;

}

Kindly resolve the issue.

  • Hello,

    Did you use any reference C2000Ware example? Can you please try stepping through each line of your code to see if configurations/variable values change as expected? And you can check that your ISRs are occurring properly. This will help to narrow down the source of when things diverge from what you are expecting to occur.

    Best Regards,

    Allison

  • Hi,

    I will be able to check whether the values of registers by next week and will let you know ,till then kindly confirm does the verion of CCS also impact the result? i am using ccs7.4

    Thanks

    Ayush

  • Hi Ayush,

    Yes, please let me know any findings. And the CCS version should not have any affect on the result - so no need to worry about that (though, is there a reason you haven't tried a newer CCS version?).

    Though this does not directly apply to your case, I also wanted to add that for newer generation devices (with DriverLib and SysConfig implementation), we have a walk-through example on the C2000 Academy that uses ADC conversion results to influence the output of a PWM. The instructions can be found HERE, and the lab exercise example projects can be found at {C2000Ware}\training\device\{device}\system_design\lab_systemdesign. Just wanted to be sure you know of it in case you use newer generation devices in the future or are simply curious!

    Best Regards,

    Allison

  • Hi , Allison

    Sorry for late reply,i didnt had access to Laptop hence the dalay.

    I have monitored the real time value of ADCRESULT0, ADCINT ,they are not changing even after varying the input values to ADC.Hence no change in duty

    cycle of output.

    PFA screen shot . and tell next course of action.

    Regards

    Ayush

  • Hi Ayush,

    Could you put a while loop in your code and software trigger the ADC by writing AdcRegs.ADCTRL2.bit.SOC_SEQ1 to 1?

    This way we can make sure that your ADC SEQ1 and interrupt is set up correctly. Have you used any of our ADC examples for F2833X devices?  

    Best Regards,

    Ben Collier

  • Hi Ben

    I have added the while loop in code.Kindly verify from your end

    Thanks and Regards

    Ayush

     .

    #include "DSP28x_Project.h"     // Device Headerfile and Examples Include File
    #include "DCL.h"
    #include "DCLF32.h"
    #include "DCL_fdlog.h"
    #include "DCLC28.h"
    
    // Function Prototypes
    __interrupt void adc_isr(void);
    
    DCL_PI pi1 = PI_DEFAULTS;
    
    // Globals
    Uint16 LoopCount;
    Uint16 ConversionCount;
    float Voltage1;
    float U = 0.5f;
    float rk = 0.6f;
    float yk;
    float uk = 0.5f;
    
    void InitEPwm1(void);
    void gpio_select(void);
    
    //
    // Main
    //
    void main(void)
    {
        // Step 1. Initialize System Control: PLL, WatchDog, enable Peripheral Clocks
        InitSysCtrl();
    
        EALLOW;
        #if (CPU_FRQ_150MHZ)     // Default - 150 MHz SYSCLKOUT
            #define ADC_MODCLK 0x3
        #endif
        #if (CPU_FRQ_100MHZ)
            #define ADC_MODCLK 0x2
        #endif
        EDIS;
    
        // Define ADCCLK clock frequency ( less than or equal to 25 MHz )
        EALLOW;
        SysCtrlRegs.HISPCP.all = ADC_MODCLK;
        EDIS;
    
        // Step 2. Initialize GPIO:
        // InitGpio();  // Skipped for this example
    
        // Step 3. Clear all interrupts and initialize PIE vector table:
        DINT;
    
        InitPieCtrl();
    
        IER = 0x0000;
        IFR = 0x0000;
    
        InitPieVectTable();
    
        EALLOW;  // This is needed to write to EALLOW protected register
        PieVectTable.ADCINT = &adc_isr;
        EDIS;    // This is needed to disable write to EALLOW protected registers
    
        // Step 4. Initialize all the Device Peripherals:
        InitAdc();  // For this example, init the ADC
    
        // Step 5. User specific code, enable interrupts:
        PieCtrlRegs.PIEIER1.bit.INTx6 = 1;
        IER |= M_INT1;      // Enable CPU Interrupt 1
        EINT;               // Enable Global interrupt INTM
        ERTM;               // Enable Global realtime interrupt DBGM
    
        LoopCount = 0;
        ConversionCount = 0;
    
        // Configure ADC
        AdcRegs.ADCMAXCONV.all = 0x0001;       // Setup 2 conv's on SEQ1
        AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x3; // Setup ADCINA3 as 1st SEQ1 conv.
        AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x2; // Setup ADCINA2 as 2nd SEQ1 conv.
    
        AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1;  // Enable SEQ1 interrupt (every EOS)
    
        // Configure ePWM1
        InitEPwm1();
    
        // Configure GPIO
        gpio_select();
    
        // Main loop
        while (1)
        {
            // Start ADC conversion sequence 1
            AdcRegs.ADCTRL2.bit.SOC_SEQ1 = 1;
    
            // Wait for ADC conversion to complete
            while (AdcRegs.ADCST.bit.INT_SEQ1 == 0);
    
            // Read ADC result from result register (e.g., from the first channel)
            yk = ((int)AdcRegs.ADCRESULT0 >> 4) * 300.0f / 4095.0f;
    
            // Process the ADC result
            uk = DCL_runPI_C2(&pi1, rk, yk);
            if (uk > 0.8f) {
                uk = 0.8f;
            } else if (uk < 0.1f) {
                uk = 0.1f;
            }
    
            EPwm1Regs.CMPA.half.CMPA = (EPwm1Regs.TBPRD) * uk;  // Set compare A value
            EPwm1Regs.CMPB = EPwm1Regs.TBPRD * uk;              // Set Compare B value
    
            // Clear the interrupt flag and reset sequencer
            AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;
            AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;
    
            LoopCount++;
        }
    }
    
    void InitEPwm1(void)
    {
        EPwm1Regs.TBCTL.bit.CTRMODE = 0; // Count up down
        EPwm1Regs.TBPRD = 37500;       // Set timer period
        EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;    // Disable phase loading
        EPwm1Regs.TBPHS.half.TBPHS = 0x0000;       // Phase is 0
        EPwm1Regs.TBCTR = 0x0000;                  // Clear counter
        EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0x001;   // Clock ratio to SYSCLKOUT
        EPwm1Regs.TBCTL.bit.CLKDIV = 000;
        EPwm1Regs.TBCTL.bit.SYNCOSEL = 1;
        EPwm1Regs.CMPA.half.CMPA = 10000;    // Set compare A value
        EPwm1Regs.CMPB = 10000;              // Set Compare B value
        EPwm1Regs.DBCTL.bit.OUT_MODE = 3;
        EPwm1Regs.DBCTL.bit.POLSEL = 2;
        EPwm1Regs.DBCTL.bit.IN_MODE = 0;
        EPwm1Regs.DBRED = 100;
        EPwm1Regs.DBFED = 100;
        EPwm1Regs.ETSEL.bit.SOCAEN = 1;     // Enable SOC on A group
        EPwm1Regs.ETSEL.bit.SOCASEL = 4;    // Select SOC from CPMA on upcount
        EPwm1Regs.ETPS.bit.SOCAPRD = 1;     // Generate pulse on 1st event
    
        EPwm1Regs.AQCTLA.bit.ZRO = 2;    // Set PWM1A on Zero
        EPwm1Regs.AQCTLA.bit.CAU = 1;
    }
    
    void gpio_select(void)
    {
        EALLOW;
        GpioCtrlRegs.GPAPUD.bit.GPIO0 = 0;   // Enable pullup on GPIO0
        GpioCtrlRegs.GPAPUD.bit.GPIO1 = 0;   // Enable pullup on GPIO1
        GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1;  // GPIO0 = PWM1A
        GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1;  // GPIO1 = PWM1B
        EDIS;
    }
    
    __interrupt void adc_isr(void)
    {
        yk = ((int)AdcRegs.ADCRESULT0 >> 4) * 300.0f / 4095.0f;
    
        uk = DCL_runPI_C2(&pi1, rk, yk);
        if (uk > 0.8f) {
            uk = 0.8f;
        } else if (uk < 0.1f) {
            uk = 0.1f;
        }
    
        EPwm1Regs.CMPA.half.CMPA = (EPwm1Regs.TBPRD) * uk;  // Set compare A value
        EPwm1Regs.CMPB = EPwm1Regs.TBPRD * uk;              // Set Compare B value
    
        if (ConversionCount == 9) {
            ConversionCount = 0;
        } else {
            ConversionCount++;
        }
    
        AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;         // Reset SEQ1
        AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;       // Clear INT SEQ1 bit
        PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;   // Acknowledge interrupt to PIE
    
        return;
    }
    

  • Hi Ayush,

    In your current while(1) loop, the SOC will only be triggered once if the interrupt is not set up correctly. Could you change your while(1) loop so that it only contains the software SOC trigger?

    Also, have you used any of our ADC examples for F2833X devices?

    Best Regards,

    Ben Collier

  • In this code i have changed rk from constant to 100 to 0 slope function by adding while loop.

    kindly verify if coding is correct or not.

    Thanks


    while (1)
    {
    // Update the time counter
    timeCounter++;

    // Determine the reference signal value
    if (timeCounter < constantDuration)
    {
    // Constant phase
    rk = 100.0f;
    }
    else
    {
    // Ramp down phase
    rk = 100.0f + rampRate * (timeCounter - constantDuration);
    if (rk < 0.0f)
    {
    rk = 0.0f; // Clamp to 0 to prevent going below 0
    }
    }

    // Start ADC conversion sequence 1
    AdcRegs.ADCTRL2.bit.SOC_SEQ1 = 1;

    // Wait for ADC conversion to complete
    while (AdcRegs.ADCST.bit.INT_SEQ1 == 0);

    // Read ADC result from result register (e.g., from the first channel)
    yk = ((int)AdcRegs.ADCRESULT0 >> 4) * 300.0f / 4095.0f;

    // Process the ADC result
    uk = DCL_runPI_C2(&pi1, rk, yk);
    if (uk > 0.8f) {
    uk = 0.8f;
    } else if (uk < 0.1f) {
    uk = 0.1f;
    }

    // Set PWM duty cycle based on the control output
    EPwm1Regs.CMPA.half.CMPA = (EPwm1Regs.TBPRD) * uk;
    EPwm1Regs.CMPB = EPwm1Regs.TBPRD * uk;
    }

  • Ayush,

    I cannot comment on the functionality of your control code. I just want to make sure that your ADC is triggering as expected.

    Could you include only the following line in your while(1) loop? 

    AdcRegs.ADCTRL2.bit.SOC_SEQ1 = 1;

    Then could you check the ADC result register to make sure that you see the expected result? 

    Thanks,

    Ben Collier