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.

TMS320F28379D: HRPWM with a down counter

Part Number: TMS320F28379D

How can I run a HRPWM for a down counter which is needed for better resolution at low duty cycle? I have modified the example code "hrpwm_duty_sfo_v8" as follows but both PWMxA and PWMxB signals are low when observed with an Oscilloscope. 

Key changes are: 

(*ePWM[j]).TBCTL.bit.CTRMODE = TB_COUNT_DOWN;

(*ePWM[j]).HRCNFG.bit.EDGMODE = HR_REP; 

(*ePWM[j]).HRCNFG.bit.EDGMODEB = HR_REP;

(*ePWM[j]).AQCTLA.bit.ZRO = AQ_CLEAR; // PWM toggle high/low
(*ePWM[j]).AQCTLA.bit.CAU = AQ_SET;
(*ePWM[j]).AQCTLB.bit.ZRO = AQ_CLEAR;
(*ePWM[j]).AQCTLB.bit.CBU = AQ_SET;

void HRPWM_Config(period)
{
    Uint16 j;

    //
    // ePWM channel register configuration with HRPWM
    // ePWMxA / ePWMxB toggle low/high with MEP control on Rising edge
    //
    for (j=1;j<PWM_CH;j++)
    {
        (*ePWM[j]).TBCTL.bit.PRDLD = TB_SHADOW;  // set Immediate load
        (*ePWM[j]).TBPRD = period-1;             // PWM frequency = 1 / period
        (*ePWM[j]).CMPA.bit.CMPA = period / 2;   // set duty 50% initially
        (*ePWM[j]).CMPA.bit.CMPAHR = (1 << 8);   // initialize HRPWM extension
        (*ePWM[j]).CMPB.bit.CMPB = period / 2;   // set duty 50% initially
        (*ePWM[j]).CMPB.all |= (1 << 8);         // initialize HRPWM extension
        (*ePWM[j]).TBPHS.all = 0;
        (*ePWM[j]).TBCTR = 0;

        (*ePWM[j]).TBCTL.bit.CTRMODE = TB_COUNT_DOWN;
        (*ePWM[j]).TBCTL.bit.PHSEN = TB_DISABLE;
        (*ePWM[j]).TBCTL.bit.SYNCOSEL = TB_SYNC_DISABLE;
        (*ePWM[j]).TBCTL.bit.HSPCLKDIV = TB_DIV1;
        (*ePWM[j]).TBCTL.bit.CLKDIV = TB_DIV1;
        (*ePWM[j]).TBCTL.bit.FREE_SOFT = 11;

        (*ePWM[j]).CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
        (*ePWM[j]).CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
        (*ePWM[j]).CMPCTL.bit.SHDWAMODE = CC_SHADOW;
        (*ePWM[j]).CMPCTL.bit.SHDWBMODE = CC_SHADOW;


        (*ePWM[j]).AQCTLA.bit.ZRO = AQ_CLEAR;      // PWM toggle high/low
        (*ePWM[j]).AQCTLA.bit.CAU = AQ_SET;
        (*ePWM[j]).AQCTLB.bit.ZRO = AQ_CLEAR;
        (*ePWM[j]).AQCTLB.bit.CBU = AQ_SET;

        EALLOW;
        (*ePWM[j]).HRCNFG.all = 0x0;
        (*ePWM[j]).HRCNFG.bit.EDGMODE = HR_REP;  // MEP control on falling edge
        (*ePWM[j]).HRCNFG.bit.CTLMODE = HR_CMP;
        (*ePWM[j]).HRCNFG.bit.HRLOAD  = HR_CTR_ZERO;
        (*ePWM[j]).HRCNFG.bit.EDGMODEB = HR_REP; // MEP control on falling edge
        (*ePWM[j]).HRCNFG.bit.CTLMODEB = HR_CMP;
        (*ePWM[j]).HRCNFG.bit.HRLOADB  = HR_CTR_ZERO;
        #if (AUTOCONVERT)
        (*ePWM[j]).HRCNFG.bit.AUTOCONV = 1;      // Enable auto-conversion
                                                 // logic
        #endif
        (*ePWM[j]).HRPCTL.bit.HRPE = 0; // Turn off high-resolution period
                                        // control.
        EDIS;
    }
}

And the remaining code for duty cycle is as follows: 

            DutyFine = 0x3000;
            if(UpdateFine)
            {

                for(i=1; i<PWM_CH; i++)
                {
                    CMPA_reg_val = ((long)DutyFine * (*ePWM[i]).TBPRD)>>15;
                    CMPB_reg_val = ((long)DutyFine * (*ePWM[i]).TBPRD)>>15;
                    temp = ((long)DutyFine * (*ePWM[i]).TBPRD) ;
                    temp1 = ((long)DutyFine * (*ePWM[i]).TBPRD) ;
                    temp = temp - ((long)CMPA_reg_val<<15);
                    temp1 = temp1 - ((long)CMPB_reg_val<<15);

                   #if (AUTOCONVERT)
                    CMPAHR_reg_val = temp<<1; // convert to Q16
                    CMPBHR_reg_val = temp<<1; // convert to Q16
                   #else
                    CMPAHR_reg_val = ((temp*MEP_ScaleFactor)+(0x0080<<7))>>15;
                    CMPAHR_reg_val = CMPAHR_reg_val << 8;
                    CMPBHR_reg_val = ((temp1*MEP_ScaleFactor)+(0x0080<<7))>>15;
                    CMPBHR_reg_val = CMPBHR_reg_val << 8;
                   #endif

                   //
                   // Example for a 32 bit write to CMPA:CMPAHR
                   //
                    (*ePWM[i]).CMPA.all = ((long)CMPA_reg_val)<<16 |
                                          CMPAHR_reg_val; // loses lower 8-bits
                   //
                   // Example for a 32 bit write to CMPB:CMPBHR
                   //
                    (*ePWM[i]).CMPB.all = ((long)CMPB_reg_val)<<16 |
                                          CMPBHR_reg_val; // loses lower 8-bits
                }
            }
            else
            {
                //
                // CMPA_reg_val is calculated as a Q0.
                // Since DutyFine is a Q15 number, and the period is Q0
                // the product is Q15. So to store as a Q0, we shift right
                // 15 bits.
                //
                for(i=1; i<PWM_CH; i++)
                {
                    (*ePWM[i]).CMPA.bit.CMPA = ((long)DutyFine *
                                                (*ePWM[i]).TBPRD>>15);
                    (*ePWM[i]).CMPB.bit.CMPB = ((long)DutyFine *
                                                (*ePWM[i]).TBPRD>>15);
                }
            }

            //
            // Call the scale factor optimizer lib function SFO()
            // periodically to track for any change due to temp/voltage.
            // This function generates MEP_ScaleFactor by running the
            // MEP calibration module in the HRPWM logic. This scale
            // factor can be used for all HRPWM channels. The SFO()
            // function also updates the HRMSTEP register with the
            // scale factor value.
            //
            status = SFO(); // in background, MEP calibration module
                            // continuously updates MEP_ScaleFactor

            if (status == SFO_ERROR)
            {
                error();   // SFO function returns 2 if an error occurs & #
                           // of MEP steps/coarse step
            }              // exceeds maximum of 255.

  • Hi,

    (*ePWM[j]).AQCTLA.bit.CAU = AQ_SET;

    What was the logic behind keeping the CAU bit at SET. I believe this should be replaced with CAD instead of CAU. Checking when counter equals to CMPA in down count mode and counter equals to zero will be the same instant. Please check by replacing CAU with CAD.

    Thanks,
    Aditya

  • Hello Aditya, This certainly helped to at least get some switching waveform but results are not meeting the expectations yet. I am working with ePWM3 and the code for configuration is as follows:

    void HRPWM_Config(period)
    {
    
        //
        // ePWM channel register configuration with HRPWM
        // ePWMxA / ePWMxB toggle low/high with MEP control on Rising edge
        //
    
            EPwm3Regs.TBCTL.bit.PRDLD = TB_SHADOW;  // set Shadow load
            EPwm3Regs.TBPRD = period-1;             // PWM frequency = 1 / period
            EPwm3Regs.CMPA.bit.CMPA = 0;   // set duty 0% initially
            EPwm3Regs.CMPA.bit.CMPAHR = (0 << 8);   // initialize HRPWM extension
            EPwm3Regs.CMPB.bit.CMPB = 0;   // set duty 0% initially
            EPwm3Regs.CMPB.all |= (0 << 8);         // initialize HRPWM extension
            EPwm3Regs.TBPHS.all = 0;
            EPwm3Regs.TBCTR = 0;
    
            EPwm3Regs.TBCTL.bit.CTRMODE = TB_COUNT_DOWN;
            EPwm3Regs.TBCTL.bit.PHSEN = TB_DISABLE;
            EPwm3Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_DISABLE;
            EPwm3Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1;
            EPwm3Regs.TBCTL.bit.CLKDIV = TB_DIV1;
            EPwm3Regs.TBCTL.bit.FREE_SOFT = 0x11;
    
            EPwm3Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;  // LOAD CMPA on CTR = 0
            EPwm3Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
            EPwm3Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
            EPwm3Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
    
    
            EPwm3Regs.AQCTLA.bit.ZRO = AQ_CLEAR;      // PWM toggle high/low
            EPwm3Regs.AQCTLA.bit.CAD = AQ_SET;
            EPwm3Regs.AQCTLB.bit.ZRO = AQ_CLEAR;
            EPwm3Regs.AQCTLB.bit.CBD = AQ_SET;
    
    
        EALLOW;
    
          EPwm3Regs.HRCNFG.all = 0x0;
          EPwm3Regs.HRCNFG.bit.EDGMODE = HR_REP;  // MEP control on rising edge
          EPwm3Regs.HRCNFG.bit.CTLMODE = HR_CMP; // CMPAHR controls the MEP
          EPwm3Regs.HRCNFG.bit.HRLOAD  = HR_CTR_ZERO;  // Shadow load on CTR=Zero
          EPwm3Regs.HRCNFG.bit.EDGMODEB = HR_REP;  // MEP control on rising edge
          EPwm3Regs.HRCNFG.bit.CTLMODEB = HR_CMP;
          EPwm3Regs.HRCNFG.bit.HRLOADB  = HR_CTR_ZERO;
          EPwm3Regs.HRCNFG.bit.SELOUTB  = HR_INVERT_B; // 1: ePWMxB output is inverted version of ePWMxA signal.
          #if(AUTOCONVERT)
          EPwm3Regs.HRCNFG.bit.AUTOCONV = 1;      // Enable auto-conversion
                                                   // logic
          #endif
          EPwm3Regs.HRPCTL.bit.HRPE = 0; // Turn off high-resolution period
                                          // control.
    
       EDIS;
    
    
            // Interrupt where we will change the Compare Values
                //
                EPwm3Regs.ETSEL.bit.INTSEL = ET_CTR_ZERO;     // Select INT on Zero event
                EPwm3Regs.ETSEL.bit.INTEN = 1;                // Enable INT
                EPwm3Regs.ETPS.bit.INTPRD = ET_3RD;           // Generate INT on 3rd event
    
    }
      

    The main problem is that duty cycle is not matching its set value that is controlled by the variable "input" in the following code. Here "DutyFineA" and "input" are of types Uint16 and float. The duty cycle of the switching waveforms remains same even when I changing the value of "input" that is varied in the range of 0.0f to 0.7f. I will appreciate all your help to find an issue with this code. 

            DutyFineA = _IQ15(input);
                        /* all below calculation apply for CMPB as well
                        // CMPA_reg_val , CMPA_reg_val is calculated as a Q0.
                        // Since DutyFine is a Q15 number, and the period is Q0
                        // the product is Q15. So to store as a Q0, we shift right
                        // 15 bits.
    
                        CMPA_reg_val = ((long)DutyFine * EPwm1Regs.TBPRD)>>15;
    
                        // This next step is to obtain the remainder which was
                        // truncated during our 15 bit shift above.
                        // compute the whole value, and then subtract CMPA_reg_val
                        // shifted LEFT 15 bits:
                        temp = ((long)DutyFine * EPwm1Regs.TBPRD) ;
                        temp = temp - ((long)CMPA_reg_val<<15);
    
                        ** If auto-conversion is disabled, the following step can be
                        // skipped. If autoconversion is enabled, the SFO function will
                        // write the MEP_ScaleFactor to the HRMSTEP register and the
                        // hardware will automatically scale the remainder in the CMPAHR
                        // register by the MEP_ScaleFactor.
                        // Because the remainder calculated above (temp) is in Q15 format,
                        // it must be shifted left by 1 to convert to Q16 format for the
                        // hardware to properly convert.
                        CMPAHR_reg_val = temp<<1;
    
                        ** If auto-conversion is enabled, the following step is performed
                           automatically in hardware and can be skipped
                        // This obtains the MEP count in digits, from
                        // 0,1, .... MEP_Scalefactor.
                        // 0x0080 (0.5 in Q8) is converted to 0.5 in Q15 by shifting left 7.
                        // This is added to fractional duty*MEP_SF product in order to round
                        // the decimal portion of the product up to the next integer if the
                        // decimal portion is >=0.5.
                        //
                        //Once again since this is Q15
                        // convert to Q0 by shifting:
                        CMPAHR_reg_val = (temp*MEP_ScaleFactor+(0x0080<<7))>>15;
    
                        ** If auto-conversion is enabled, the following step is performed
                           automatically in hardware and can be skipped
                        // Now the lower 8 bits contain the MEP count.
                        // Since the MEP count needs to be in the upper 8 bits of
                        // the 16 bit CMPAHR register, shift left by 8.
                        CMPAHR_reg_val = CMPAHR_reg_val << 8;
    
                        ** If auto-conversion is enabled, the following step is performed
                           automatically in hardware and can be skipped
                        // Add the offset and rounding
                        CMPAHR_reg_val += 0x0080;
    
                        // Write the values to the registers as one 32-bit or two 16-bits
                        EPwm1Regs.CMPA.bit.CMPA = CMPA_reg_val;
                        EPwm1Regs.CMPA.bit.CMPAHR = CMPAHR_reg_val;
                        */
    
                        //
                        // All the above operations may be condensed into
                        // the following form:
                        // EPWM1 calculations
    
    
             if(UpdateFine)
                             {
                                     CMPA_reg_val = ((long)DutyFineA * (EPwm3Regs.TBPRD)) >> 15;
                                     temp = ((long)DutyFineA * (EPwm3Regs.TBPRD)) ;
                                     temp = temp - ((long)CMPA_reg_val << 15);
    
    
                                    #if(AUTOCONVERT)
                                     CMPAHR_reg_val = temp << 1; // convert to Q16
    
                                    #else
                                     CMPAHR_reg_val = ((temp * MEP_ScaleFactor) +
                                                       (0x0080 << 7)) >> 15;
                                     CMPAHR_reg_val = CMPAHR_reg_val << 8;
    
                                    #endif
    
                                    //
                                    // Example for a 32 bit write to CMPA:CMPAHR
                                    //
                                     EPwm3Regs.CMPA.all = ((long)CMPA_reg_val) << 16 |
                                                           CMPAHR_reg_val; // loses lower 8-bits
    
    
                                     if (Reference==4 && duty_counter < 1000)
                                     {
                                         Duty_Capture[duty_counter] = EPwm3Regs.CMPA.all;
                                         Duty_Capture1[duty_counter] = _IQ16(input);
                                         duty_counter++;
    
                                     }
    
                             }
             //
                         // Call the scale factor optimizer lib function SFO()
                         // periodically to track for any change due to temp/voltage.
                         // This function generates MEP_ScaleFactor by running the
                         // MEP calibration module in the HRPWM logic. This scale
                         // factor can be used for all HRPWM channels. The SFO()
                         // function also updates the HRMSTEP register with the
                         // scale factor value.
                         //
                         status = SFO(); // in background, MEP calibration module
                                         // continuously updates MEP_ScaleFactor
    
                         if(status == SFO_ERROR)
                         {
                             error();   // SFO function returns 2 if an error occurs & #
                                        // of MEP steps/coarse step
                         }              // exceeds maximum of 255.

  • What is the duty that you see when you change values from 0 to 0.7?

  • Hello, The results were not sensible. The duty cycle was not changing when the set value was less than 0.1, and also the switching waveforms remained same even for some of the values of duty cycle. 

    In the end, the solution that worked for us was to still have a HRPWM with a up counter, invert signals for the PWM (see the code below), MEP is based on rising edge and finally work with 1-D as a reference duty cycle. I hope this would be helpful to others. 

            EPwm3Regs.AQCTLA.bit.ZRO = AQ_CLEAR;      // PWM toggle high/low
            EPwm3Regs.AQCTLA.bit.CAU = AQ_SET;
            EPwm3Regs.AQCTLB.bit.ZRO = AQ_CLEAR;
            EPwm3Regs.AQCTLB.bit.CBU = AQ_SET;

  • Hi,

    Thanks for sharing this. I need to get back and check the performance at lower duty cycle for down-count HRPWM.

    The solution that you've done is really smart! Appreciate you for sharing the idea here.

    Regards,

    Aditya