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.

C2000WARE: CMPA nor CMPAHR registers are being initialized or updated

Part Number: C2000WARE

Hi,

I am having an issue that my PWM outputs are constantly PWMA1 = 0 and PWMB1 = 1, because i'm using active complimentary high PWM and my CMPA and CMPAHR values are not updating in code, or even initialising. It seems they are stuck at 0. Code below for the bits that are of concern: 

interrupt void MainCPU_ISR(void)
{
// Read sensor data across the ADCs
// Read all in CPU1 to make easier to distribute between cores
inputVolts_CLA1 = AdcbResultRegs.ADCRESULT5; // Read the ADC value placed into ADC register
inputVolts_CPU2 = inputVolts_CLA1; // Place a copy into CLA shared RAM

// May need to type-cast these values as floating point for the DCL CLA calculations to be accurate as possible
flyVolts = ((AdcaResultRegs.ADCRESULT1
+ AdcaResultRegs.ADCRESULT2
+ AdcaResultRegs.ADCRESULT3
+ AdcaResultRegs.ADCRESULT4
+ AdcaResultRegs.ADCRESULT5
+ AdcaResultRegs.ADCRESULT6
+ AdcaResultRegs.ADCRESULT7
+ AdcaResultRegs.ADCRESULT8)/8); // Compute the average reading on the over-sampled Cathode Output Voltage SOCs
// 8x over-sampling for cathode voltage

// Modulate the PWM register compare bit
EPwm1Regs.CMPA.bit.CMPAHR = MAX_DUTY_FLY;

// Return from interrupt
AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // Clear ADC INT1 flag
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge PIE group 1 to enable further interrupts

Isr1Count++;
}

The ISR is triggering fine, even when I place a constant value in the CMPAHR register, nothing happens. Have also tried with CMPA. Here is my PWM initialisation code:

// Fly-back PWM Module
void InitEPwm1(void)
{
// Setup time-base
EPwm1Regs.TBCTL.bit.CTRMODE = 0; // Up count mode
EPwm1Regs.TBPRD = FLY_PERIOD; // Set timer period
// EPwm1Regs.TBPRDHR = FLY_PERIOD; // Set timer period, for HR bits
EPwm1Regs.TBCTL.bit.PHSEN = 0; // Disable phase loading
EPwm1Regs.TBPHS.bit.TBPHS = 0x0000; // Phase is 0
EPwm1Regs.TBCTR = 0x0000; // Clear counter

// Set TBCLK = EPWMCLK = 100MHz
// Although the system clock is 200MHz, maximum EPWM clock is 100MHz
EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0; // Clock ratio to SYSCLKOUT
EPwm1Regs.TBCTL.bit.CLKDIV = 0;
EPwm1Regs.TBCTL.bit.SYNCOSEL = 1; // SYNC output on CTR = 0

// Set initial modulation in Counter Compare Module
EPwm1Regs.CMPA.bit.CMPAHR = 10; // Set compare A value initially to zero (we will want to soft-start)

// Set actions in the action qualifier module
EPwm1Regs.AQCTLA.bit.CAU = 1; // Action When TBCTR = CMPA on Up Count -> Force PWMA Output Low
EPwm1Regs.AQCTLA.bit.CAD = 2; // Action when TBCTR = CMPA on Down Count -> Force PWMA Output High
EPwm1Regs.DBCTL.bit.OUT_MODE = 3; // Fully enable dead-band for both falling and rising-edges
EPwm1Regs.DBCTL.bit.POLSEL = 2; // Active High Complimentary (AHC) Mode
EPwm1Regs.DBFEDHR.bit.DBFEDHR = FLY_DEADTIME; // Set dead-time for falling edge
EPwm1Regs.DBREDHR.bit.DBREDHR = FLY_DEADTIME; // Set dead-time for rising edge /* BEEN CHANGED BY JM HOLLAND */ TO HR BITS

EALLOW;

// Digital Compare (DC) Submodule
// Generate DCAH signal from Trip Combination Input
EPwm1Regs.DCTRIPSEL.bit.DCAHCOMPSEL = 15; // OR together all Trip combinations selected by DCALTRIPSEL Registers -> DCAH
EPwm1Regs.DCTRIPSEL.bit.DCBHCOMPSEL = 15; // OR together all Trip combinations selected by DCALTRIPSEL Registers -> DCBH

// DCAH/DCAEVT1/2 are used for one-shot trip faults
// DCBH/DCBEVT1/2 are used for cycle-by-cycle faults
// Individually select which trip signals are passed into the block to be ORed together
EPwm1Regs.DCAHTRIPSEL.bit.TRIPINPUT5 = 1; // Input Over Voltage Trip One-Shot Fault
EPwm1Regs.DCAHTRIPSEL.bit.TRIPINPUT7 = 1; // Cathode Over Voltage Trip One-Shot Fault
EPwm1Regs.DCBHTRIPSEL.bit.TRIPINPUT4 = 1; // Fly-back Over Current Trip Cycle-by-Cycle Fault

// Event A Action Qualifier block
// Generate DCAEVT1/DCBEVT1 trip events according to DCAH and DCBH
// Only EVT1 can be used for OST, and EVT2 for CBC
// Need to enable both A and B of these registers to trigger both the PWMx outputs
EPwm1Regs.TZDCSEL.bit.DCAEVT1 = 2; // DCAEVT1: DCAH = high, DCAL = don't care
EPwm1Regs.TZDCSEL.bit.DCAEVT2 = 2; // DCAEVT2: DCAH = high, DCAL = don't care
EPwm1Regs.TZDCSEL.bit.DCBEVT1 = 2; // DCBEVT1: DCBH = high, DCBL = don't care
EPwm1Regs.TZDCSEL.bit.DCBEVT2 = 2; // DCBEVT2: DCBH = high, DCBL = don't care

// Event Triggering block - Generate DCAEVT1.force and DCBEVT1.force signals (One Shot Source)
EPwm1Regs.DCACTL.bit.EVT1SRCSEL = 0; // Select DCAEVT1 as input signal
EPwm1Regs.DCACTL.bit.EVT1FRCSYNCSEL = 1; // Select Asynchronous
EPwm1Regs.TZFRC.bit.DCAEVT1 = 0; // Enable DCAEVT1.force output signal
EPwm1Regs.DCBCTL.bit.EVT1SRCSEL = 0; // Select DCBEVT1 as input signal
EPwm1Regs.DCBCTL.bit.EVT1FRCSYNCSEL = 1; // Select Asynchronous
EPwm1Regs.TZFRC.bit.DCBEVT1 = 0; // Enable DCBEVT1.force output signal

// Event Triggering block - Generated DCAEVT2.force and DCBEVT2.force signals (Cycle-by-Cycle Source)
EPwm1Regs.DCACTL.bit.EVT2SRCSEL = 0; // Select DCAEVT2 as input signal
EPwm1Regs.DCACTL.bit.EVT2FRCSYNCSEL = 1; // Select Asynchronous
EPwm1Regs.TZFRC.bit.DCAEVT2 = 0; // Enable DCAEVT2.force output signal
EPwm1Regs.DCBCTL.bit.EVT2SRCSEL = 0; // Select DCBEVT2 as input signal
EPwm1Regs.DCBCTL.bit.EVT2FRCSYNCSEL = 1; // Select Asynchronous
EPwm1Regs.TZFRC.bit.DCBEVT2 = 0; // Enable DCBEVT2.force output signal

// Trip Zone Submodule - Trip Logic block
// Using only TZCTL and EVT1
EPwm1Regs.TZCTL2.bit.ETZE = 0; // Use only TZCTL register, disable TSCTL2

EPwm1Regs.TZCTL.bit.DCAEVT1 = 0x10; // On trip, force EPWM1A to a LOW state
EPwm1Regs.TZCTL.bit.DCBEVT1 = 0x10; // On trip, force EPWM1B to a LOW state
EPwm1Regs.TZCTL.bit.DCAEVT2 = 0x10; // On trip, force EPWM1A to a LOW state
EPwm1Regs.TZCTL.bit.DCBEVT2 = 0x10; // On trip, force EPWM1B to a LOW state
EPwm1Regs.TZCTL.bit.TZA = 0x10; // On trip, force EPWM1A to a LOW state
EPwm1Regs.TZCTL.bit.TZB = 0x10; // On trip, force EPWM1B to a LOW state

// Trip-Zone Submodule - select OSHT and CBC sources
EPwm1Regs.TZSEL.bit.DCAEVT1 = 1; // Enable DCAEVT1 as one-shot-trip source for this ePWM module
EPwm1Regs.TZSEL.bit.DCBEVT1 = 1; // Enable DCBEVT1 as one-shot trip source for this ePWM module
EPwm1Regs.TZSEL.bit.DCAEVT2 = 1; // Enable DCAEVT2 as a CBC trip source for this ePWM module
EPwm1Regs.TZSEL.bit.DCBEVT2 = 1; // Enable DCBEVT2 as a CBC trip source for this ePWM module

//EPwm1Regs.TZFRC.bit.OST = 0x01; // Force trip to disable PWM outputs
//EPwm1Regs.TZFRC.bit.CBC = 0x01; // Force cycle-by-cycle trip to disable PWM outputs

// May insert code which either waits a delay time before clearing the one-shot fault, or something along those lines
// At the moment, I believe, this will turn OFF the PWM module until user-reset.

// High Resolution Pulse Width Modulator Definitions
// We may need more information here!
EPwm1Regs.HRCNFG.all = 0x0; // Clear all bits first within the HRCNFG register
EPwm1Regs.HRCNFG.bit.EDGMODE = 0x3; // Control both falling and rising edge delays with the MEP
EPwm1Regs.HRCNFG.bit.CTLMODE = 0x0; // CMPAHR controls the MEP
EPwm1Regs.HRCNFG.bit.HRLOAD = 0x0; // Shadow load on CTR=Zero for count-up mode

EDIS;

// Set up ADC trigger pulse to ADC SOCA (ADCAINT1)
EPwm1Regs.ETSEL.bit.SOCAEN = 0; // Disable SOCA on A group
EPwm1Regs.ETSEL.bit.SOCASEL = 2; // Select SOCA on period match
EPwm1Regs.ETSEL.bit.SOCAEN = 1; // Enable SOCA
EPwm1Regs.ETPS.bit.SOCAPRD = 1; // Generate pulse on 1st event
}

I have checked all the peripheral clocks and the TBPRD is counting correctly, but no modulation occurs because there is nothing in the CMPA nor CMPAHR bits. I have tried multiple combinations of CMPA and CMPAHR as you can see and no matter what, something is stopping the CMPA and CMPAHR register from actually being updated with the value I set. 

Any ideas on what might be causing this bug from my code snippet?

Best wishes,
Joel

  • Please use driverlib since it takes care of EALLOW, EDIS and enables all peripherals at Device_init().

    Start from one of our examples:

    https://dev.ti.com/tirex/explore/node?node=AMx7heStjdxOgGNR8NwrlQ__gYkahfz__LATEST

    There are over 10 examples for EACH device.

  • Unfortunately I am not willing to learn how to use driverlib, as I do not have the time, and know how to program using the direct register access method much better. All of my code is written in this way and I do not wish to start over again. 

  • Joel, It is much easier and 90% of the use cases are already configured in an example.

    Your other option is to check the examples that are working and comparing them to your code to see what the difference is between the register configurations.

    The items that come to mind are:

    1. EPWMx is not enabled. Use Sysctl module to enable it.

    2. The registers you are writing to are EALLOW protected.

    3. The registers are updating but your debugger is not halted to show it.

    Again, if you are a beginner, using bitfields will be a steep learning curve... You will have to be an expert to write all the code yourself from scratch.

    Start from an example and then make modifications and insert your code into it.

  • Hi Nima,

    In my opinion it is not much easier. I have looked through some examples using driverlib, ones that are slightly more complicated like digital controllers I need, and I am lost before I even spend a minute looking at it. There are functions all over the place with arguments all over the place. With the examples I have been using with direct register access they have been very easy to understand and quick looks in the technical manual tell me what I need to set certain bits as if I want to slightly change the functions of the modules. 

    I believe the main error I have in this example, is using the HRPWM module. There are none that I can find which use the HRPWM module AND the digital controller library. So it's hard to understand how I can use a controller output to modulate the HRPWM Module. That part is missing from my code. It is below, using my "flyback_command" in place of "dutyFine" in the examples.

    CMPA_reg_val = ((long)flyback_command * EPwm1Regs.TBPRD)>>15;
    CMPB_reg_val = ((long)flyback_command * EPwm1Regs.TBPRD)>>15;
    temp = ((long)flyback_command * EPwm1Regs.TBPRD) ;
    temp1 = ((long)flyback_command * EPwm1Regs.TBPRD) ;
    temp = temp - ((long)CMPA_reg_val<<15);
    temp1 = temp1 - ((long)CMPB_reg_val<<15);

    // Shift Autoconversions 1-bit left
    CMPAHR_reg_val = temp<<1; // convert to Q16
    CMPBHR_reg_val = temp<<1; // convert to Q16

    // 32 bit write to CMPA:CMPAHR
    EPwm1Regs.CMPA.all = ((long)CMPA_reg_val)<<16 |
    CMPAHR_reg_val; // loses lower 8-bits

    But CMPA, CMPAHR, CMPB, CMPBHR are all zero. I am unsure whether the type-casting and shifting is done properly. I clamp the output of my DCL controller from 0.0 to 0.25 to represent a maximum duty cycle of 25%. The example I have been referring to uses dutyFine as an unsigned 16-bit integer rather than a float, and cast that to a long. 

    Is that where I am going wrong? Am I shifting everything out of the register effectively making it equal to zero?

    Best,
    Joel

  • For now, I have taken my float value from 0-0.25 and multiplied this by the maximum uint16_t value, but my max duty cycle is coming out close to 50% instead of 25%. Anyway, at least the registers have the CMPA values inserted now, but still an issue with the conversion.

    Uint16_t flyback_16bit_command = flyback_command * 65536; 

    modulate with the above after doing bit shifting and etc.

    Joel

  • Hi,

    If auto-conversion feature of HRPWM module is enabled then you can actually directly program all the 32-bits of CMPA register (both CMPA and CMPAHR) in one go.

    For example, if "duty" in floating point and "time period" is the period of your PWM outputs then you can program by converting it to Q16 Fixed point format like below:

    HWREG(EPWM1_BASE + HRPWM_O_CMPA) = (uint32_t)((duty*(float32_t)time_period) * (float32_t)((uint32_t)1 << 16) + 0.5f);

    I would suggest you to review the configDuty() function inside device_loopback_example() available in C2000Ware directory: C2000Ware_3_04_00_00\examples\demos\hrcap_hrpwm_data_transfer\device_loopback

    Although it's a driverlib based example, but reviewing it would help you to understand how to program the HRPWM registers to update the duty etc.

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

    Regards

    Himanshu

  • Hi Himanshu,

    I have enabled autoconversion already in the example I have given you. And those equations are also directly from an example of HRPWM that is provided by TI and provides how to do the 32-bit direct register programming. But I still get the wrong answer. 

  • Have you tried with the above equation that I provided in my last response? What is the output duty you observe after using the given equation and what is the value of period that you are using? If you are using the up-down counter then the value of time_period in the equations should be half of the actual period of the output signal. 

    Try to debug the solution based on these suggestions and share the observed waveforms here.

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

    Regards

    Himanshu

  • Hi Himanshu, 

    // Duty cycle command should be a volatile float32_t, and time period an unsigned 16-bit integer
    EPwm1Regs.CMPA.all = (uint32_t)((flyback_command*(float32_t)FLY_PERIOD) * (float32_t)((uint32_t)1 << 16) + 0.5f);

    I added the above code. My duty cycle command was 0.05, 5%. I am getting this okay in the CMPA register, it says 9 which is about 5% of my 1MHz time period.

    However, the CMPAHR register has value of 62208 in the register... I assume that this is too large? 

    I am getting a 5% duty, and complimentary high output on the other (with a little crossover, unfortunately.. maybe an issue with my dead-time programming). 

    But I have did a direct 32-bit programming of the register with the above - and made sure my duty was 32-bit float and my period a 16-bit unsigned int, just like in the example. Yet still, CMPAHR register not correct.

    Joel

  • Hi,

    Good to know that you now got the right duty value by adopting the above equation. 

    Regarding the CMPAHR register, only the UPPER 8-bits contain the high-resolution portion (most significant 8-bits). Look at the register description in the device TRM. Also if the auto-conversion mode is enabled then the CMPAHR just needs to be populated with fractional duty with Q16 format (as taken care in my earlier shared equation) and the hardware will automatically translate this into number of MEP steps with the help of SFO based calibration. See the below description in TRM, I would highly recommend you to completely go through the TRM chapter to improve your understanding of these modules:

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

    Regards

    Himanshu

  • Hi Himanshu,

    If you see my reply, I did already update the registers with the single 32-bit write equation that you provided me with. I still do not get the correct value for the CMAPHR register. I also already have auto-conversion enabled.


    So unless I am missing some more code, then I don't understand why my results would be any different?

    Best,

    Joel

  • Hi,

    Actually you are getting the right CMPAHR value (evident as you are getting the right duty) but the way you are interpreting it is incorrect. That's what I was trying to suggest in my last reply, the way you need to interpret the CMPAHR value is different when the auto-conversion mode is enabled. Take some time to go through the documentation for auto-conversion mode that I pointed in my last reply, that should help to make things clear.

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

    Regards

    Himanshu

  • Hi Himanshu -

    I have completely followed this whole way your point. My issue remains. I have got the auto-conversion mode ENABLED. 

    I have used the equation that you gave me, and still, the value in CMPAHR register is not correct. 

    Even though I have used the exact same equation that you suggested that I use for when autoconversion is enabled. 

    I have performed the shifting and a "single 32-bit write" which SHOULD write the value to both the CMPA and CMPAHR registers, as far as I know. 

    Obviously, I must perform another shift or something which represents the value correctly - but the equation you have gave me is nothing like the one given in the technical manual, so I cannot tell what I am doing wrong. 

    I will attempt to use the equations in the technical manual rather than the ones provided by yourself in a previous reply, since they are quite different.

    Maybe the problem is you write HWREG(EPWM1_BASE + HRPWM_O_CMPA) whereas I use .CMPA.all bitfield. I am unsure. But I have inserted the equation you gave me exactly how it was written, with autoconversion enabled, yet the answer is incorrect. I would assume, as you stated, the shifting was performed by this "single" equation and I would not need to do more shifting to get the correct CMAPHR value? 

    Sorry if it does not make sense, I have just not been able to get my head around what I am missing. 


    Best wishes,
    Joel

  • Issue now is with C2000, does not recognise such a function as "frac(...)". 

    = frac (PWMDuty*PWMperiod<<8).

    How do we seperate the fractional part of the multiplication in code? 

    I am not an expert in coding. I understand frac() is not a function supported by TI C2000 but I would expect instead the technical manual would actually show you how to perform the frac conversion rather than just put frac(). 

  • My HRLOAD register is sitting at 0 during program execution. Is this possibly the issue? I will attach image of what my waveform looks like - switching frequency is 1MHz, and duty cycle is sitting somewhere between 3-5%. 

    DBFEDHR = DBREDHR = 5
    TBPRD = 199 
    TBPRDHR = 0
    DBFED = DBRED = 0 (I assume you don't program these is you are using HRPWM)

    HRMSTEP = 33
    HRLOAD = 0
    CMPA = 9
    CMPAHR = 62208

    Does this help pin-point what is happening with my signals?

    What is even more confusing, is that CMPBHR register has value of 256, with CMPB being 100. Cannot understand these results, whatsoever.
    There is also overlap between my gate drive signals, despite programming the high resolution deadtime registers, as you can see in the waveforms.

    Thanks for all your help so far, it really is appreciated.

    Joel

  • You can actually use the fracf32() intrinsic on C2000 by enabling FPU32 option in your compiler settings, refer to the intrinsics section in the C2000 compiler user guide for more details.

    You do not need to separate out the fractional part if you make use of the equation that I suggested because it treats the input duty as floating point in the Q16 format so it can be directly written as-it-is to the CMPA register if the auto-conversion mode is enabled. This really helps to speed up the things which the original intention of adding auto-conversion mode. But you can of course compute the CMPA and CMPAHR values separately and write them individually if that seems easier to you, this is also absolutely correct.

    HRLOAD bit = 0 just means that CMPAHR shadow value will be loaded at TBCTR=0, that is not an issue.

    Do you intend to use the dead-band in high-resolution or just the duty in High-resolution? In either case you need to program the DBRED and DBFED as well with suitable values to avoid the overlap.

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

    Regards

    Himanshu