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.
I cannot get high-res output on ePWM1 when using shadow mode set to load on CTR=PRD. I’ve taken the hrpwm_ex1_duty_sfo C2000ware HRPWM example and modified to demonstrate this (code pasted below).
I can see that with the HRPWM shadow load event set to HRPWM_LOAD_ON_CNTR_ZERO there is a fine resolution on the PWM duty cycle output. However, with HRPWM_LOAD_ON_CNTR_PERIOD there is not. Is this a known restriction?
//############################################################################# // // FILE: hrpwm_ex1_duty_sfo.c // // TITLE: HRPWM Duty Control with SFO. // //! \addtogroup driver_example_list //! <h1>HRPWM Duty Control with SFO</h1> //! //! This example modifies the MEP control registers to show edge displacement //! for high-resolution period with ePWM in Up count mode //! due to the HRPWM control extension of the respective ePWM module. //! //! This example calls the following TI's MEP Scale Factor Optimizer (SFO) //! software library V8 functions: //! //! \b int \b SFO(); \n //! - updates MEP_ScaleFactor dynamically when HRPWM is in use //! - updates HRMSTEP register (exists only in EPwm1Regs register space) //! with MEP_ScaleFactor value //! - returns 2 if error: MEP_ScaleFactor is greater than maximum value of 255 //! (Auto-conversion may not function properly under this condition) //! - returns 1 when complete for the specified channel //! - returns 0 if not complete for the specified channel //! //! This example is intended to explain the HRPWM capabilities. The code can be //! optimized for code efficiency. Refer to TI's Digital power application //! examples and TI Digital Power Supply software libraries for details. //! //! \b External \b Connections \n //! - Monitor ePWM1/2/3/4 A/B pins on an oscilloscope. // //############################################################################# // // // $Copyright: // Copyright (C) 2021 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 "driverlib.h" #include "device.h" #include "board.h" #include "SFO_V8.h" #define EPWM_TIMER_TBPRD 100UL #define MIN_HRPWM_DUTY_PERCENT 4.0/((float32_t)EPWM_TIMER_TBPRD)*100.0 // // Defines // #define LAST_EPWM_INDEX_FOR_EXAMPLE 5 #define LOAD_MODE 1 // // Globals // float32_t dutyFine = MIN_HRPWM_DUTY_PERCENT; uint16_t status; int MEP_ScaleFactor; // Global variable used by the SFO library // Result can be used for all HRPWM channels // This variable is also copied to HRMSTEP // register by SFO() function. volatile uint32_t ePWM[] = {0, myEPWM1_BASE, myEPWM2_BASE, myEPWM3_BASE, myEPWM4_BASE}; // // Function Prototypes // void initHRPWM(uint32_t period); void error(void); //__interrupt void epwm1ISR(void); //__interrupt void epwm2ISR(void); //__interrupt void epwm3ISR(void); //__interrupt void epwm4ISR(void); // // Main // void main(void) { uint16_t i = 0; // // Initialize device clock and peripherals // Device_init(); // // Disable pin locks and enable internal pull ups. // Device_initGPIO(); // // Initialize PIE and clear PIE registers. Disables CPU interrupts. // Interrupt_initModule(); // // Initialize the PIE vector table with pointers to the shell Interrupt // Service Routines (ISR). // Interrupt_initVectorTable(); // // Assign the interrupt service routines to ePWM interrupts // //Interrupt_register(INT_EPWM1, &epwm1ISR); //Interrupt_register(INT_EPWM2, &epwm2ISR); //Interrupt_register(INT_EPWM3, &epwm3ISR); //Interrupt_register(INT_EPWM4, &epwm4ISR); // // Initialize the EPWM GPIO Pins and change the XBAR inputs from using GPIO0 // Board_init(); // // Calling SFO() updates the HRMSTEP register with calibrated MEP_ScaleFactor. // HRMSTEP must be populated with a scale factor value prior to enabling // high resolution period control. // while(status == SFO_INCOMPLETE) { status = SFO(); if(status == SFO_ERROR) { error(); // SFO function returns 2 if an error occurs & # of MEP } // steps/coarse step exceeds maximum of 255. } // // Disable sync(Freeze clock to PWM as well) // SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC); initHRPWM(EPWM_TIMER_TBPRD); // // Enable sync and clock to PWM // SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC); // Enable ePWM interrupts // //Interrupt_enable(INT_EPWM1); //Interrupt_enable(INT_EPWM2); //Interrupt_enable(INT_EPWM3); //Interrupt_enable(INT_EPWM4); // // Enable Global Interrupt (INTM) and realtime interrupt (DBGM) // EINT; ERTM; for(;;) { // // Sweep DutyFine // for(dutyFine = MIN_HRPWM_DUTY_PERCENT; dutyFine < 99.9; dutyFine += 0.01) { DEVICE_DELAY_US(1000); for(i=1; i<LAST_EPWM_INDEX_FOR_EXAMPLE; i++) { float32_t count = (dutyFine * (float32_t)(EPWM_TIMER_TBPRD << 8))/100; uint32_t compCount = (count); HRPWM_setCounterCompareValue(ePWM[i], HRPWM_COUNTER_COMPARE_A, compCount); HRPWM_setCounterCompareValue(ePWM[i], HRPWM_COUNTER_COMPARE_B, compCount); } // // 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. } } } // // epwm1ISR - ePWM 1 ISR // //__interrupt void epwm1ISR(void) //{ // EPWM_clearEventTriggerInterruptFlag(EPWM1_BASE); // Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP3); //} // // epwm2ISR - ePWM 2 ISR // //__interrupt void epwm2ISR(void) //{ // EPWM_clearEventTriggerInterruptFlag(EPWM2_BASE); // Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP3); //} // // epwm3ISR - ePWM 3 ISR // //__interrupt void epwm3ISR(void) //{ // EPWM_clearEventTriggerInterruptFlag(EPWM3_BASE); // Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP3); //} // // epwm4ISR - ePWM 4 ISR // //__interrupt void epwm4ISR(void) //{ // EPWM_clearEventTriggerInterruptFlag(EPWM4_BASE); // Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP3); //} void initHRPWM(uint32_t period) { uint16_t j; // // ePWM channel register configuration with HRPWM // ePWMxA / ePWMxB toggle low/high with MEP control on Rising edge // for (j=1;j<LAST_EPWM_INDEX_FOR_EXAMPLE;j++) { EPWM_setEmulationMode(ePWM[j], EPWM_EMULATION_FREE_RUN); // // Set-up TBCLK // EPWM_setTimeBasePeriod(ePWM[j], period-1); EPWM_setPhaseShift(ePWM[j], 0U); EPWM_setTimeBaseCounter(ePWM[j], 0U); // // set duty 50% initially // HRPWM_setCounterCompareValue(ePWM[j], HRPWM_COUNTER_COMPARE_A, (period/2 << 8)); HRPWM_setCounterCompareValue(ePWM[j], HRPWM_COUNTER_COMPARE_B, (period/2 << 8)); // // Set up counter mode // EPWM_setTimeBaseCounterMode(ePWM[j], EPWM_COUNTER_MODE_UP); EPWM_disablePhaseShiftLoad(ePWM[j]); EPWM_setClockPrescaler(ePWM[j], EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1); EPWM_setSyncOutPulseMode(ePWM[j], EPWM_SYNC_OUT_PULSE_DISABLED); // // Set up shadowing // #if (LOAD_MODE == 0) EPWM_setCounterCompareShadowLoadMode(ePWM[j], EPWM_COUNTER_COMPARE_A, EPWM_COMP_LOAD_ON_CNTR_ZERO); EPWM_setCounterCompareShadowLoadMode(ePWM[j], EPWM_COUNTER_COMPARE_B, EPWM_COMP_LOAD_ON_CNTR_ZERO); #else EPWM_setCounterCompareShadowLoadMode(ePWM[j], EPWM_COUNTER_COMPARE_A, EPWM_COMP_LOAD_ON_CNTR_PERIOD); EPWM_setCounterCompareShadowLoadMode(ePWM[j], EPWM_COUNTER_COMPARE_B, EPWM_COMP_LOAD_ON_CNTR_PERIOD); #endif // // Set actions // EPWM_setActionQualifierAction(ePWM[j], EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO); EPWM_setActionQualifierAction(ePWM[j], EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO); EPWM_setActionQualifierAction(ePWM[j], EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA); EPWM_setActionQualifierAction(ePWM[j], EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPB); HRPWM_setMEPEdgeSelect(ePWM[j], HRPWM_CHANNEL_A, HRPWM_MEP_CTRL_FALLING_EDGE); HRPWM_setMEPControlMode(ePWM[j], HRPWM_CHANNEL_A, HRPWM_MEP_DUTY_PERIOD_CTRL); #if (LOAD_MODE == 0) HRPWM_setCounterCompareShadowLoadEvent(ePWM[j], HRPWM_CHANNEL_A, HRPWM_LOAD_ON_CNTR_ZERO); #else HRPWM_setCounterCompareShadowLoadEvent(ePWM[j], HRPWM_CHANNEL_A, HRPWM_LOAD_ON_CNTR_PERIOD); #endif HRPWM_setMEPEdgeSelect(ePWM[j], HRPWM_CHANNEL_B, HRPWM_MEP_CTRL_FALLING_EDGE); HRPWM_setMEPControlMode(ePWM[j], HRPWM_CHANNEL_B, HRPWM_MEP_DUTY_PERIOD_CTRL); #if (LOAD_MODE == 0) HRPWM_setCounterCompareShadowLoadEvent(ePWM[j], HRPWM_CHANNEL_B, HRPWM_LOAD_ON_CNTR_ZERO); #else HRPWM_setCounterCompareShadowLoadEvent(ePWM[j], HRPWM_CHANNEL_B, HRPWM_LOAD_ON_CNTR_PERIOD); #endif HRPWM_enableAutoConversion(ePWM[j]); // // Turn off high-resolution period control. // HRPWM_disablePeriodControl(ePWM[j]); HRPWM_disablePhaseShiftLoad(ePWM[j]); // // Interrupt where we will change the Compare Values // Select INT on Time base counter zero event, // Enable INT, generate INT on 1st event // //EPWM_setInterruptSource(ePWM[j], EPWM_INT_TBCTR_ZERO); //EPWM_enableInterrupt(ePWM[j]); //EPWM_setInterruptEventCount(ePWM[j], 1U); } } // // error - Halt debugger when called // void error (void) { ESTOP0; // Stop here and handle error }
Hello Guz,
I have not been able to observe such things on the TMS320F28379D. What values are set in the EPwm1Regs.CMPCTL.LOADAMODE and LOADBMODE. Maybe you try it with: EPwm1Regs.CMPCTL.LOADAMODE = 0x01; EPwm1Regs.CMPCTL.LOADBMODE = 0x01; In the Else part of your Init.
Best regards
Leon
Hi Leon, I did try that as well, but I had no effect. The only time I saw high-res PWM output was when HRCNFG.HRLOAD = 0x00.
Gus, let me try and find my code with HRPWM in UP-COUNT MODE.
Did this original example use UP-COUNT mode?
I probably wrote this example myself, but I don't remember... I should still have all of old files.
The HRPWM example I have:
// // Set up counter mode // EPWM_setTimeBaseCounterMode(ePWM[j], EPWM_COUNTER_MODE_UP); EPWM_disablePhaseShiftLoad(ePWM[j]); EPWM_setClockPrescaler(ePWM[j], EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1); EPWM_setSyncOutPulseMode(ePWM[j], EPWM_SYNC_OUT_PULSE_DISABLED); // // Set up shadowing // EPWM_setCounterCompareShadowLoadMode(ePWM[j], EPWM_COUNTER_COMPARE_A, EPWM_COMP_LOAD_ON_CNTR_PERIOD); EPWM_setCounterCompareShadowLoadMode(ePWM[j], EPWM_COUNTER_COMPARE_B, EPWM_COMP_LOAD_ON_CNTR_PERIOD); // // Set actions // EPWM_setActionQualifierAction(ePWM[j], EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO); EPWM_setActionQualifierAction(ePWM[j], EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO); EPWM_setActionQualifierAction(ePWM[j], EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA); EPWM_setActionQualifierAction(ePWM[j], EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPB);
This REQUIRED the LOAD settings to be:
HRPWM_setMEPEdgeSelect(ePWM[j], HRPWM_CHANNEL_A, HRPWM_MEP_CTRL_FALLING_EDGE); HRPWM_setMEPControlMode(ePWM[j], HRPWM_CHANNEL_A, HRPWM_MEP_DUTY_PERIOD_CTRL); HRPWM_setCounterCompareShadowLoadEvent(ePWM[j], HRPWM_CHANNEL_A, HRPWM_LOAD_ON_CNTR_ZERO_PERIOD); HRPWM_setMEPEdgeSelect(ePWM[j], HRPWM_CHANNEL_B, HRPWM_MEP_CTRL_FALLING_EDGE); HRPWM_setMEPControlMode(ePWM[j], HRPWM_CHANNEL_B, HRPWM_MEP_DUTY_PERIOD_CTRL); HRPWM_setCounterCompareShadowLoadEvent(ePWM[j], HRPWM_CHANNEL_B, HRPWM_LOAD_ON_CNTR_ZERO_PERIOD);
Nima,
Thanks for looking into this. I do see that with the load setting changed to HRPWM_LOAD_ON_CNTR_ZERO_PERIOD the high-res output works as expected.