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.

CCS/TMS320F28335: Digital Controller Library - PI values in struct are different from my settings

Part Number: TMS320F28335
Other Parts Discussed in Thread: C2000WARE

Tool/software: Code Composer Studio

Hello,

I am trying to implement your Digital Controller Library on the F28335 following the guidelines from SPRUI31. However, no matter my settings or values for KP, KI, upper/lower saturation, the values within my PI struct seem to be different. I believe I have a misconception on how to use and understand the library and would therefore appreciate your help. I am using phaseshift control between two bridges, where the phaseshift between two PWM channels is supposed to be between 800 and 1460 within the phaseshift register. Hence I set the saturation limits to 800 and 1460. I start with Kp=10 and disable the integrator to have a purely P regulator (the Ki parameters will be adjusted in a later stage to properly control the DCDC converter). These values are defined in PI_VALUES as shown in the C code given below.

The ADC interrupt service routine works properly and the ADC readings work properly as well. Debugging the code, I can see that the PI function is also called and the DCL_PI.asm code is executed. By inspecting the values of my PI regulator, I can see that the value differ from my settings. Also, I can see that the output of the PI regulator is clamped to Umin, which is the lower saturation bound to b e 2039 as opposed to be 800 in my settings. Do you have any idea of what is happening and what I am doing wrong? I would appreciate your guidance.

#include "DSP28x_Project.h"
#include "DCL.h"


void InitialADC();
void InitePWM1();
void InitePWM2();
void InitePWM5();
void InitePWM6();
__interrupt void ADC_isr();
__interrupt void TZ_isr();

// Flash functionality
extern Uint16 RamfuncsLoadStart;
extern Uint16 RamfuncsLoadEnd;
extern Uint16 RamfuncsRunStart;

// Variables
#define DT 25 // Dead time calc: DT = Value/CLKfreq -> 25/150MHz = 166ns
Uint16 phaseshift1;

// PI controller variables
#define	PI_VALUES { 1000.0f, 0.0f, 0.0f, 1460.0f, 800.0f, 1460.0f } // Kp, Ki, i10, Sat_max, Sat_min, Sat_storage
volatile float32 rk;	// Reference value
volatile float32 yk;	// Measured ADC signal
volatile float32 uk;	// PI output
volatile PI pi1;

volatile Uint16 ConversionCount;
volatile float32 Voltage1[256];
volatile float32 Current[256];

void main(void) {

	// Initialize PLL, Watchdog, enable peripheral clocks
	InitSysCtrl();

	// Used to program to flash
    MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);

	// Initialize ePWM and TZ
	InitEPwmGpio();
	InitTzGpio();

	// Initialize PIE control registers
	InitPieCtrl();

	IER = 0x0000;
	IFR = 0x0000;

	// Initialize PIE vector table to default ISR
	InitPieVectTable();

	// Remap the ISR function to the PIE vector table
	EALLOW;
	PieVectTable.ADCINT = &ADC_isr;
	PieVectTable.EPWM1_TZINT = &TZ_isr;
	EDIS;

	// First step of setting up the ADC sampling rate
	EALLOW;
	SysCtrlRegs.HISPCP.all = 3; // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3)   = 25.0 MHz
	EDIS;

	// Synchronization already done in the InitSysCtrl() function --> Ask the TI support to verify
	/*
	EALLOW;
	SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;	// Enable time base clock synchronization with SYSCLKOUT from DSP
	EDIS;
	*/

    InitFlash();

	ConversionCount = 0;				// Initialize the count
	uk = 0;
	rk = 2050;	// Approx. 5A load voltage
	PI pi1 = PI_VALUES;					// Initialize the PI controller

	// Initialize the ADC
	InitAdc();
	phaseshift1 = 5;	// Compensating a ca. 15ns delay between ePWM1 and ePWM6
	InitialADC();		// Set up the ADC for voltage and current measurement

	EALLOW;
	SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;
	EDIS;

	InitePWM1();
	InitePWM2();
	InitePWM5();
	InitePWM6();

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

	/*
	 * Set GPIO4 as output to measure execution time within ISR
	 */
	EALLOW;
	// Set GPIO10 as a GPIO - already done in InitGpio()
	GpioCtrlRegs.GPAMUX1.bit.GPIO4 = 0;
	// Set GPIO10 as an output
	GpioCtrlRegs.GPADIR.bit.GPIO4 = 1;
	EDIS;
	
	PieCtrlRegs.PIEIER1.bit.INTx6 = 1;	// Group 1, bit 6 for ADC
	PieCtrlRegs.PIEIER2.bit.INTx1 = 1;	// Group 2, bit 1 for ePWM1_TZ
	IER |= M_INT1;						// Sets the interrupt enable bit of group 1
	IER |= M_INT2;						// Sets the interrupt enable bit of group 3
	EINT;								// Enable global interrupts INTM

	for(;;)
	{

	}
}

__interrupt void ADC_isr(void)
{
	GpioDataRegs.GPASET.bit.GPIO4 = 1;

	Voltage1[ConversionCount] = AdcRegs.ADCRESULT0 >>4;	// Read value from ADCINA5
	Current[ConversionCount] = AdcRegs.ADCRESULT1 >>4;	// Read value from ADCINB5

	if(Current[ConversionCount]>2800)
	{
		// Overcurrent - Stop DAB converter
		EPwm1Regs.CMPA.half.CMPA = 0;
		EPwm2Regs.CMPA.half.CMPA = 1500;
		EPwm6Regs.CMPA.half.CMPA = 0;
		EPwm5Regs.CMPA.half.CMPA = 1500;
	}

	  /*
	   * Calculating the phaseshift between two bridges using PI control
	   */
	yk = Current[ConversionCount];
	uk = DCL_runPI(&pi1,rk,yk);

		EPwm5Regs.TBPHS.half.TBPHS = uk;	// ePWM5 and ePWM6 will have phaseshift
		EPwm6Regs.TBPHS.half.TBPHS = phaseshift1;	// Compensate ca. 16ns delay of ePWM6

	      // If 256 conversions have been logged, start over
	      if(ConversionCount == 255)
	      {
	         ConversionCount = 0;
	      }
	      else
	      {
	          ConversionCount++;
	      }

		// Re-initialize for next ADC
		AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;	// Reset SEQ1
		AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;	// Clears the interrupt flag bit
		PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;	// Acknowledge interrupt to PIE

		GpioDataRegs.GPACLEAR.bit.GPIO4 = 1;
}

__interrupt void TZ_isr(void)
{

	// Clear interrupt flag
	EALLOW;
	EPwm1Regs.TZCLR.bit.OST = 1;
	EPwm2Regs.TZCLR.bit.OST = 1;
	EPwm5Regs.TZCLR.bit.OST = 1;
	EPwm6Regs.TZCLR.bit.OST = 1;
	EPwm1Regs.TZCLR.bit.INT = 1;
	EDIS;
	PieCtrlRegs.PIEACK.all = PIEACK_GROUP2;	// Acknowledge interrupt to PIE
}

void InitialADC(void)
{
	// Configure ADC
	AdcRegs.ADCTRL3.bit.ADCCLKPS = 1;	// Set the ADC sampling rate: 25MHz/(2*1+1) = 8.3MHz
	AdcRegs.ADCTRL1.bit.ACQ_PS = 0;		//
	AdcRegs.ADCTRL1.bit.SEQ_CASC = 0;	// Cascaded mode
	AdcRegs.ADCTRL1.bit.CONT_RUN = 0;	// Start-stop mode
	AdcRegs.ADCTRL3.bit.SMODE_SEL = 1;	// Simultaneous sampling mode
	AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1;	// ePWM starts SOCA trigger
	AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 0;	// One conversion
	AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 5;	// ADCINA5 and ADCINB5
	AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1;	// Interrupt request enabled
}

void InitePWM1(void)
{
	// Enable SOCA for ADC measurements
    EPwm1Regs.ETSEL.bit.SOCAEN = 1;            	// Enable SOCA
    EPwm1Regs.ETSEL.bit.SOCASEL = 4;            // Generate SOCA pulse at 50% duty cycle
    EPwm1Regs.ETPS.bit.SOCAPRD = 1;             // Generate pulse on 1st event

	EPwm1Regs.TBPRD = 1499;	// Set the PWM period time
	EPwm1Regs.CMPA.half.CMPA = (1499+1)/2;
	EPwm1Regs.TBPHS.half.TBPHS = 0; // Set Phase register to zero
	EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;	// Up count mode
	EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE; // Master module
	EPwm1Regs.TBCTL.bit.PRDLD = TB_SHADOW;
	EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // Sync down-stream module
	EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0;	// Set time base clock to SYSCLKOUT
	EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
	EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
	EPwm1Regs.CMPCTL.bit.LOADAMODE = 0;	// Loads on either CTR=0 or CTR=PRD
	EPwm1Regs.CMPCTL.bit.LOADBMODE = 0;	// Loads on either CTR=0 or CTR=PRD
	EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET;	// Sets pin when CTR=PRD
	EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;	// Clears pin when CTR=COMPA

	EALLOW;
	EPwm1Regs.TZSEL.bit.OSHT3 = 1;		// Enable TZ3
	EPwm1Regs.TZCTL.bit.TZA = 2;		// Clear ePWM1A on TZ event
	EPwm1Regs.TZCTL.bit.TZB = 2;		// Clear ePWM1B on TZ event
	EPwm1Regs.TZEINT.bit.OST = 1;		// Enable interrupt generation
	EDIS;


	EPwm1Regs.DBCTL.bit.IN_MODE = 0;	// ePWMxA source for falling and rising edge
	EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	// DB full enable
	EPwm1Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		// Active high complementary

	EPwm1Regs.DBRED = DT;
	EPwm1Regs.DBFED = DT;
}

void InitePWM2(void)
{
	EPwm2Regs.TBPRD = 1499;	// Set the PWM period time
	EPwm2Regs.CMPA.half.CMPA = (1499+1)/2;
	EPwm2Regs.TBPHS.half.TBPHS = 0; // Set Phase register to zero
	EPwm2Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;	// Up count mode
	EPwm2Regs.TBCTL.bit.PHSEN = TB_ENABLE; // Master module
	EPwm2Regs.TBCTL.bit.PRDLD = TB_SHADOW;
	EPwm2Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // Sync down-stream module
	EPwm2Regs.TBCTL.bit.HSPCLKDIV = 0;	// Set time base clock to SYSCLKOUT
	EPwm2Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
	EPwm2Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
	EPwm2Regs.CMPCTL.bit.LOADAMODE = 0;	// Loads on either CTR=0 or CTR=PRD
	EPwm2Regs.CMPCTL.bit.LOADBMODE = 0;	// Loads on either CTR=0 or CTR=PRD
	EPwm2Regs.AQCTLA.bit.ZRO = AQ_CLEAR;	// Clears pin when CTR=PRD
	EPwm2Regs.AQCTLA.bit.CAU = AQ_SET;	// Sets pin when CTR=COMPA

	EALLOW;
	EPwm2Regs.TZSEL.bit.OSHT3 = 1;		// Enable TZ3
	EPwm2Regs.TZCTL.bit.TZA = 2;		// Clear ePWM2A on TZ event
	EPwm2Regs.TZCTL.bit.TZB = 2;		// Clear ePWM2B on TZ event
	EDIS;

	EPwm2Regs.DBCTL.bit.IN_MODE = 0;	// ePWMxA source for falling and rising edge
	EPwm2Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	// DB full enable
	EPwm2Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		// Active high complementary

	EPwm2Regs.DBRED = DT;
	EPwm2Regs.DBFED = DT;
}

void InitePWM5(void)
{
	EPwm5Regs.TBPRD = 1499;	// Set the PWM period time
	EPwm5Regs.CMPA.half.CMPA = (1499+1)/2;
	EPwm5Regs.TBPHS.half.TBPHS = 0; // Set Phase register to zero
	EPwm5Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;	// Up count mode
	EPwm5Regs.TBCTL.bit.PHSEN = TB_ENABLE; // Slave module
	EPwm5Regs.TBCTL.bit.PRDLD = TB_SHADOW;
	EPwm5Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // Sync down-stream module
	EPwm5Regs.TBCTL.bit.HSPCLKDIV = 0;	// Set time base clock to SYSCLKOUT
	EPwm5Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
	EPwm5Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
	EPwm5Regs.CMPCTL.bit.LOADAMODE = 0;	// Loads on either CTR=0 or CTR=PRD
	EPwm5Regs.CMPCTL.bit.LOADBMODE = 0;	// Loads on either CTR=0 or CTR=PRD
	EPwm5Regs.AQCTLA.bit.ZRO = AQ_CLEAR;	// Clears pin when CTR=PRD
	EPwm5Regs.AQCTLA.bit.CAU = AQ_SET;	// Sets pin when CTR=COMPA


	EALLOW;
	EPwm5Regs.TZSEL.bit.OSHT3 = 1;		// Enable TZ3
	EPwm5Regs.TZCTL.bit.TZA = 2;		// Clear ePWM5A on TZ event
	EPwm5Regs.TZCTL.bit.TZB = 2;		// Clear ePWM5B on TZ event
	EDIS;


	EPwm5Regs.DBCTL.bit.IN_MODE = 0;	// ePWMxA source for falling and rising edge
	EPwm5Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	// DB full enable
	EPwm5Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		// Active high complementary

	EPwm5Regs.DBRED = DT;
	EPwm5Regs.DBFED = DT;
}

void InitePWM6(void)
{
	EPwm6Regs.TBPRD = 1499;	// Set the PWM period time
	EPwm6Regs.CMPA.half.CMPA = (1499+1)/2;
	EPwm6Regs.TBPHS.half.TBPHS = 0; // Set Phase register to zero
	EPwm6Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;	// Up count mode
	EPwm6Regs.TBCTL.bit.PHSEN = TB_ENABLE; // Slave module
	EPwm6Regs.TBCTL.bit.PRDLD = TB_SHADOW;
	EPwm6Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // Sync down-stream module
	EPwm6Regs.TBCTL.bit.HSPCLKDIV = 0;	// Set time base clock to SYSCLKOUT
	EPwm6Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
	EPwm6Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
	EPwm6Regs.CMPCTL.bit.LOADAMODE = 0;	// Loads on either CTR=0 or CTR=PRD
	EPwm6Regs.CMPCTL.bit.LOADBMODE = 0;	// Loads on either CTR=0 or CTR=PRD
	EPwm6Regs.AQCTLA.bit.ZRO = AQ_SET;	// Sets pin when CTR=PRD
	EPwm6Regs.AQCTLA.bit.CAU = AQ_CLEAR;	// Clears pin when CTR=COMPA


	EALLOW;
	EPwm6Regs.TZSEL.bit.OSHT3 = 1;		// Enable TZ3
	EPwm6Regs.TZCTL.bit.TZA = 2;		// Clear ePWM6A on TZ event
	EPwm6Regs.TZCTL.bit.TZB = 2;		// Clear ePWM6B on TZ event
	EDIS;


	EPwm6Regs.DBCTL.bit.IN_MODE = 0;	// ePWMxA source for falling and rising edge
	EPwm6Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	// DB full enable
	EPwm6Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		// Active low complementary

	EPwm6Regs.DBRED = DT;
	EPwm6Regs.DBFED = DT;
}

  • Alexander,

    At the moment you are declaring pi1 in two places.  Can you start by commenting out line 77, and initialising the structure at the declaration by changing line 27 to read:

    volatile PI pi1 = PI_VALUES;

    Then, can you set a break-point on the InitAdc() function (line 80) and check the PI structure is initialised correctly at that point?  If so, try setting another break-point on the DCL_runPI() line and re-check.  I see nothing in your code which touches the controller gains or limits so it they're initialised correctly they should not change.

    Most of your problems are to do with numeric formatting.  All the DCL functions expect floating-point inputs and deliver floating-point outputs.  You have correctly declared rk, yk, and uk as floating-point, but you are assigning rk and yk to fixed-point numbers.  You need to convert these to floating-point format before passing them to the DCL_runPI() function. 

    rk = 2050.0f;

    yk = (float) Current[ConversionCount];

    You are also assigning the 32-bit floating-point result uk to a 16-bit register:

    EPwm5Regs.TBPHS.half.TBPHS = uk; 

    This needs to be converted into 16-bit unsigned integer format before writing it to the register.  To do this, you can declare a variable (eg. x) as long and then assign it to uk:

    x = (long) uk;

    ...then shift as required and assign part to a 16-bit unsigned int (z).  For example:

    z = (unsigned int) (x >> 4);

    You can then write it to the register, but step through it carefully to make sure the results are as expected.

    Regards,

    Richard

  • Thank you Richard, your answer helped a lot.
  • Sorry to revive this thread again, but the DCL is not completely clear to me yet. I am still using the PI controller. An updated version of my code will follow to this text. My issue is that I am having a saturated output, which stays at that value irrespective of the sign of the error (i.e. input of my controller is positive or negative). Attached are two figures (one where the measured voltage is lower than the reference voltage and one where the measured voltage is greater than the reference voltage). In both cases, the output stay the same.

    I cannot explain that situation entirely myself, and I am in fact wondering about the i10 and i6 values within my PI regulator. The block diagram of the discrete PI compensator from your technical reference is also attached to this message.

    1)

    i10 seems to be the accumulated from the previous sampling, so:

    i10 = v4[n-1] with n as the current sample point.

    Hence the value of i10 determines the maximum value to which I can integrate / sum up to. Since it is a floating point number, I would set i10 as high as possible, lets say 20000.0f

    Am I correct in my understanding?

    2)

    i6 is the feedback of the (anti) saturation mechanism. If V5 = =u(k), the difference is zero and hence i6 is zero as well. In equation 14, the feedback from the saturation vanishes and the PI regulator acts normally. Contrary, if u(k) saturates, i6 is fed back to the integrator as can be seen in equation 14.

    If my understanding to (1) and (2) is correct, then I am surprised that in the expression windows of CCS, the value for i6 is zero and stays at zero irrespective of my converter operating point. In other words, since I am saturating my output u(k), how can i6 be 0? I expect it to be different than 0 to counteract the windup of my integrator.

    Block diagram of discrete pi regulator:

    Positive error: e(k) = r(k) - y(k)

    Negative error: e(k) = r(k) - y(k)

    My c code:

    #include "DSP28x_Project.h"
    #include "DCL.h"
    
    
    void InitialADC();
    void InitePWM1();
    void InitePWM2();
    void InitePWM5();
    void InitePWM6();
    __interrupt void ADC_isr();
    __interrupt void TZ_isr();
    
    // Flash functionality
    extern Uint16 RamfuncsLoadStart;
    extern Uint16 RamfuncsLoadEnd;
    extern Uint16 RamfuncsRunStart;
    
    // Variables
    #define DT 25 // Dead time calc: DT = Value/CLKfreq -> 25/150MHz = 166ns
    
    // PI controller variables
    #define	PI_VALUES { 0.3f, 0.001f, 20000.0f, 1460.0f, 20.0f, 20000.0f} // Kp, Ki, i10, Sat_max, Sat_min, Sat_storage
    Uint16 phaseshift;
    Uint16 phaseshift1;
    volatile int32 dummy;
    volatile float32 rk;	// Reference value
    volatile float32 yk;	// Measured ADC signal
    volatile float32 uk;	// PI output
    volatile PI pi1 = PI_VALUES;
    
    volatile Uint16 ConversionCount;
    volatile float32 Voltage1[256];
    volatile float32 Current[256];
    
    void main(void) {
    
    	// Initialize PLL, Watchdog, enable peripheral clocks
    	InitSysCtrl();
    
    	// Used to program to flash
        MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);
    
    	// Initialize ePWM and TZ
    	InitEPwmGpio();
    	InitTzGpio();
    
    	// Initialize PIE control registers
    	InitPieCtrl();
    
    	IER = 0x0000;
    	IFR = 0x0000;
    
    	// Initialize PIE vector table to default ISR
    	InitPieVectTable();
    
    	// Remap the ISR function to the PIE vector table
    	EALLOW;
    	PieVectTable.ADCINT = &ADC_isr;
    	PieVectTable.EPWM1_TZINT = &TZ_isr;
    	EDIS;
    
    	// First step of setting up the ADC sampling rate
    	EALLOW;
    	SysCtrlRegs.HISPCP.all = 3; // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3)   = 25.0 MHz
    	EDIS;
    
    	// Synchronization already done in the InitSysCtrl() function --> Ask the TI support to verify
    	/*
    	EALLOW;
    	SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;	// Enable time base clock synchronization with SYSCLKOUT from DSP
    	EDIS;
    	*/
    
        InitFlash();
    
    	ConversionCount = 0;				// Initialize the count
    	dummy = 1460;
    	uk = 1460.0f;
    //	rk = 2050.0f;	// Approx. 5A load current
    	rk = 300.0f;	// Approx. 10V load voltage
    	//PI pi1 = PI_VALUES;
    
    	// Initialize the ADC
    	InitAdc();
    	phaseshift = 0;
    	phaseshift1 = 4;	// Compensating a ca. 15ns delay between ePWM1 and ePWM6
    	InitialADC();		// Set up the ADC for voltage and current measurement
    
    	EALLOW;
    	SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;
    	EDIS;
    
    	InitePWM1();
    	InitePWM2();
    	InitePWM5();
    	InitePWM6();
    
    	EALLOW;
    	SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;
    	EDIS;
    
    	/*
    	 * Set GPIO4 as output to measure execution time within ISR
    	 */
    	EALLOW;
    	// Set GPIO10 as a GPIO - already done in InitGpio()
    	GpioCtrlRegs.GPAMUX1.bit.GPIO4 = 0;
    	// Set GPIO10 as an output
    	GpioCtrlRegs.GPADIR.bit.GPIO4 = 1;
    	EDIS;
    	
    	PieCtrlRegs.PIEIER1.bit.INTx6 = 1;	// Group 1, bit 6 for ADC
    	PieCtrlRegs.PIEIER2.bit.INTx1 = 1;	// Group 2, bit 1 for ePWM1_TZ
    	IER |= M_INT1;						// Sets the interrupt enable bit of group 1
    	IER |= M_INT2;						// Sets the interrupt enable bit of group 3
    	EINT;								// Enable global interrupts INTM
    
    	for(;;)
    	{
    
    	}
    }
    
    __interrupt void ADC_isr(void)
    {
    	Voltage1[ConversionCount] = AdcRegs.ADCRESULT0 >>4;	// Read value from ADCINA5
    	Current[ConversionCount] = AdcRegs.ADCRESULT1 >>4;	// Read value from ADCINB5
    
    	if(Current[ConversionCount]>2800)
    	{
    		// Overcurrent - Stop DAB converter
    		EPwm1Regs.CMPA.half.CMPA = 0;
    		EPwm2Regs.CMPA.half.CMPA = 1500;
    		EPwm6Regs.CMPA.half.CMPA = 0;
    		EPwm5Regs.CMPA.half.CMPA = 1500;
    	}
    
    	  /*
    	   * Calculating the phaseshift between two bridges using PI control
    	   */
    	yk = (float)Voltage1[ConversionCount];
    	// Regulate when minimum output voltage is reached
    	if(yk>=200)
    	{
    	GpioDataRegs.GPASET.bit.GPIO4 = 1;	// For testing purposes
    	uk = DCL_runPI(&pi1,rk,yk);
    	GpioDataRegs.GPACLEAR.bit.GPIO4 = 1;	// For testing purposes
    	dummy = (long)uk;
    	}
    
    
    		EPwm5Regs.TBPHS.half.TBPHS = dummy;	// ePWM5 and ePWM6 will have phaseshift
    		EPwm6Regs.TBPHS.half.TBPHS = phaseshift1;	// Compensate ca. 16ns delay of ePWM6
    
    	      // If 256 conversions have been logged, start over
    	      if(ConversionCount == 255)
    	      {
    	         ConversionCount = 0;
    	      }
    	      else
    	      {
    	          ConversionCount++;
    	      }
    
    		// Re-initialize for next ADC
    		AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;	// Reset SEQ1
    		AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;	// Clears the interrupt flag bit
    		PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;	// Acknowledge interrupt to PIE
    }
    
    __interrupt void TZ_isr(void)
    {
    
    	// Clear interrupt flag
    	EALLOW;
    	EPwm1Regs.TZCLR.bit.OST = 1;
    	EPwm2Regs.TZCLR.bit.OST = 1;
    	EPwm5Regs.TZCLR.bit.OST = 1;
    	EPwm6Regs.TZCLR.bit.OST = 1;
    	EPwm1Regs.TZCLR.bit.INT = 1;
    	EDIS;
    	PieCtrlRegs.PIEACK.all = PIEACK_GROUP2;	// Acknowledge interrupt to PIE
    }
    
    void InitialADC(void)
    {
    	// Configure ADC
    	AdcRegs.ADCTRL3.bit.ADCCLKPS = 1;	// Set the ADC sampling rate: 25MHz/(2*1+1) = 8.3MHz
    	AdcRegs.ADCTRL1.bit.ACQ_PS = 0;		//
    	AdcRegs.ADCTRL1.bit.SEQ_CASC = 0;	// Cascaded mode
    	AdcRegs.ADCTRL1.bit.CONT_RUN = 0;	// Start-stop mode
    	AdcRegs.ADCTRL3.bit.SMODE_SEL = 1;	// Simultaneous sampling mode
    	AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1;	// ePWM starts SOCA trigger
    	AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 0;	// One conversion
    	AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 5;	// ADCINA5 and ADCINB5
    	AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1;	// Interrupt request enabled
    }
    
    void InitePWM1(void)
    {
    	// Enable SOCA for ADC measurements
        EPwm1Regs.ETSEL.bit.SOCAEN = 1;            	// Enable SOCA
        EPwm1Regs.ETSEL.bit.SOCASEL = 4;            // Generate SOCA pulse at 50% duty cycle
        EPwm1Regs.ETPS.bit.SOCAPRD = 1;             // Generate pulse on 1st event
    
    	EPwm1Regs.TBPRD = 1499;	// Set the PWM period time
    	EPwm1Regs.CMPA.half.CMPA = (1499+1)/2;
    	EPwm1Regs.TBPHS.half.TBPHS = 0; // Set Phase register to zero
    	EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;	// Up count mode
    	EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE; // Master module
    	EPwm1Regs.TBCTL.bit.PRDLD = TB_SHADOW;
    	EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // Sync down-stream module
    	EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0;	// Set time base clock to SYSCLKOUT
    	EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
    	EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
    	EPwm1Regs.CMPCTL.bit.LOADAMODE = 0;	// Loads on either CTR=0 or CTR=PRD
    	EPwm1Regs.CMPCTL.bit.LOADBMODE = 0;	// Loads on either CTR=0 or CTR=PRD
    	EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET;	// Sets pin when CTR=PRD
    	EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;	// Clears pin when CTR=COMPA
    
    	EALLOW;
    	EPwm1Regs.TZSEL.bit.OSHT3 = 1;		// Enable TZ3
    	EPwm1Regs.TZCTL.bit.TZA = 2;		// Clear ePWM1A on TZ event
    	EPwm1Regs.TZCTL.bit.TZB = 2;		// Clear ePWM1B on TZ event
    	EPwm1Regs.TZEINT.bit.OST = 1;		// Enable interrupt generation
    	EDIS;
    
    
    	EPwm1Regs.DBCTL.bit.IN_MODE = 0;	// ePWMxA source for falling and rising edge
    	EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	// DB full enable
    	EPwm1Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		// Active high complementary
    
    	EPwm1Regs.DBRED = DT;
    	EPwm1Regs.DBFED = DT;
    }
    
    void InitePWM2(void)
    {
    	EPwm2Regs.TBPRD = 1499;	// Set the PWM period time
    	EPwm2Regs.CMPA.half.CMPA = (1499+1)/2;
    	EPwm2Regs.TBPHS.half.TBPHS = 0; // Set Phase register to zero
    	EPwm2Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;	// Up count mode
    	EPwm2Regs.TBCTL.bit.PHSEN = TB_ENABLE; // Master module
    	EPwm2Regs.TBCTL.bit.PRDLD = TB_SHADOW;
    	EPwm2Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // Sync down-stream module
    	EPwm2Regs.TBCTL.bit.HSPCLKDIV = 0;	// Set time base clock to SYSCLKOUT
    	EPwm2Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
    	EPwm2Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
    	EPwm2Regs.CMPCTL.bit.LOADAMODE = 0;	// Loads on either CTR=0 or CTR=PRD
    	EPwm2Regs.CMPCTL.bit.LOADBMODE = 0;	// Loads on either CTR=0 or CTR=PRD
    	EPwm2Regs.AQCTLA.bit.ZRO = AQ_CLEAR;	// Clears pin when CTR=PRD
    	EPwm2Regs.AQCTLA.bit.CAU = AQ_SET;	// Sets pin when CTR=COMPA
    
    	EALLOW;
    	EPwm2Regs.TZSEL.bit.OSHT3 = 1;		// Enable TZ3
    	EPwm2Regs.TZCTL.bit.TZA = 2;		// Clear ePWM2A on TZ event
    	EPwm2Regs.TZCTL.bit.TZB = 2;		// Clear ePWM2B on TZ event
    	EDIS;
    
    	EPwm2Regs.DBCTL.bit.IN_MODE = 0;	// ePWMxA source for falling and rising edge
    	EPwm2Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	// DB full enable
    	EPwm2Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		// Active high complementary
    
    	EPwm2Regs.DBRED = DT;
    	EPwm2Regs.DBFED = DT;
    }
    
    void InitePWM5(void)
    {
    	EPwm5Regs.TBPRD = 1499;	// Set the PWM period time
    	EPwm5Regs.CMPA.half.CMPA = (1499+1)/2;
    	EPwm5Regs.TBPHS.half.TBPHS = 0; // Set Phase register to zero
    	EPwm5Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;	// Up count mode
    	EPwm5Regs.TBCTL.bit.PHSEN = TB_ENABLE; // Slave module
    	EPwm5Regs.TBCTL.bit.PRDLD = TB_SHADOW;
    	EPwm5Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // Sync down-stream module
    	EPwm5Regs.TBCTL.bit.HSPCLKDIV = 0;	// Set time base clock to SYSCLKOUT
    	EPwm5Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
    	EPwm5Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
    	EPwm5Regs.CMPCTL.bit.LOADAMODE = 0;	// Loads on either CTR=0 or CTR=PRD
    	EPwm5Regs.CMPCTL.bit.LOADBMODE = 0;	// Loads on either CTR=0 or CTR=PRD
    	EPwm5Regs.AQCTLA.bit.ZRO = AQ_CLEAR;	// Clears pin when CTR=PRD
    	EPwm5Regs.AQCTLA.bit.CAU = AQ_SET;	// Sets pin when CTR=COMPA
    
    
    	EALLOW;
    	EPwm5Regs.TZSEL.bit.OSHT3 = 1;		// Enable TZ3
    	EPwm5Regs.TZCTL.bit.TZA = 2;		// Clear ePWM5A on TZ event
    	EPwm5Regs.TZCTL.bit.TZB = 2;		// Clear ePWM5B on TZ event
    	EDIS;
    
    
    	EPwm5Regs.DBCTL.bit.IN_MODE = 0;	// ePWMxA source for falling and rising edge
    	EPwm5Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	// DB full enable
    	EPwm5Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		// Active high complementary
    
    	EPwm5Regs.DBRED = DT;
    	EPwm5Regs.DBFED = DT;
    }
    
    void InitePWM6(void)
    {
    	EPwm6Regs.TBPRD = 1499;	// Set the PWM period time
    	EPwm6Regs.CMPA.half.CMPA = (1499+1)/2;
    	EPwm6Regs.TBPHS.half.TBPHS = 0; // Set Phase register to zero
    	EPwm6Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;	// Up count mode
    	EPwm6Regs.TBCTL.bit.PHSEN = TB_ENABLE; // Slave module
    	EPwm6Regs.TBCTL.bit.PRDLD = TB_SHADOW;
    	EPwm6Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // Sync down-stream module
    	EPwm6Regs.TBCTL.bit.HSPCLKDIV = 0;	// Set time base clock to SYSCLKOUT
    	EPwm6Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
    	EPwm6Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
    	EPwm6Regs.CMPCTL.bit.LOADAMODE = 0;	// Loads on either CTR=0 or CTR=PRD
    	EPwm6Regs.CMPCTL.bit.LOADBMODE = 0;	// Loads on either CTR=0 or CTR=PRD
    	EPwm6Regs.AQCTLA.bit.ZRO = AQ_SET;	// Sets pin when CTR=PRD
    	EPwm6Regs.AQCTLA.bit.CAU = AQ_CLEAR;	// Clears pin when CTR=COMPA
    
    
    	EALLOW;
    	EPwm6Regs.TZSEL.bit.OSHT3 = 1;		// Enable TZ3
    	EPwm6Regs.TZCTL.bit.TZA = 2;		// Clear ePWM6A on TZ event
    	EPwm6Regs.TZCTL.bit.TZB = 2;		// Clear ePWM6B on TZ event
    	EDIS;
    
    
    	EPwm6Regs.DBCTL.bit.IN_MODE = 0;	// ePWMxA source for falling and rising edge
    	EPwm6Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	// DB full enable
    	EPwm6Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		// Active low complementary
    
    	EPwm6Regs.DBRED = DT;
    	EPwm6Regs.DBFED = DT;
    }
    

  • Hi guys,

    may I ask you one more time for elaborating on my two questions from the previous post, as I am still uncertain on the meaning (value sizing) of i10 and i6 within the PI regulator.

    1)

    i10 seems to be the accumulated from the previous sampling, so:

    i10 = v4[n-1] with n as the current sample point.

    Hence the value of i10 determines the maximum value to which I can integrate / sum up to. Since it is a floating point number, I would set i10 as high as possible, lets say 20000.0f

    Am I correct in my understanding?

    2)

    i6 is the feedback of the (anti) saturation mechanism. If V5 = =u(k), the difference is zero and hence i6 is zero as well. In equation 14, the feedback from the saturation vanishes and the PI regulator acts normally. Contrary, if u(k) saturates, i6 is fed back to the integrator as can be seen in equation 14.

    It is not really clear to me why i6 is constantly 0.0 even though I am saturating my PI output.

    Thank you,

  • Alexander,

    The PI controller doesn't work like that.  The variable i10 is the integrator output.  You should initialise that to 0 and then not touch it.  Saturation is handled at the output using Umax and Umin.

    The purpose of i6 is anti-windup.  It should only ever hold the value 1.0f (output not saturated) or 0.0f (output saturated).  If you look at the diagram i6 is multiplied with v3 to form v8, so it acts to enable or disable integration.

    I'm travelling right now, but post back if you're still in difficulty and I'll get to it tomorrow.

    Regards,

    Richard

  • Thank you Richard. Although the PI regulator from the DCL becomes more clear to me, I am still struggling with the actual implementation.

    I designed my PI controller according to my bode plot analysis:

    Note that the graph OL represents the open loop system. One can see that at my crossover frequency, I am having more than enough phase margin (an optimal controller is not required at this stage of the design).

    My PI regulator in MATLAB is set as:

    %% PI Controller
    Kp = 1.5;
    Ki = 300;
    Ka = Kp;
    Kb = Ki/Ka;
    num_PI = [Ka Ka*Kb];
    den_PI = [1 0];
    G_PI = tf(num_PI,den_PI)
    G_PIdiscr = c2d(G_PI,Tsw,'tustin')

    Without copying the whole C code (again), this is how the PI regulator is thus implemented within CCS:

    #define	PI_VALUES { 1.5f, 300.0f, 0.0f, 1125.0f, 900.0f, 1.0f} // Kp, Ki, i10, Sat_max, Sat_min, Sat_storage

    And the PI regulator inside the ISR:

    __interrupt void ADC_isr(void)
    {
    	Voltage1[ConversionCount] = AdcRegs.ADCRESULT0 >>4;	// Read value from ADCINA5
    	Current[ConversionCount] = AdcRegs.ADCRESULT1 >>4;	// Read value from ADCINB5
    
    	  /*
    	   * Calculating the phaseshift between two bridges using PI control
    	   */
    	yk = (float)Current[ConversionCount];
    	//Regulate when minimum output voltage is reached
    	if(Voltage1[ConversionCount]>=200)
    	{
    	uk = (DCL_runPI(&pi1,rk,yk));	// PI regulator
    	dummy = (long)uk;
    	}
    
    
    		EPwm5Regs.TBPHS.half.TBPHS = dummy;	// ePWM5 and ePWM6 will have phaseshift
    		EPwm6Regs.TBPHS.half.TBPHS = phaseshift1;	// Compensate ca. 16ns delay of ePWM6
    
    	      // If 256 conversions have been logged, start over
    	      if(ConversionCount == 255)
    	      {
    	         ConversionCount = 0;
    	      }
    	      else
    	      {
    	          ConversionCount++;
    	      }
    
    		// Re-initialize for next ADC
    		AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;	// Reset SEQ1
    		AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;	// Clears the interrupt flag bit
    		PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;	// Acknowledge interrupt to PIE
    }

    With these values, the integrator output (i10) becomes too large such that smaller values for Ki are necessary. 

    So I am wondering which values Kp and Ki are actually supposed to be used for the PI regulator. In fact, there is also a scaling from my output current to the ADC voltage, i.e. at 100A, the ADC measures 3V. So there is a scaling of 3V/100A=0.03. Is that scaling supposed to be included in the Kp and Ki parameters of the DCL function? If so, the outcome of the PI must be scaled back. But in that case, I ignoring the saturation limits again.

    In summary, I am challenged by translating the theoretical results into the actual microcontroller and make it work. Any ideas on how I could further debug/solve this issue? I would like to avoid the "try and error" approach.

    Thank you.

  • Hi Alexander,

    I think the PI implementation you're modeling in Matlab is not quite right.  If you look at the diagram of the DCL controller in your first post, the proportional gain (Kp) appears in series with the controller.  This would be termed a "series" or "ideal" PI controller.  Your Matlab script looks like a "parallel" type, in which the proportional gain is in the parallel path.  In Matlab, the series controller would be:

    num_PI = [Kp Kp*Ki];

    This will make a difference to the results.  If you want to use a parallel implementation you will need v2.0 of the DCL, which you can find in C2000Ware:

    http://www.ti.com/tool/c2000ware

    When you apply the Tustin transformation, a scaling factor is applied to the result to match the gains before and after transformation, so if you're checking the Matlab results with a hand computation you need to remember that factor.

    You are correct that there is scaling to be applied to account for the ADC and PWM gain change.  I have attached a diagram which shows how this might be accomplished in a typical voltage control loop.

    Your C code looks fine.  I think all you need to change is the Kp & Ki gains in the controller initialization.

    I hope this moves you forward.  Let me know if I can help further.

    Regards,

    Richard

    C2000 Digital Power Control Workshop - v3-2 - scaling.pdf

  • Hey Richard,

    Thank you for your comment on the PI regulator, I changed to the series version of the PI regulator. I believe that one of my big issues is with the C function DCL_runPI as I am not familiar with the assembly implementation in it and hence on how the PI algorithm is executed. To me the assembly code seems like a step by step execution from equation (14) in the SPRUI31 documentation.

    The analogue PI regulator is given as

    G_PI(s) = Y(s)/E(s) = (Kp*s+Kp*Ki)/s

    for which I have determined my Kp and Ki gains in Matlab.

    When I manually discretize the PI controller, I will have after some mathematic algebra the following transfer function:

    G_PI(z) = Y(z)/E(z) = (2*Kp(1-z^-1)+TsKpKi(1+z^-1)) / (2*(1-z^-1))

    Reordering the terms, I will end up with the linear difference equation:

    y[n] = y[n-1]+(-Kp+Ts/2KpKi)e[n-1]+(Kp+Ts/2KpKi)e[n]

    Now, doing the math gives us:

    (-Kp+Ts/2KpKi) = -1.497

    and

    (Kp+Ts/2KpKi) = 1.502

    These results match with Matlab when I discretize my PI regulator using the c2d function. Not sure, though, that these are the values I actually want/need in my code.

    I am now not having Kp and Ki anymore since I have derived my LDE. Contrary, the DCL_runPI function asks for Kp and Ki, so I am getting confused which values I exactly need to use for the DCL_runPI function? 

    Thank you

  • Hi Alexander,

    It's getting confusing because we now have two different Kp & Ki variables appearing in the formulae.

    Your equations are correct, as are the LDE coefficients. Let's call these c1 = 1.502 and c0 = -1.497.

    The missing piece is the series digital PI as a difference equation. Once you have that, you can equate coefficients with the LDE you found, and work out the digital Kp & Ki gains. However we're going to start tripping over notation, so lets call the digital PI gains Vp and Vi, just for now.

    If you look at the DCL series PI controller structure and derive an LDE for it, you'll find (in your notation):

    y[n] = y[n-1] + (Vp + Vi)e[n] - Vp e[n-1]

    Now we can equate coefficients with the LDE you found:

    Kp+Ts/2KpKi = Vp + Vi = c1

    -Kp+Ts/2KpKi = -Vp = c0

    Now it's just a matter of inserting c1 & c0, and calculating Vp & Vi:

    Vp = 1.497

    Vi = 0.005

    These are the equivalent DCL PI controller gains. There are several steps, but I hope this makes some kind of sense. Let me know and I'll be happy to go over it again.

    Regards,

    Richard
  • Hi Richard,
    thank you very very much for your patience and your explanations which helped me a lot. In fact, apart from my misunderstanding on how to derive the Vp and Vi values for the DCL PI controller, I was also facing some issues in the implementation of such inside the ISR. In particular having proper scaling and saturation limits caused my controller to run quite unstable.
    With these things solved now, my controller runs pretty satisfactory for my purpose.

    Once again, thank you very much.

    Cheers,
  • Hi Alexander,

    You're welcome, and thank you for the post.

    Glad to know everything worked out.

    Regards,

    Richard