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.
Hello everyone,
I'm just implementing a few optimizations of my code of the dual active bridge power converter.
To calculate the HRPWM TBPHS register values, I'm using the following example code:
PHSFine = -(long)controlvalue; //controlvalue is negativ PHS_reg_val = (long)(PHSFine * (EPwm3Regs.TBPRD*2 + 2))>>15; tempPHS = (long)(PHSFine * (EPwm3Regs.TBPRD*2 + 2)) ; tempPHS = tempPHS - ((long)PHS_reg_val << 15); PHSHR_reg_val = tempPHS << 1; // convert to Q16 EPwm3Regs.TBPHS.all = (((long)PHS_reg_val) << 16) | PHSHR_reg_val; // loses lower 8-bits EPwm4Regs.TBPHS.all = (((long)PHS_reg_val) << 16) | PHSHR_reg_val; // loses lower 8-bits
I have the following issue:
during the control phase, the PHSHR_reg_val is always near the maximum value and jumps between ~65000 and ~200. The PHS_reg_val value jumps also between two values.
This results in jitter on the PWM signal and is no smooth transition.
If I add 16384 to PHSFine and subtract 100 from the register value PHS_reg_val , the jitter disappears. The code follows.
PHSFine = 16384 +(long)controlvalue; //Phase Shift Mode PHS_reg_val = (long)(PHSFine * (EPwm3Regs.TBPRD*2 + 2))>>15; tempPHS = (long)(PHSFine * (EPwm3Regs.TBPRD*2 + 2)) ; tempPHS = tempPHS - ((long)PHS_reg_val << 15); PHSHR_reg_val = tempPHS << 1; // convert to Q16 EPwm3Regs.TBPHS.all = (100-((long)PHS_reg_val) << 16) | PHSHR_reg_val; // loses lower 8-bits EPwm4Regs.TBPHS.all = (100-((long)PHS_reg_val) << 16) | PHSHR_reg_val; // loses lower 8-bits
I cannot understand why there is a difference because normally the control loop should generate the same phase shift value.
Thanks for your help.
Hi Patrick,
f I add 16384 to PHSFine and subtract 100 from the register value PHS_reg_val , the jitter disappears. The code follows.
This is strange. Would it be possible to see how you have configured the EPWM module?
Are you applying a synchronization pulse at every EPWM period or once on initialization?
Best Regards,
Marlyn
Thanks for your quick answer.
The subroutines are called one after the other. Firstly, I call initEPWMstartup_project(void) and then initEPWM_project(void).
void initEPWMstartup_project(void) { volatile struct EPWM_REGS *ePWM[] = {0, &EPwm1Regs, &EPwm2Regs, &EPwm3Regs, &EPwm4Regs, &EPwm7Regs, &EPwm8Regs}; volatile struct EPWM_REGS *ePWM_LV[] = {0, &EPwm1Regs, &EPwm2Regs, &EPwm7Regs, &EPwm8Regs}; EALLOW; //stops the clock within the enabled EPWMs CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 0; // Disable TBCLK within the EPWM SyncSocRegs.SYNCSELECT.bit.EPWM4SYNCIN = 0x0; //EPWM4 is also synchronized with the clock of EPWM1 SyncSocRegs.SYNCSELECT.bit.EPWM7SYNCIN = 0x0; //EPWM4 is also synchronized with the clock of EPWM1 int j = 0; for(j=1; j<=PWM_CH; j++) { (*ePWM[j]).TBCTL.bit.HSPCLKDIV = 0; // Change clock divider to /1 (*ePWM[j]).TBPRD = SWPERIOD; // Period = (fSYSCLK/fs)/2 - 1 = 100e6/250e3 - 1 = 199 (*ePWM[j]).TBPHS.bit.TBPHS = 0; // Set Phase register to 50% period (*ePWM[j]).TBCTR = 0; // clear TB counter (*ePWM[j]).TBCTL.bit.SYNCOSEL = TB_SYNC_IN; // Master: sync out pulse at counter zero - all other PWM TB_SYNC_IN (slave) (*ePWM[j]).TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN; // Sawtooth carrier (*ePWM[j]).TBCTL.bit.PHSEN = TB_ENABLE; // Phase loading enable (*ePWM[j]).TBCTL.bit.PRDLD = TB_SHADOW; // use shadow register for TBPRD (synchronized pulses) (*ePWM[j]).TBCTL.bit.HSPCLKDIV = TB_DIV1; // TBCLK = SYSCLKOUT (two registers, same functionality-history) (*ePWM[j]).TBCTL.bit.CLKDIV = TB_DIV1; // TBCLK = SYSCLKOUT (two registers, same functionality-history) (*ePWM[j]).TBCTL.bit.PHSDIR = 0; (*ePWM[j]).CMPCTL.bit.SHDWAMODE = CC_SHADOW; // use shadow register for compare value A (*ePWM[j]).CMPCTL.bit.SHDWBMODE = CC_SHADOW; // use shadow register for compare value B (*ePWM[j]).CMPCTL.bit.LOADAMODE = CC_CTR_ZERO; // load on CTR = TBPRD and ZRO. Shadow Mode (Ref Manuel S.1833) // (definition when the content is transferred from shadow to active register) // Time-base counter equal to the period (TBCTR = TBPRD) // Time-base counter equal to zero (TBCTR = 0x00) // Both CTR = PRD and CTR = Zero (*ePWM[j]).CMPCTL.bit.LOADBMODE = CC_CTR_ZERO; // load on CTR = TBPRD and ZRO (*ePWM[j]).DBCTL.bit.OUT_MODE = DB_FULL_ENABLE; // Deadband enabled (*ePWM[j]).DBCTL.bit.POLSEL = DB_ACTV_HIC; (*ePWM[j]).DBCTL.bit.IN_MODE = DBA_ALL; (*ePWM[j]).TZCTL.bit.TZA = TZ_FORCE_LO; // Force to low at trip (fault condition) (*ePWM[j]).TZCTL.bit.TZB = TZ_FORCE_LO; // Force to low at trip (fault condition) (*ePWM[j]).TZFRC.bit.OST = 1; // Enable TZ interrupt on any TZ event //HRPWM (*ePWM[j]).HRCNFG.all = 0x0; // clear all bits first (*ePWM[j]).HRCNFG.bit.EDGMODE = HR_BEP; // Control both Edge Position (*ePWM[j]).HRCNFG.bit.CTLMODE = HR_PHS; // Phasecontrol controls the MEP (*ePWM[j]).HRCNFG.bit.HRLOAD = HR_CTR_PRD; // Shadow load on CTR=PRD (*ePWM[j]).HRCNFG.bit.EDGMODEB = HR_BEP; // Control both Edge Position (*ePWM[j]).HRCNFG.bit.CTLMODEB = HR_PHS; // Phasecontrol controls the MEP (*ePWM[j]).HRCNFG.bit.HRLOADB = HR_CTR_PRD; // Shadow load on CTR=PRD (*ePWM[j]).TBPHS.bit.TBPHSHR = (1 << 8); //init HRPWM (*ePWM[j]).HRPCTL.bit.HRPE = 0; (*ePWM[j]).CMPA.bit.CMPA = 20 ; (*ePWM[j]).CMPB.bit.CMPB = SWPERIOD - 20 ; (*ePWM[j]).TBPRDHR = SWPERIOD; (*ePWM[j]).HRCNFG.bit.AUTOCONV = 1; // Enable auto-conversion (*ePWM[j]).DBFED.all = 3; // Dead Time Xns Deadband Generator Falling (*ePWM[j]).DBRED.all = 3; // Dead Time Xns Deadband Generator rising (*ePWM[j]).HRPCTL.bit.TBPHSHRLOADE = 1; // Enable TBPHSHR sync // (required for updwn count HR control) } for(j=1; j<=4; j++) { (*ePWM_LV[j]).AQCTLA.bit.CAU = AQ_CLEAR; //Counter equals zero (*ePWM_LV[j]).AQCTLA.bit.CBD = AQ_SET; //Counter equals CMPA on up-count (CAU) (*ePWM_LV[j]).AQCSFRC.bit.CSFB = AQ_CLEAR; //force output B low } //Setup PWM1 (DSP_DO_LV1_PWM_2L(B),DSP_DO_LV1_PWM_2H(A)) // Set up Action Qualifier regs (AQ) -- what happens on the output pin EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // Master: sync out pulse at counter zero - all other PWM TB_SYNC_IN (slave) // Set up event trigger for ADC conversion start at max. and 0 EPwm1Regs.ETSEL.bit.SOCASEL = ET_CTR_ZERO; // SOCA at counter zero WAS ZERO EPwm1Regs.ETSEL.bit.SOCAEN = 0; // Enable SOCA EPwm1Regs.ETPS.bit.SOCAPRD = ET_1ST; // SOCA on every event /***************************************************************************************************************************************/ //Setup PWM2 (DSP_DO_LV1_PWM_1L(B),DSP_DO_LV1_PWM_1H(A)) // Set up Action Qualifier regs (AQ) -- what happens on the output pin EPwm2Regs.AQCTLA.bit.CAU = AQ_SET; EPwm2Regs.AQCTLA.bit.CBD = AQ_CLEAR; EPwm2Regs.AQCSFRC.bit.CSFB = AQ_CLEAR; //force output B low /***************************************************************************************************************************************/ //Setup PWM7 (DSP_DO_LV2_PWM_2L(B),DSP_DO_LV2_PWM_2H(A)) EPwm7Regs.AQCTLA.bit.CAU = AQ_SET; EPwm7Regs.AQCTLA.bit.CBD = AQ_CLEAR; EPwm7Regs.AQCSFRC.bit.CSFB = AQ_CLEAR; /****************************************************************************************************************************/ //Setup PWM3 (DSP_DO_HV_PWM_1L(A),DSP_DO_HV_PWM_1H(B)) -- caution: pinning swapped compared to LV EPwm3Regs.DBFED.all = 13; // Dead Time X0ns Deadband Generator Falling EPwm3Regs.DBRED.all = 15; // Dead Time Xns Deadband Generator rising EPwm3Regs.AQCTLA.bit.ZRO = AQ_SET; //Counter equals zero EPwm3Regs.AQCTLA.bit.PRD = AQ_CLEAR; //Counter equals CMPA on up-count (CAU) EPwm3Regs.AQCSFRC.bit.CSFB = AQ_CLEAR; //force output B low /**************************************************************************************************************************/ //Setup PWM4 (DSP_DO_HV_PWM_2L(A),DSP_DO_HV_PWM_2H(B))-- caution: pinning swapped compared to LV // Set up Dead Band regs (DB) EPwm4Regs.DBFED.all = 13; EPwm4Regs.DBRED.all = 15; EPwm4Regs.AQCTLA.bit.ZRO = AQ_SET; //Counter equals zero EPwm4Regs.AQCTLA.bit.PRD = AQ_CLEAR; //Counter equals CMPA on up-count (CAU) EPwm4Regs.AQCSFRC.bit.CSFB = AQ_CLEAR; //force output B low //start the clocks again, all timers are synchronized /**************************************************************************************************************************/ CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 1; EDIS; } void initEPWM_project(void) { volatile struct EPWM_REGS *ePWM[] = {0, &EPwm1Regs, &EPwm2Regs, &EPwm3Regs, &EPwm4Regs, &EPwm7Regs, &EPwm8Regs}; EALLOW; EPwm3Regs.EPWMXLINK.bit.TBPRDLINK = 0b0011; //Link EPWM3 to EPWM4 int j = 0; for(j=1; j<=PWM_CH; j++) { (*ePWM[j]).CMPA.bit.CMPA = ((SWPERIOD+1)/2)-1 ; (*ePWM[j]).AQCTLA.bit.CAU = AQ_CLEAR; (*ePWM[j]).AQCTLA.bit.CAD = AQ_SET; (*ePWM[j]).AQCSFRC.bit.CSFB = AQ_CLEAR; //force output B low (*ePWM[j]).AQCTLA.bit.CBD = 0; (*ePWM[j]).AQCTLA.bit.ZRO = 0; (*ePWM[j]).AQCTLA.bit.PRD = 0; } /***************************************************************************************************************************************/ //Setup PWM2 (DSP_DO_LV1_PWM_1L(B),DSP_DO_LV1_PWM_1H(A)) // Set up Action Qualifier regs (AQ) EPwm2Regs.AQCTLA.bit.CAU = AQ_SET; EPwm2Regs.AQCTLA.bit.CAD = AQ_CLEAR; EPwm2Regs.AQCSFRC.bit.CSFB = AQ_CLEAR; /***************************************************************************************************************************************/ //Setup PWM7 (DSP_DO_LV2_PWM_2L(B),DSP_DO_LV2_PWM_2H(A)) EPwm7Regs.AQCTLA.bit.CAU = AQ_SET; EPwm7Regs.AQCTLA.bit.CAD = AQ_CLEAR; EPwm7Regs.AQCSFRC.bit.CSFB = AQ_CLEAR; /****************************************************************************************************************************/ EPwm4Regs.AQCTLA.bit.CAU = AQ_SET; EPwm4Regs.AQCTLA.bit.CAD = AQ_CLEAR; EPwm4Regs.AQCSFRC.bit.CSFB = AQ_CLEAR; EPwm3Regs.TBCTL.bit.PHSDIR = 1; EPwm4Regs.TBCTL.bit.PHSDIR = 1; EDIS; }
Hi Patrick,
Thank you for sharing your code. I will take a look through it and provide any feedback by tomorrow.
Best Regards,
Marlyn
Hi Patrick,
If in up & down count mode, and doing both edge control then in this case you need to have action qualifier settings as follows: CMPxU = set, CMPxD = clear, no actions on TBCTR= PRD or ZRO. For AQCTLA registers, set on CAU and clear on CAD, for AQCLTB, set on CBU and clear on CBD
The SYNCOSEL for EPWM1 is set as "TB_CTR_ZERO", which will send a sync pulse to the slave modules every time EPWM1's TBCTR counter is equal to 0. Note that when high-resolution period mode is enabled, an EPWMxSYNC pulse will introduce +/- 1 - 2 cycle jitter to the PWM (+/- 1 cycle in up-count mode and +/- 2 cycle in up-down count mode).
You should set the 'Sync Out Pulse' to be configured to software, a software synchronization pulse should be issued only once during initialization and then only when the phase or period is changed. If a software sync pulse is applied while the PWM is running, the jitter will appear on the PWM output at the time of the sync pulse.
Best Regards,
Marlyn