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.

Compiler/LAUNCHXL-F28379D: ADC Input to control EPWM Duty cycle

Part Number: LAUNCHXL-F28379D
Other Parts Discussed in Thread: C2000WARE

Tool/software: TI C/C++ Compiler

Good day,

Using the C2000Ware example , "epwm_updown_aq_cpu01.c" , is it possible to use a potentiometer as an ADC input in order to control the EPWM Duty cycle of this example ?

I believe that I need to make some change on line 430 below:

CMPA.bit.CMPA++ ---->>>>  AdcaResultRegs.ADCRESULT2

Is it correct ?

Best regards

  • Hi,

    Yes basically you need to update CMPA value based on the ADC result register but most won't probably it won't be a direct mapping. Because ADC output will be between 0-4096 (if its 12 bit), so you need to perform scaling i.e.

    CMPA.bit.CMPA = (AdcaResultRegs.ADCRESULT2/4096) * (TBPRD +1)

    If my reply answers your question please click on "This resolved my issue" button located at the bottom of my post.

  • Hi,

    CMPA output is not updating ( freeze at 3.3V dc). Please check my code


    the potentiometer is on ADCINA2.

    //###########################################################################
    //
    // FILE:   epwm_updown_aq_cpu01.c
    //
    // Included Files
    //
    #include "F28x_Project.h"

    //
    // Defines
    //
    #define EPWM1_TIMER_TBPRD  2000  // Period register
    #define EPWM1_MAX_CMPA     1950
    #define EPWM1_MIN_CMPA       50
    #define EPWM1_MAX_CMPB     1950
    #define EPWM1_MIN_CMPB       50

    #define EPWM_CMP_UP           1
    #define EPWM_CMP_DOWN         0

    //
    // Globals
    //
    typedef struct
    {
        volatile struct EPWM_REGS *EPwmRegHandle;
        Uint16 EPwm_CMPA_Direction;
        Uint16 EPwm_CMPB_Direction;
        Uint16 EPwmTimerIntCount;
        Uint16 EPwmMaxCMPA;
        Uint16 EPwmMinCMPA;
        Uint16 EPwmMaxCMPB;
        Uint16 EPwmMinCMPB;
    }EPWM_INFO;

    EPWM_INFO epwm1_info;


    //
    // Function Prototypes
    //
    void ConfigureADC(void);
    void SetupADCEpwm(Uint16 channel1, Uint16 channel2);
    void InitEPwm1Example(void);
    __interrupt void epwm1_isr(void);
    void update_compare(EPWM_INFO*);


    //
    // Main
    //
    void main(void)
    {
    //
    // Step 1. Initialize System Control:
    // PLL, WatchDog, enable Peripheral Clocks
    // This example function is found in the F2837xD_SysCtrl.c file.
    //
        InitSysCtrl();

    //
    // Step 2. Initialize GPIO:
    // This example function is found in the F2837xD_Gpio.c file and
    // illustrates how to set the GPIO to it's default state.
    //
    //    InitGpio();

    //
    // enable PWM1, PWM2 and PWM3
    //
        CpuSysRegs.PCLKCR2.bit.EPWM1=1;


    //
    // For this case just init GPIO pins for ePWM1, ePWM2, ePWM3
    // These functions are in the F2837xD_EPwm.c file
    //
        InitEPwm1Gpio();


    //
    // 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 F2837xD_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 F2837xD_DefaultIsr.c.
    // This function is found in F2837xD_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.EPWM1_INT = &epwm1_isr;
        EDIS;   // This is needed to disable write to EALLOW protected registers

    //
    // For this example, only initialize the ePWM
    //
        EALLOW;
        CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 0;
        EDIS;

        InitEPwm1Example();

        EALLOW;
        CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 1;
        EDIS;

    //
    // Step 4. User specific code, enable interrupts:
    //

    //
    // Enable CPU INT3 which is connected to EPWM1-3 INT:
    //
        IER |= M_INT3;

    //
    // Enable EPWM INTn in the PIE: Group 3 interrupt 1-3
    //
        PieCtrlRegs.PIEIER3.bit.INTx1 = 1;
        PieCtrlRegs.PIEIER3.bit.INTx2 = 1;
        PieCtrlRegs.PIEIER3.bit.INTx3 = 1;

    //
    // Enable global Interrupts and higher priority real-time debug events:
    //
        EINT;  // Enable Global interrupt INTM
        ERTM;  // Enable Global realtime interrupt DBGM

    //
    // Step 5. IDLE loop. Just sit and loop forever (optional):
    //
        for(;;)
        {
            asm ("    NOP");
        }
    }

    void ConfigureADC(void)
    {
        EALLOW;

        //
        //write configurations
        //
        AdcaRegs.ADCCTL2.bit.PRESCALE = 6; //set ADCCLK divider to /4
        AdcaSetMode(ADC_ADCA, ADC_RESOLUTION_12BIT, ADC_SIGNALMODE_SINGLE);

        //
        //Set pulse positions to late
        //
        AdcaRegs.ADCCTL1.bit.INTPULSEPOS = 1;

        //
        //power up the ADC
        //
        AdcaRegs.ADCCTL1.bit.ADCPWDNZ = 1;

        //
        //delay for 1ms to allow ADC time to power up
        //
        DELAY_US(1000);

        EDIS;
    }

    void SetupADCEpwm(Uint16 channel1, Uint16 channel2)
    {
        Uint16 acqps;

        //
        //determine minimum acquisition window (in SYSCLKS) based on resolution
        //
        if(ADC_RESOLUTION_12BIT == AdcaRegs.ADCCTL2.bit.RESOLUTION)
        {
            acqps = 14; //75ns
        }
        else //resolution is 16-bit
        {
            acqps = 63; //320ns
        }

        //
        //Select the channels to convert and setup the end of conversion flag
        //ADCA
        //
        EALLOW;
        AdcaRegs.ADCSOC2CTL.bit.CHSEL = 2;  //SOC2 will convert pin A2
        AdcaRegs.ADCSOC2CTL.bit.ACQPS = acqps; //sample window is 100 SYSCLK cycles
        AdcaRegs.ADCSOC2CTL.bit.TRIGSEL = 5; //trigger on ePWM1 SOCA/C
        AdcaRegs.ADCINTSEL1N2.bit.INT1SEL = 0; //end of SOC2 will set INT1 flag
        AdcaRegs.ADCINTSEL1N2.bit.INT1E = 1;   //enable INT1 flag
        AdcaRegs.ADCINTSEL1N2.bit.INT2E = 1;   //enable INT2 flag
        EDIS;
    }

    //
    // epwm1_isr - EPWM1 ISR
    //
    __interrupt void epwm1_isr(void)
    {
        //
        // Update the CMPA and CMPB values
        //
        update_compare(&epwm1_info);

        //
        // Clear INT flag for this timer
        //
        EPwm1Regs.ETCLR.bit.INT = 1;

        //
        // Acknowledge this interrupt to receive more interrupts from group 3
        //
        PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;
    }



    // InitEPwm1Example - Initialize EPWM1 configuration
    //
    void InitEPwm1Example(void)
    {
        //
        // Setup TBCLK
        //
        EPwm1Regs.TBPRD = EPWM1_TIMER_TBPRD;       // Set timer period 801 TBCLKs
        EPwm1Regs.TBPHS.bit.TBPHS = 0x0000;        // Phase is 0
        EPwm1Regs.TBCTR = 0x0000;                  // Clear counter

        //
        // Set Compare values
        //
        EPwm1Regs.CMPA.bit.CMPA = EPWM1_MIN_CMPA;    // Set compare A value
        EPwm1Regs.CMPB.bit.CMPB = EPWM1_MAX_CMPB;    // Set Compare B value

        //
        // Setup counter mode
        //
        EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN; // Count up and down
        EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;        // Disable phase loading
        EPwm1Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1;       // Clock ratio to SYSCLKOUT
        EPwm1Regs.TBCTL.bit.CLKDIV = TB_DIV1;

        //
        // Setup shadowing
        //
        EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
        EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
        EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO; // Load on Zero
        EPwm1Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;

        //
        // Set actions
        //
        EPwm1Regs.AQCTLA.bit.CAU = AQ_SET;            // Set PWM1A on event A, up
                                                      // count
        EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;          // Clear PWM1A on event A,
                                                      // down count

        EPwm1Regs.AQCTLB.bit.CBU = AQ_SET;            // Set PWM1B on event B, up
                                                      // count
        EPwm1Regs.AQCTLB.bit.CBD = AQ_CLEAR;          // Clear PWM1B on event B,
                                                      // down count

        //
        // Interrupt where we will change the Compare Values
        //
        EPwm1Regs.ETSEL.bit.INTSEL = ET_CTR_ZERO;     // Select INT on Zero event
        EPwm1Regs.ETSEL.bit.INTEN = 1;                // Enable INT
        EPwm1Regs.ETPS.bit.INTPRD = ET_3RD;           // Generate INT on 3rd event

        //
        // Information this example uses to keep track
        // of the direction the CMPA/CMPB values are
        // moving, the min and max allowed values and
        // a pointer to the correct ePWM registers
        //
        epwm1_info.EPwm_CMPA_Direction = EPWM_CMP_UP;   // Start by increasing CMPA
        epwm1_info.EPwm_CMPB_Direction = EPWM_CMP_DOWN; // & decreasing CMPB
        epwm1_info.EPwmTimerIntCount = 0;               // Zero the interrupt counter
        epwm1_info.EPwmRegHandle = &EPwm1Regs;          // Set the pointer to the
                                                        // ePWM module
        epwm1_info.EPwmMaxCMPA = EPWM1_MAX_CMPA;        // Setup min/max CMPA/CMPB
                                                        // values
        epwm1_info.EPwmMinCMPA = EPWM1_MIN_CMPA;
        epwm1_info.EPwmMaxCMPB = EPWM1_MAX_CMPB;
        epwm1_info.EPwmMinCMPB = EPWM1_MIN_CMPB;
    }


    // update_compare - Update the PWM compare values
    //
    void update_compare(EPWM_INFO *epwm_info)
    {
        //
        // Every 10'th interrupt, change the CMPA/CMPB values
        //
        if(epwm_info->EPwmTimerIntCount == 10)
        {
            epwm_info->EPwmTimerIntCount = 0;

            //
            // If we were increasing CMPA, check to see if
            // we reached the max value.  If not, increase CMPA
            // else, change directions and decrease CMPA
            //
            if(epwm_info->EPwm_CMPA_Direction == EPWM_CMP_UP)
            {
                if(epwm_info->EPwmRegHandle->CMPA.bit.CMPA <
                   epwm_info->EPwmMaxCMPA)
                {
                    epwm_info->EPwmRegHandle->CMPA.bit.CMPA = (AdcaResultRegs.ADCRESULT2/4096) * (EPWM1_TIMER_TBPRD + 1);
                }
                else
                {
                    epwm_info->EPwm_CMPA_Direction = EPWM_CMP_DOWN;
                    epwm_info->EPwmRegHandle->CMPA.bit.CMPA--;
                }
            }

            //
            // If we were decreasing CMPA, check to see if
            // we reached the min value.  If not, decrease CMPA
            // else, change directions and increase CMPA
            //
            else
            {
                if(epwm_info->EPwmRegHandle->CMPA.bit.CMPA ==
                   epwm_info->EPwmMinCMPA)
                {
                    epwm_info->EPwm_CMPA_Direction = EPWM_CMP_UP;
                    epwm_info->EPwmRegHandle->CMPA.bit.CMPA++;
                }
                else
                {
                    epwm_info->EPwmRegHandle->CMPA.bit.CMPA--;
                }
            }

            //
            // If we were increasing CMPB, check to see if
            // we reached the max value.  If not, increase CMPB
            // else, change directions and decrease CMPB
            //
            if(epwm_info->EPwm_CMPB_Direction == EPWM_CMP_UP)
            {
                if(epwm_info->EPwmRegHandle->CMPB.bit.CMPB < epwm_info->EPwmMaxCMPB)
                {
                    epwm_info->EPwmRegHandle->CMPB.bit.CMPB++;
                }
                else
                {
                    epwm_info->EPwm_CMPB_Direction = EPWM_CMP_DOWN;
                    epwm_info->EPwmRegHandle->CMPB.bit.CMPB--;
                }
            }

            //
            // If we were decreasing CMPB, check to see if
            // we reached the min value.  If not, decrease CMPB
            // else, change directions and increase CMPB
            //
            else
            {
                if(epwm_info->EPwmRegHandle->CMPB.bit.CMPB == epwm_info->EPwmMinCMPB)
                {
                    epwm_info->EPwm_CMPB_Direction = EPWM_CMP_UP;
                    epwm_info->EPwmRegHandle->CMPB.bit.CMPB++;
                }
                else
                {
                    epwm_info->EPwmRegHandle->CMPB.bit.CMPB--;
                }
            }
        }
        else
        {
            epwm_info->EPwmTimerIntCount++;
        }

        return;
    }

    //
    // End of file
    //

  • Hi,

    I looked at your code, first of all I feel the better implementation would be to update the CMPA values inside the ADC EOC ISR instead of updating at every 10th EPWM ISR. So basically you can disable EPWM1 ISR and instead define an ISR which would be triggered at End of Conversion (EOC) and there you update the CMPA values.

    Secondly, I can see you are checking whther the counter is moving UP/DOWN and based on that you are updating CMPA value but you don't need all those comparisons  now. That's why I would recommend you to clean the code and get rid of EPWM ISR completely and use ADC ISR.

    If my reply answers your question please click on "This resolved my issue" button located at the bottom of my post.