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.
Dear TI members,
I am attaching a code of my simulation for PID. I need your help to just explain parts of the code:
Firstly: If rk =0.25 is set, then yk was achieved with the help of 12 bit ADC values (0-4095) and from the code line 342:
yk = ((float) AdcRegs.ADCRESULT0 - 2048.0f) / 2047.0f;
yk could be between 1 and -1 .........if (float) AdcRegs.ADCRESULT0 between (4095 and 0).
Now, next line 345 : uk = DCL_runPID_C4(&pid1, rk, yk, lk);......for running the PID function. which gives Control effort value u(k).
Question1 : Question is what could be the u(k) value range in numbers??? Please explain this line 345.
Line 348: clampactive = DCL_runClamp_C1(&uk, upperlim, lowerlim); defining the upper and lower limit saturation.
Question 2: Line 350: lk = (clampactive == 0U) ? 1.0f : 0.0f;.... There is no compulsion for choosing either 1 or 0 for clampactive. and can we choose any of them????
For line 353-354:
Duty = (uk / 2.0f + 0.5f) * (float) EPwm1Regs.TBPRD;
EPwm1Regs.CMPA.half.CMPA = (Uint16) Duty;
Question3:
From above line, if u(k) is known from Question 1, then why are we dividing by 2.5 (2.0f+0.5f) and then it multiply with EPwm1Regs.TBPRD ? This Epwm is the one at which we are sampling the ADC at line 286 (EPwm1Regs.TBPRD = 0xFFFF)??? If yes then how this above duty cycle changes if suppose your y(k) changes dues to ADC values? I think I should remove line 309 and then check GPIO0 or GPIO1 to see the change of duty cycle if y(k) changes.
//########################################################################### // // FILE: Example_2833xAdcSoc.c // // TITLE: ADC Start of Conversion Example // //! \addtogroup f2833x_example_list //! <h1> ADC Start of Conversion (adc_soc)</h1> //! //! This ADC example uses ePWM1 to generate a periodic ADC SOC on SEQ1. //! Two channels are converted, ADCINA3 and ADCINA2. //! //! \b Watch \b Variables \n //! - Voltage1[10] - Last 10 ADCRESULT0 values //! - Voltage2[10] - Last 10 ADCRESULT1 values //! - ConversionCount - Current result number 0-9 //! - LoopCount - Idle loop counter // //########################################################################### // $TI Release: $ // $Release Date: $ // $Copyright: // Copyright (C) 2009-2023 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 "DSP28x_Project.h" // Device Headerfile and Examples Include File #include "DSP2833x_Device.h" #include "DSP2833x_Examples.h" #include "DSP2833x_GlobalPrototypes.h" #include "DCLF32.h" // // Function Prototypes // void gpio_select(void); void Setup_ePWM1(void); __interrupt void adc_isr(void); // // Globals // Uint16 LoopCount; Uint16 ConversionCount; Uint16 Voltage1[10]; Uint16 Voltage2[10]; //DCL_PID pid1 = PID_DEFAULTS; //typedef struct dcl_pid pid1; struct dcl_pid pid1; //float pid1; DCL_PID pid1 = PID_DEFAULTS; float32_t yk; float32_t lk; float32_t uk; //float32_t rk = 0.25; // initial value for control reference //float32_t lk = 1.0; // control loop not saturated float32_t rk; // initial value for control reference float32_t lk; // control loop not saturated float Duty; float upperlim = 0.95; float lowerlim = 0.05; unsigned int clampactive; // // Main // void main(void) { //DCL_PID pid1 = PID_DEFAULTS; pid1.Kp = 9.0f; pid1.Ki = 0.015f; pid1.Kd = 0.35f; pid1.Kr = 1.0f; pid1.c1 = 188.0296600613396f; pid1.c2 = 0.880296600613396f; pid1.d2 = 0.0f; pid1.d3 = 0.0f; pid1.i10 = 0.0f; pid1.i14 = 1.0f; pid1.Umax = 1.0f; pid1.Umin = -1.0f; rk = 0.25; // initial value for control reference lk = 1.0; // // Step 1. Initialize System Control: // PLL, WatchDog, enable Peripheral Clocks // This example function is found in the DSP2833x_SysCtrl.c file. // InitSysCtrl(); EALLOW; #if (CPU_FRQ_150MHZ) // Default - 150 MHz SYSCLKOUT // // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3) = 25.0 MHz // #define ADC_MODCLK 0x3 #endif #if (CPU_FRQ_100MHZ) // // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2) = 25.0 MHz // #define ADC_MODCLK 0x2 #endif EDIS; // // Define ADCCLK clock frequency ( less than or equal to 25 MHz ) // Assuming InitSysCtrl() has set SYSCLKOUT to 150 MHz // EALLOW; SysCtrlRegs.HISPCP.all = ADC_MODCLK; EDIS; // // Step 2. Initialize GPIO: // This example function is found in the DSP2833x_Gpio.c file and // illustrates how to set the GPIO to it's default state. // // InitGpio(); // Skipped for this example // // Step 3. Clear all interrupts and initialize PIE vector table: // Disable CPU interrupts // DINT; // // Initialize the PIE control registers to their default state. // The default state is all PIE interrupts disabled and flags // are cleared. // This function is found in the DSP2833x_PieCtrl.c file. // InitPieCtrl(); // // Disable CPU interrupts and clear all CPU interrupt flags: // IER = 0x0000; IFR = 0x0000; // // Initialize the PIE vector table with pointers to the shell Interrupt // Service Routines (ISR). // This will populate the entire table, even if the interrupt // is not used in this example. This is useful for debug purposes. // The shell ISR routines are found in DSP2833x_DefaultIsr.c. // This function is found in DSP2833x_PieVect.c. // InitPieVectTable(); // // Interrupts that are used in this example are re-mapped to // ISR functions found within this file. // EALLOW; // This is needed to write to EALLOW protected register PieVectTable.ADCINT = &adc_isr; EDIS; // This is needed to disable write to EALLOW protected registers //* configure ePWM1 */ EALLOW; SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; EDIS; //InitEPwm1Gpio(); // [F2806x_EPwm.c] gpio_select(); Setup_ePWM1(); // init ePWM1A //void Setup_ePWM1(void) //{ //EPwm1Regs.TBCTL.bit.CTRMODE = 3; // freeze TB counter //EPwm1Regs.TBCTL.bit.PRDLD = 1; // immediate load //EPwm1Regs.TBCTL.bit.PHSEN = 0; // disable phase loading //EPwm1Regs.TBCTL.bit.SYNCOSEL = 3; // disable SYNCOUT signal //EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0; // TBCLK = SYSCLKOUT //EPwm1Regs.TBCTL.bit.CLKDIV = 0; // clock divider = /1 //EPwm1Regs.TBCTL.bit.FREE_SOFT = 2; // free run on emulation suspend //EPwm1Regs.TBPRD = 0x2328; // set period for ePWM1 (0x2328 = 10kHz) //EPwm1Regs.TBPHS.all = 0; // time-base Phase Register //EPwm1Regs.TBCTR = 0; // time-base Counter Register //EPwm1Regs.ETSEL.bit.SOCAEN = 1; // enable SOC on A group //EPwm1Regs.ETSEL.bit.SOCASEL = 1; // select SOC from zero match //EPwm1Regs.ETPS.bit.SOCAPRD = 1; // generate pulse on 1st event //EPwm1Regs.CMPCTL.bit.SHDWAMODE = 0; // enable shadow mode //EPwm1Regs.CMPCTL.bit.LOADAMODE = 2; // reload on CTR=zero //EPwm1Regs.CMPA.half.CMPA = 0x0080; // set compare A value //EPwm1Regs.AQCTLA.bit.CAU = AQ_SET; // HIGH on CMPA up match //EPwm1Regs.AQCTLA.bit.ZRO = AQ_CLEAR; // LOW on zero match //} EALLOW; SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; EDIS; // // Step 4. Initialize all the Device Peripherals: // This function is found in DSP2833x_InitPeripherals.c // // InitPeripherals(); // Not required for this example InitAdc(); // For this example, init the ADC // // Step 5. User specific code, enable interrupts: // //gpio_select(); //Setup_ePWM1(); // init ePWM1A,ePWM1B // // Enable ADCINT in PIE // PieCtrlRegs.PIEIER1.bit.INTx6 = 1; IER |= M_INT1; // Enable CPU Interrupt 1 EINT; // Enable Global interrupt INTM ERTM; // Enable Global realtime interrupt DBGM LoopCount = 0; ConversionCount = 0; // // Configure ADC // AdcRegs.ADCMAXCONV.all = 0x0001; // Setup 2 conv's on SEQ1 AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x3; // Setup ADCINA3 as 1st SEQ1 conv. AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x2; // Setup ADCINA2 as 2nd SEQ1 conv. // // Enable SOCA from ePWM to start SEQ1 // AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1; AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1; // Enable SEQ1 interrupt (every EOS) // // Assumes ePWM1 clock is already enabled in InitSysCtrl(); // EPwm1Regs.ETSEL.bit.SOCAEN = 1; // Enable SOC on A group EPwm1Regs.ETSEL.bit.SOCASEL = 4; // Select SOC from from CPMA on upcount EPwm1Regs.ETPS.bit.SOCAPRD = 1; // Generate pulse on 1st event EPwm1Regs.CMPA.half.CMPA = 0x0080; // Set compare A value EPwm1Regs.TBPRD = 0xFFFF; // Set period for ePWM1 EPwm1Regs.TBCTL.bit.CTRMODE = 0; // count up and start // // Wait for ADC interrupt // for(;;) { LoopCount++; } } // void Setup_ePWM1(void) { EPwm1Regs.TBCTL.bit.CTRMODE = 3; // freeze TB counter EPwm1Regs.TBCTL.bit.PRDLD = 1; // immediate load EPwm1Regs.TBCTL.bit.PHSEN = 0; // disable phase loading EPwm1Regs.TBCTL.bit.SYNCOSEL = 3; // disable SYNCOUT signal EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0; // TBCLK = SYSCLKOUT EPwm1Regs.TBCTL.bit.CLKDIV = 0; // clock divider = /1 EPwm1Regs.TBCTL.bit.FREE_SOFT = 2; // free run on emulation suspend EPwm1Regs.TBPRD = 0x2328; // set period for ePWM1 (0x2328 = 10kHz) EPwm1Regs.TBPHS.all = 0; // time-base Phase Register EPwm1Regs.TBCTR = 0; // time-base Counter Register EPwm1Regs.ETSEL.bit.SOCAEN = 1; // enable SOC on A group EPwm1Regs.ETSEL.bit.SOCASEL = 1; // select SOC from zero match EPwm1Regs.ETPS.bit.SOCAPRD = 1; // generate pulse on 1st event EPwm1Regs.CMPCTL.bit.SHDWAMODE = 0; // enable shadow mode EPwm1Regs.CMPCTL.bit.LOADAMODE = 2; // reload on CTR=zero EPwm1Regs.CMPA.half.CMPA = 0x0080; // set compare A value EPwm1Regs.AQCTLA.bit.CAU = AQ_SET; // HIGH on CMPA up match EPwm1Regs.AQCTLA.bit.ZRO = AQ_CLEAR; // LOW on zero match } void gpio_select(void) { EALLOW; GpioCtrlRegs.GPAPUD.bit.GPIO0 = 0; // Enable pullup on GPIO0 GpioCtrlRegs.GPAPUD.bit.GPIO1 = 0; // Enable pullup on GPIO1 GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1; // GPIO0 = PWM1A GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1; // GPIO0 = PWM1B EDIS; } // adc_isr - // __interrupt void adc_isr(void) { Voltage1[ConversionCount] = AdcRegs.ADCRESULT0 >>4; Voltage2[ConversionCount] = AdcRegs.ADCRESULT1 >>4; // read ADC channel yk = ((float) AdcRegs.ADCRESULT0 - 2048.0f) / 2047.0f; // run PID controller uk = DCL_runPID_C4(&pid1, rk, yk, lk); // external clamp for anti-windup reset clampactive = DCL_runClamp_C1(&uk, upperlim, lowerlim); //clampactive = DCL_runClamp_C1(&uk, Umax, Umin); lk = (clampactive == 0U) ? 1.0f : 0.0f; // write u(k) to PWM Duty = (uk / 2.0f + 0.5f) * (float) EPwm1Regs.TBPRD; EPwm1Regs.CMPA.half.CMPA = (Uint16) Duty; // // If 40 conversions have been logged, start over // if(ConversionCount == 9) { ConversionCount = 0; } else { ConversionCount++; } // // Reinitialize for next ADC sequence // AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // Reset SEQ1 AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1; // Clear INT SEQ1 bit PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE return; } // // End of File //
Please explain all the questions step by step. Sorry I am new to using DCL PID simulation.
Thanks
Regards
Arsalan
Hi Arsalan,
For Q1, PID controllers also have an internal clamp that is set on line 122&123, therefore, your output ranges will be clamped to 1.0 ~ -1.0
For Q2, I'm not exactly sure what you meant by "no compulsion for choosing either 1 or 0 for clampactive". The purpose of this external clamp was to make sure that the controller won't be overdamped due to the saturated response (if control effort is clamped) and thus can't be recovered due to the influence of feedback loop. Our example only implemented rudimentary responses to either allow 100% of feedback or 0% of feedback, as shown in this diagram, lk dictates the ratio of the feedback into the controller:
Feel free to change the values around, and as a matter of fact, this external clamp is unnecessary as PID controller also includes an internal clamp. I believe the purpose of this clamp is only to showcase when lk is clamped. Referring to the code implementation of DCL_PID_C3, which is the inline C version of asm C4.
For Q3, I would have to reach out to our C2000 PWM export to give you a concrete answer on why are we dividing it by 2.5. But I believe you have some clean up to do as you have multiple instances of EPwm1Regs initialization. Will get to back you later once I received feedback from our internal export.
Best,
Sen Wang
Hi Seng,
Thanks for the background information that you have provided. It has been very helpful for me. I will wait for further feedback from you for 2.5 division.
Yesterday I tested the code on hardware. I am summarizing the results here: Signal is applied at ADCINA3 as input from 0 - 3V setup (see Picture1)
So here I plotted Voltage 1 graph: that gives a value around 243. at 0.1V ADCINA3 as input voltage and TBPRD 0xFFFF (see Picture2).
Picture3 shows all dcl values parameters (Kp,ki,kd,c1,c2,d1,d2.,vmax,vmin...etc). Here rk = 0.25,lk =0,yk=0.9,uk = 0.05 and duty cycle = 34405.875 at 0.1V ADCINA3 as input voltage
Picture1 :
Picture 2:
Picture 3:
After increasing the input voltage ADCINA3 slowly from 0.1V to 3V, the results are tabulated here in Picture 4 & Picture 5.
Voltage 1 graph: that gives a value around 4095 in Picture 4. And rk = 0.25,lk =0,yk=30.9,uk = 0.05 and duty cycle = 34405.875 in Picture5 at 3V ADCINA3 as input voltage
Picture 4:
Picture 5:
Question1:
When there is no Power. All ground then, Duty cycle 2.4% at oscilloscope. When Power is up, then Duty cycle 48% . As the input voltage ADCINA3 increases, there is no change in duty cycle number 34405.875 (Picture 5). it remains same and why? I guess it should change the duty cycle according to the applied voltage change. because I will send this duty cycle to one of ePwm Gpio port to watch the result through oscilloscope to check either the duty cycle changes how much depending upon increasing the applied input voltage. Please explain this. I am following this setup to build on my hardware:
Question 2:
As you see above in above pictures, y(k) is increased from 0.9 to 30.9 when increasing voltage from 0V to 3V ADCINA3 as input voltage.
In the DCL guide of ''Example 5: PID Controller Running on FPU32'', the y(k) must be equal to r(k) after settling. So here after increasing the input ADCINA3 voltage, the Picture 5 shows the settling output after little delay but y(k) doesn't become equal to reference voltage r(k). Please explain.
Question3 :
c1 and c2 are filter coefficients but d2 and d3 are same as i10 and i14 ? can we say them a coefficient factor after delay? here in above pictures only d2 and d3 are changing when increasing voltage, and why? Please explain.
Code is attached here: (This time the multiple EPwm1Regs initialization is removed here)
//########################################################################### // // FILE: Example_2833xAdcSoc.c // // TITLE: ADC Start of Conversion Example // //! \addtogroup f2833x_example_list //! <h1> ADC Start of Conversion (adc_soc)</h1> //! //! This ADC example uses ePWM1 to generate a periodic ADC SOC on SEQ1. //! Two channels are converted, ADCINA3 and ADCINA2. //! //! \b Watch \b Variables \n //! - Voltage1[10] - Last 10 ADCRESULT0 values //! - Voltage2[10] - Last 10 ADCRESULT1 values //! - ConversionCount - Current result number 0-9 //! - LoopCount - Idle loop counter // //########################################################################### // $TI Release: $ // $Release Date: $ // $Copyright: // Copyright (C) 2009-2023 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 "DSP28x_Project.h" // Device Headerfile and Examples Include File #include "DSP2833x_Device.h" #include "DSP2833x_Examples.h" #include "DSP2833x_GlobalPrototypes.h" #include "DCLF32.h" // // Function Prototypes // void gpio_select(void); void Setup_ePWM1(void); __interrupt void adc_isr(void); // // Globals // Uint16 LoopCount; Uint16 ConversionCount; Uint16 Voltage1[10]; Uint16 Voltage2[10]; //typedef struct dcl_pid pid1; struct dcl_pid pid1; //DCL_PID pid1 = PID_DEFAULTS; DCL_PID pid1 = PID_DEFAULTS; float32_t yk; float32_t lk; float32_t uk; float32_t rk; // initial value for control reference float32_t lk; // control loop not saturated float Duty; float upperlim = 0.95; float lowerlim = 0.05; unsigned int clampactive; // // Main // void main(void) { pid1.Kp = 9.0f; pid1.Ki = 0.015f; pid1.Kd = 0.35f; pid1.Kr = 1.0f; pid1.c1 = 188.0296600613396f; pid1.c2 = 0.880296600613396f; pid1.d2 = 0.0f; pid1.d3 = 0.0f; pid1.i10 = 0.0f; pid1.i14 = 1.0f; pid1.Umax = 1.0f; pid1.Umin = -1.0f; rk = 0.25; // initial value for control reference lk = 1.0; // control loop not saturated // // Step 1. Initialize System Control: // PLL, WatchDog, enable Peripheral Clocks // This example function is found in the DSP2833x_SysCtrl.c file. // InitSysCtrl(); EALLOW; #if (CPU_FRQ_150MHZ) // Default - 150 MHz SYSCLKOUT // // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3) = 25.0 MHz // #define ADC_MODCLK 0x3 #endif #if (CPU_FRQ_100MHZ) // // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2) = 25.0 MHz // #define ADC_MODCLK 0x2 #endif EDIS; // // Define ADCCLK clock frequency ( less than or equal to 25 MHz ) // Assuming InitSysCtrl() has set SYSCLKOUT to 150 MHz // EALLOW; SysCtrlRegs.HISPCP.all = ADC_MODCLK; EDIS; // // Step 2. Initialize GPIO: // This example function is found in the DSP2833x_Gpio.c file and // illustrates how to set the GPIO to it's default state. // // InitGpio(); // Skipped for this example // // Step 3. Clear all interrupts and initialize PIE vector table: // Disable CPU interrupts // DINT; // // Initialize the PIE control registers to their default state. // The default state is all PIE interrupts disabled and flags // are cleared. // This function is found in the DSP2833x_PieCtrl.c file. // InitPieCtrl(); // // Disable CPU interrupts and clear all CPU interrupt flags: // IER = 0x0000; IFR = 0x0000; // // Initialize the PIE vector table with pointers to the shell Interrupt // Service Routines (ISR). // This will populate the entire table, even if the interrupt // is not used in this example. This is useful for debug purposes. // The shell ISR routines are found in DSP2833x_DefaultIsr.c. // This function is found in DSP2833x_PieVect.c. // InitPieVectTable(); // // Interrupts that are used in this example are re-mapped to // ISR functions found within this file. // EALLOW; // This is needed to write to EALLOW protected register PieVectTable.ADCINT = &adc_isr; EDIS; // This is needed to disable write to EALLOW protected registers //* configure ePWM1 */ EALLOW; SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; EDIS; //InitEPwm1Gpio(); // [F2806x_EPwm.c] gpio_select(); Setup_ePWM1(); // init ePWM1A EALLOW; SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; EDIS; // // Step 4. Initialize all the Device Peripherals: // This function is found in DSP2833x_InitPeripherals.c // // InitPeripherals(); // Not required for this example InitAdc(); // For this example, init the ADC // // Step 5. User specific code, enable interrupts: // // // Enable ADCINT in PIE // PieCtrlRegs.PIEIER1.bit.INTx6 = 1; IER |= M_INT1; // Enable CPU Interrupt 1 EINT; // Enable Global interrupt INTM ERTM; // Enable Global realtime interrupt DBGM LoopCount = 0; ConversionCount = 0; // // Configure ADC // AdcRegs.ADCMAXCONV.all = 0x0001; // Setup 2 conv's on SEQ1 AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x3; // Setup ADCINA3 as 1st SEQ1 conv. AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x2; // Setup ADCINA2 as 2nd SEQ1 conv. // // Enable SOCA from ePWM to start SEQ1 // AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1; AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1; // Enable SEQ1 interrupt (every EOS) // // Assumes ePWM1 clock is already enabled in InitSysCtrl(); // // Wait for ADC interrupt // for(;;) { LoopCount++; } } // void Setup_ePWM1(void) { //EPwm1Regs.TBCTL.bit.CTRMODE = 3; // freeze TB counter EPwm1Regs.TBCTL.bit.CTRMODE = 0; // count up and start EPwm1Regs.TBCTL.bit.PRDLD = 1; // immediate load EPwm1Regs.TBCTL.bit.PHSEN = 0; // disable phase loading EPwm1Regs.TBCTL.bit.SYNCOSEL = 3; // disable SYNCOUT signal EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0; // TBCLK = SYSCLKOUT EPwm1Regs.TBCTL.bit.CLKDIV = 0; // clock divider = /1 EPwm1Regs.TBCTL.bit.FREE_SOFT = 2; // free run on emulation suspend //EPwm1Regs.TBPRD = 0x2328; // set period for ePWM1 (0x2328 = 10kHz) EPwm1Regs.TBPRD = 0xFFFF; // set period for ePWM1 (0x2328 = 10kHz) EPwm1Regs.TBPHS.all = 0; // time-base Phase Register EPwm1Regs.TBCTR = 0; // time-base Counter Register EPwm1Regs.ETSEL.bit.SOCAEN = 1; // enable SOC on A group //EPwm1Regs.ETSEL.bit.SOCASEL = 1; // select SOC from zero match EPwm1Regs.ETSEL.bit.SOCASEL = 1; // select SOC from zero match //EPwm1Regs.ETSEL.bit.SOCASEL = 4; // select SOC from zero match EPwm1Regs.ETPS.bit.SOCAPRD = 1; // generate pulse on 1st event EPwm1Regs.CMPCTL.bit.SHDWAMODE = 0; // enable shadow mode EPwm1Regs.CMPCTL.bit.LOADAMODE = 2; // reload on CTR=zero EPwm1Regs.CMPA.half.CMPA = 0x0080; // set compare A value EPwm1Regs.AQCTLA.bit.CAU = AQ_SET; // HIGH on CMPA up match EPwm1Regs.AQCTLA.bit.ZRO = AQ_CLEAR; // LOW on zero match } void gpio_select(void) { EALLOW; GpioCtrlRegs.GPAPUD.bit.GPIO0 = 0; // Enable pullup on GPIO0 GpioCtrlRegs.GPAPUD.bit.GPIO1 = 0; // Enable pullup on GPIO1 GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1; // GPIO0 = PWM1A GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1; // GPIO0 = PWM1B EDIS; } // adc_isr - // __interrupt void adc_isr(void) { Voltage1[ConversionCount] = AdcRegs.ADCRESULT0 >>4; Voltage2[ConversionCount] = AdcRegs.ADCRESULT1 >>4; // read ADC channel yk = ((float) AdcRegs.ADCRESULT0 - 2048.0f) / 2047.0f; // run PID controller uk = DCL_runPID_C4(&pid1, rk, yk, lk); // external clamp for anti-windup reset clampactive = DCL_runClamp_C1(&uk, upperlim, lowerlim); //clampactive = DCL_runClamp_C1(&uk, Umax, Umin); lk = (clampactive == 0U) ? 1.0f : 0.0f; // write u(k) to PWM Duty = (uk / 2.0f + 0.5f) * (float) EPwm1Regs.TBPRD; EPwm1Regs.CMPA.half.CMPA = (Uint16) Duty; // // If 40 conversions have been logged, start over // if(ConversionCount == 9) { ConversionCount = 0; } else { ConversionCount++; } // // Reinitialize for next ADC sequence // AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // Reset SEQ1 AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1; // Clear INT SEQ1 bit PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE return; } // // End of File //
Thanks
Regards
Arsalan
I will wait for further feedback from you for 2.5 division.
Hi Arsalan,
This is line is NOT dividing by 2.5f. Instead it's explained by this thread: e2e.ti.com/.../ccs-tms320f28379d-dcl-c200-motorware
"In response to the first question, the assumption in the example was that the controller output was normalised to +/-1.0f. The code line scales and then offsets the controller output to bring it in the 0 - 1.0f range, which is multiplied by the PWM period to get the duty cycle. It's just a code example."
Best regards,
Ryan Ma
Hi Arsalan, I apologize for the delay.
Like Ryan said, uk/2.0f + 0.5f was an attempt to normalize the control output range from -1.0~1.0f to 0~1.0f, representing 0% to 100% of the duty cycle.
And judging from your code, you've setup ADCINA2, ADCINA3 on F28335 but the ISR is still reading ADCRESULT0 from the F28069 DCL example, providing the signal to ADCINA3 shouldn't have any effect on ADCRESULT0 and all subsequent changes in ADCRESULT0 and PID is probably due to floating voltages.
One thing I forgot to mention is that there's also an external saturation that is included to further clamp the saturation range to 0.05f~0.95f instead of +/-1.0f. So you could comment out the runClamp_C1 in line 315 if you don't need additional saturation.
Best,
Sen Wang
Hi Ryan,
Thanks for your reply. I will get back to you soon after implementing your suggestion. I will let you know the results soon.
Regards
Arsalan
Hi Sen,
Thanks for your reply. I will get back to you soon after implementing your suggestion. I will let you know the results soon.
Regards
Arsalan
Hi Sen & Ryan,
Thanks for your comprehensive replies. Quite Helpful
And judging from your code, you've setup ADCINA2, ADCINA3 on F28335 but the ISR is still reading ADCRESULT0 from the F28069 DCL example, providing the signal to ADCINA3 shouldn't have any effect on ADCRESULT0 and all subsequent changes in ADCRESULT0 and PID is probably due to floating voltages.
As suggested, I fixed that issue, Now I am applying at ADCINA0 and ISR is reading ADCRESULT. and it is reading the duty cycle only at 2 values. (65553 or 0) at 0V and 3V respectively on watch window. There is no middle value it gives when i set the voltage suppose 1.2V and the duty cycle is 0 (no middle value around). see Picture. below:
At 0V(ground) applied on ADCINA0, the output Voltage1 = 0 approx on watch window, The Duty is 65535.(because TBPRD = FFFF) see Picture 1,2 and when 3V applied, the Duty cycle shows 0 Picture 3,4.
Picture 5 shows that when i applied a voltage of 1.2 approx at ADCINA0, the Duty cycle doesn't show around a middle value. it stays at 0. The Duty cycle must be change depending upon the whatever the voltage is applied accordingly.
Picture1 (at 0 V)
Picture2 (at 0V)--Duty cycle = 65535
Picture3 (at 3 V)
Picture4 (at 3V)--Duty cycle = 0
Picture 5 (at 1.2V approx)... Duty cycle has no middle value. it stays at 0.
Updated code is attached here
//########################################################################### // // FILE: Example_2833xAdcSoc.c // // TITLE: ADC Start of Conversion Example // //! \addtogroup f2833x_example_list //! <h1> ADC Start of Conversion (adc_soc)</h1> //! //! This ADC example uses ePWM1 to generate a periodic ADC SOC on SEQ1. //! Two channels are converted, ADCINA3 and ADCINA2. //! //! \b Watch \b Variables \n //! - Voltage1[10] - Last 10 ADCRESULT0 values //! - Voltage2[10] - Last 10 ADCRESULT1 values //! - ConversionCount - Current result number 0-9 //! - LoopCount - Idle loop counter // //########################################################################### // $TI Release: $ // $Release Date: $ // $Copyright: // Copyright (C) 2009-2023 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 "DSP28x_Project.h" // Device Headerfile and Examples Include File #include "DSP2833x_Device.h" #include "DSP2833x_Examples.h" #include "DSP2833x_GlobalPrototypes.h" #include "DCLF32.h" // // Function Prototypes // void gpio_select(void); void Setup_ePWM1(void); __interrupt void adc_isr(void); // // Globals // Uint16 LoopCount; Uint16 ConversionCount; Uint16 Voltage1[10]; Uint16 Voltage2[10]; //typedef struct dcl_pid pid1; struct dcl_pid pid1; //DCL_PID pid1 = PID_DEFAULTS; DCL_PID pid1 = PID_DEFAULTS; float32_t yk; float32_t lk; float32_t uk; float32_t rk; // initial value for control reference float32_t lk; // control loop not saturated float Duty; float upperlim = 0.95; float lowerlim = 0.05; unsigned int clampactive; // // Main // void main(void) { pid1.Kp = 9.0f; pid1.Ki = 0.015f; pid1.Kd = 0.35f; pid1.Kr = 1.0f; pid1.c1 = 188.0296600613396f; pid1.c2 = 0.880296600613396f; pid1.d2 = 0.0f; pid1.d3 = 0.0f; pid1.i10 = 0.0f; pid1.i14 = 1.0f; pid1.Umax = 1.0f; pid1.Umin = -1.0f; rk = 0.25; // initial value for control reference lk = 1.0; // control loop not saturated // // Step 1. Initialize System Control: // PLL, WatchDog, enable Peripheral Clocks // This example function is found in the DSP2833x_SysCtrl.c file. // InitSysCtrl(); EALLOW; #if (CPU_FRQ_150MHZ) // Default - 150 MHz SYSCLKOUT // // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3) = 25.0 MHz // #define ADC_MODCLK 0x3 #endif #if (CPU_FRQ_100MHZ) // // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2) = 25.0 MHz // #define ADC_MODCLK 0x2 #endif EDIS; // // Define ADCCLK clock frequency ( less than or equal to 25 MHz ) // Assuming InitSysCtrl() has set SYSCLKOUT to 150 MHz // EALLOW; SysCtrlRegs.HISPCP.all = ADC_MODCLK; EDIS; // // Step 2. Initialize GPIO: // This example function is found in the DSP2833x_Gpio.c file and // illustrates how to set the GPIO to it's default state. // // InitGpio(); // Skipped for this example // // Step 3. Clear all interrupts and initialize PIE vector table: // Disable CPU interrupts // DINT; // // Initialize the PIE control registers to their default state. // The default state is all PIE interrupts disabled and flags // are cleared. // This function is found in the DSP2833x_PieCtrl.c file. // InitPieCtrl(); // // Disable CPU interrupts and clear all CPU interrupt flags: // IER = 0x0000; IFR = 0x0000; // // Initialize the PIE vector table with pointers to the shell Interrupt // Service Routines (ISR). // This will populate the entire table, even if the interrupt // is not used in this example. This is useful for debug purposes. // The shell ISR routines are found in DSP2833x_DefaultIsr.c. // This function is found in DSP2833x_PieVect.c. // InitPieVectTable(); // // Interrupts that are used in this example are re-mapped to // ISR functions found within this file. // EALLOW; // This is needed to write to EALLOW protected register PieVectTable.ADCINT = &adc_isr; EDIS; // This is needed to disable write to EALLOW protected registers //* configure ePWM1 */ EALLOW; SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; EDIS; //InitEPwm1Gpio(); // [F2806x_EPwm.c] gpio_select(); Setup_ePWM1(); // init ePWM1A EALLOW; SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; EDIS; // // Step 4. Initialize all the Device Peripherals: // This function is found in DSP2833x_InitPeripherals.c // // InitPeripherals(); // Not required for this example InitAdc(); // For this example, init the ADC // // Step 5. User specific code, enable interrupts: // // // Enable ADCINT in PIE // PieCtrlRegs.PIEIER1.bit.INTx6 = 1; IER |= M_INT1; // Enable CPU Interrupt 1 EINT; // Enable Global interrupt INTM ERTM; // Enable Global realtime interrupt DBGM LoopCount = 0; ConversionCount = 0; // // Configure ADC // AdcRegs.ADCMAXCONV.all = 0x0001; // Setup 2 conv's on SEQ1 AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0; // Setup ADCINA0 as 1st SEQ1 conv. AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x2; // Setup ADCINA2 as 2nd SEQ1 conv. // // Enable SOCA from ePWM to start SEQ1 // AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1; AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1; // Enable SEQ1 interrupt (every EOS) // // Assumes ePWM1 clock is already enabled in InitSysCtrl(); // // Wait for ADC interrupt // for(;;) { LoopCount++; } } // void Setup_ePWM1(void) { //EPwm1Regs.TBCTL.bit.CTRMODE = 3; // freeze TB counter EPwm1Regs.TBCTL.bit.CTRMODE = 0; // count up and start EPwm1Regs.TBCTL.bit.PRDLD = 1; // immediate load EPwm1Regs.TBCTL.bit.PHSEN = 0; // disable phase loading EPwm1Regs.TBCTL.bit.SYNCOSEL = 3; // disable SYNCOUT signal EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0; // TBCLK = SYSCLKOUT EPwm1Regs.TBCTL.bit.CLKDIV = 0; // clock divider = /1 EPwm1Regs.TBCTL.bit.FREE_SOFT = 2; // free run on emulation suspend //EPwm1Regs.TBPRD = 0x2328; // set period for ePWM1 (0x2328 = 10kHz) EPwm1Regs.TBPRD = 0xFFFF; // set period for ePWM1 (0x2328 = 10kHz) EPwm1Regs.TBPHS.all = 0; // time-base Phase Register EPwm1Regs.TBCTR = 0; // time-base Counter Register EPwm1Regs.ETSEL.bit.SOCAEN = 1; // enable SOC on A group //EPwm1Regs.ETSEL.bit.SOCASEL = 1; // select SOC from zero match EPwm1Regs.ETSEL.bit.SOCASEL = 1; // select SOC from zero match //EPwm1Regs.ETSEL.bit.SOCASEL = 4; // select SOC from zero match EPwm1Regs.ETPS.bit.SOCAPRD = 1; // generate pulse on 1st event EPwm1Regs.CMPCTL.bit.SHDWAMODE = 0; // enable shadow mode EPwm1Regs.CMPCTL.bit.LOADAMODE = 2; // reload on CTR=zero EPwm1Regs.CMPA.half.CMPA = 0x0080; // set compare A value EPwm1Regs.AQCTLA.bit.CAU = AQ_SET; // HIGH on CMPA up match EPwm1Regs.AQCTLA.bit.ZRO = AQ_CLEAR; // LOW on zero match } void gpio_select(void) { EALLOW; GpioCtrlRegs.GPAPUD.bit.GPIO0 = 0; // Enable pullup on GPIO0 GpioCtrlRegs.GPAPUD.bit.GPIO1 = 0; // Enable pullup on GPIO1 GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1; // GPIO0 = PWM1A GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1; // GPIO0 = PWM1B EDIS; } // adc_isr - // __interrupt void adc_isr(void) { Voltage1[ConversionCount] = AdcRegs.ADCRESULT0 >>4; Voltage2[ConversionCount] = AdcRegs.ADCRESULT1 >>4; // read ADC channel yk = ((float) AdcRegs.ADCRESULT0 - 2048.0f) / 2047.0f; // run PID controller uk = DCL_runPID_C4(&pid1, rk, yk, lk); // external clamp for anti-windup reset //clampactive = DCL_runClamp_C1(&uk, upperlim, lowerlim); //clampactive = DCL_runClamp_C1(&uk, Umax, Umin); lk = (clampactive == 0U) ? 1.0f : 0.0f; // write u(k) to PWM Duty = (uk / 2.0f + 0.5f) * (float) EPwm1Regs.TBPRD; EPwm1Regs.CMPA.half.CMPA = (Uint16) Duty; // // If 40 conversions have been logged, start over // if(ConversionCount == 9) { ConversionCount = 0; } else { ConversionCount++; } // // Reinitialize for next ADC sequence // AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // Reset SEQ1 AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1; // Clear INT SEQ1 bit PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE return; } // // End of File //
Please suggest this issue accordingly
Thanks
Regards
Arsalan
Hi Sen & Ryan,
Thanks for your comprehensive replies. Quite Helpful
And judging from your code, you've setup ADCINA2, ADCINA3 on F28335 but the ISR is still reading ADCRESULT0 from the F28069 DCL example, providing the signal to ADCINA3 shouldn't have any effect on ADCRESULT0 and all subsequent changes in ADCRESULT0 and PID is probably due to floating voltages.
As suggested, I fixed that issue, Now I am applying at ADCINA0 and ISR is reading ADCRESULT. and it is reading the duty cycle only at 2 values. (65553 or 0) at 0V and 3V respectively on watch window. There is no middle value it gives when i set the voltage suppose 1.2V and the duty cycle is 0 (no middle value around). see Picture. below:
At 0V(ground) applied on ADCINA0, the output Voltage1 = 0 approx on watch window, The Duty is 65535.(because TBPRD = FFFF) see Picture 1,2 and when 3V applied, the Duty cycle shows 0 Picture 3,4.
Picture 5 shows that when i applied a voltage of 1.2 approx at ADCINA0, the Duty cycle doesn't show around a middle value. it stays at 0. The Duty cycle must be change depending upon the whatever the voltage is applied accordingly.
Picture1 (at 0 V)
Picture2 (at 0V)--Duty cycle = 65535
Picture3 (at 3 V)
Picture4 (at 3V)--Duty cycle = 0
Picture 5 (at 1.2V approx)... Duty cycle has no middle value. it stays at 0.
Updated code is attached here
//########################################################################### // // FILE: Example_2833xAdcSoc.c // // TITLE: ADC Start of Conversion Example // //! \addtogroup f2833x_example_list //! <h1> ADC Start of Conversion (adc_soc)</h1> //! //! This ADC example uses ePWM1 to generate a periodic ADC SOC on SEQ1. //! Two channels are converted, ADCINA3 and ADCINA2. //! //! \b Watch \b Variables \n //! - Voltage1[10] - Last 10 ADCRESULT0 values //! - Voltage2[10] - Last 10 ADCRESULT1 values //! - ConversionCount - Current result number 0-9 //! - LoopCount - Idle loop counter // //########################################################################### // $TI Release: $ // $Release Date: $ // $Copyright: // Copyright (C) 2009-2023 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 "DSP28x_Project.h" // Device Headerfile and Examples Include File #include "DSP2833x_Device.h" #include "DSP2833x_Examples.h" #include "DSP2833x_GlobalPrototypes.h" #include "DCLF32.h" // // Function Prototypes // void gpio_select(void); void Setup_ePWM1(void); __interrupt void adc_isr(void); // // Globals // Uint16 LoopCount; Uint16 ConversionCount; Uint16 Voltage1[10]; Uint16 Voltage2[10]; //typedef struct dcl_pid pid1; struct dcl_pid pid1; //DCL_PID pid1 = PID_DEFAULTS; DCL_PID pid1 = PID_DEFAULTS; float32_t yk; float32_t lk; float32_t uk; float32_t rk; // initial value for control reference float32_t lk; // control loop not saturated float Duty; float upperlim = 0.95; float lowerlim = 0.05; unsigned int clampactive; // // Main // void main(void) { pid1.Kp = 9.0f; pid1.Ki = 0.015f; pid1.Kd = 0.35f; pid1.Kr = 1.0f; pid1.c1 = 188.0296600613396f; pid1.c2 = 0.880296600613396f; pid1.d2 = 0.0f; pid1.d3 = 0.0f; pid1.i10 = 0.0f; pid1.i14 = 1.0f; pid1.Umax = 1.0f; pid1.Umin = -1.0f; rk = 0.25; // initial value for control reference lk = 1.0; // control loop not saturated // // Step 1. Initialize System Control: // PLL, WatchDog, enable Peripheral Clocks // This example function is found in the DSP2833x_SysCtrl.c file. // InitSysCtrl(); EALLOW; #if (CPU_FRQ_150MHZ) // Default - 150 MHz SYSCLKOUT // // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3) = 25.0 MHz // #define ADC_MODCLK 0x3 #endif #if (CPU_FRQ_100MHZ) // // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2) = 25.0 MHz // #define ADC_MODCLK 0x2 #endif EDIS; // // Define ADCCLK clock frequency ( less than or equal to 25 MHz ) // Assuming InitSysCtrl() has set SYSCLKOUT to 150 MHz // EALLOW; SysCtrlRegs.HISPCP.all = ADC_MODCLK; EDIS; // // Step 2. Initialize GPIO: // This example function is found in the DSP2833x_Gpio.c file and // illustrates how to set the GPIO to it's default state. // // InitGpio(); // Skipped for this example // // Step 3. Clear all interrupts and initialize PIE vector table: // Disable CPU interrupts // DINT; // // Initialize the PIE control registers to their default state. // The default state is all PIE interrupts disabled and flags // are cleared. // This function is found in the DSP2833x_PieCtrl.c file. // InitPieCtrl(); // // Disable CPU interrupts and clear all CPU interrupt flags: // IER = 0x0000; IFR = 0x0000; // // Initialize the PIE vector table with pointers to the shell Interrupt // Service Routines (ISR). // This will populate the entire table, even if the interrupt // is not used in this example. This is useful for debug purposes. // The shell ISR routines are found in DSP2833x_DefaultIsr.c. // This function is found in DSP2833x_PieVect.c. // InitPieVectTable(); // // Interrupts that are used in this example are re-mapped to // ISR functions found within this file. // EALLOW; // This is needed to write to EALLOW protected register PieVectTable.ADCINT = &adc_isr; EDIS; // This is needed to disable write to EALLOW protected registers //* configure ePWM1 */ EALLOW; SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; EDIS; //InitEPwm1Gpio(); // [F2806x_EPwm.c] gpio_select(); Setup_ePWM1(); // init ePWM1A EALLOW; SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; EDIS; // // Step 4. Initialize all the Device Peripherals: // This function is found in DSP2833x_InitPeripherals.c // // InitPeripherals(); // Not required for this example InitAdc(); // For this example, init the ADC // // Step 5. User specific code, enable interrupts: // // // Enable ADCINT in PIE // PieCtrlRegs.PIEIER1.bit.INTx6 = 1; IER |= M_INT1; // Enable CPU Interrupt 1 EINT; // Enable Global interrupt INTM ERTM; // Enable Global realtime interrupt DBGM LoopCount = 0; ConversionCount = 0; // // Configure ADC // AdcRegs.ADCMAXCONV.all = 0x0001; // Setup 2 conv's on SEQ1 AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0; // Setup ADCINA0 as 1st SEQ1 conv. AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x2; // Setup ADCINA2 as 2nd SEQ1 conv. // // Enable SOCA from ePWM to start SEQ1 // AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1; AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1; // Enable SEQ1 interrupt (every EOS) // // Assumes ePWM1 clock is already enabled in InitSysCtrl(); // // Wait for ADC interrupt // for(;;) { LoopCount++; } } // void Setup_ePWM1(void) { //EPwm1Regs.TBCTL.bit.CTRMODE = 3; // freeze TB counter EPwm1Regs.TBCTL.bit.CTRMODE = 0; // count up and start EPwm1Regs.TBCTL.bit.PRDLD = 1; // immediate load EPwm1Regs.TBCTL.bit.PHSEN = 0; // disable phase loading EPwm1Regs.TBCTL.bit.SYNCOSEL = 3; // disable SYNCOUT signal EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0; // TBCLK = SYSCLKOUT EPwm1Regs.TBCTL.bit.CLKDIV = 0; // clock divider = /1 EPwm1Regs.TBCTL.bit.FREE_SOFT = 2; // free run on emulation suspend //EPwm1Regs.TBPRD = 0x2328; // set period for ePWM1 (0x2328 = 10kHz) EPwm1Regs.TBPRD = 0xFFFF; // set period for ePWM1 (0x2328 = 10kHz) EPwm1Regs.TBPHS.all = 0; // time-base Phase Register EPwm1Regs.TBCTR = 0; // time-base Counter Register EPwm1Regs.ETSEL.bit.SOCAEN = 1; // enable SOC on A group //EPwm1Regs.ETSEL.bit.SOCASEL = 1; // select SOC from zero match EPwm1Regs.ETSEL.bit.SOCASEL = 1; // select SOC from zero match //EPwm1Regs.ETSEL.bit.SOCASEL = 4; // select SOC from zero match EPwm1Regs.ETPS.bit.SOCAPRD = 1; // generate pulse on 1st event EPwm1Regs.CMPCTL.bit.SHDWAMODE = 0; // enable shadow mode EPwm1Regs.CMPCTL.bit.LOADAMODE = 2; // reload on CTR=zero EPwm1Regs.CMPA.half.CMPA = 0x0080; // set compare A value EPwm1Regs.AQCTLA.bit.CAU = AQ_SET; // HIGH on CMPA up match EPwm1Regs.AQCTLA.bit.ZRO = AQ_CLEAR; // LOW on zero match } void gpio_select(void) { EALLOW; GpioCtrlRegs.GPAPUD.bit.GPIO0 = 0; // Enable pullup on GPIO0 GpioCtrlRegs.GPAPUD.bit.GPIO1 = 0; // Enable pullup on GPIO1 GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1; // GPIO0 = PWM1A GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1; // GPIO0 = PWM1B EDIS; } // adc_isr - // __interrupt void adc_isr(void) { Voltage1[ConversionCount] = AdcRegs.ADCRESULT0 >>4; Voltage2[ConversionCount] = AdcRegs.ADCRESULT1 >>4; // read ADC channel yk = ((float) AdcRegs.ADCRESULT0 - 2048.0f) / 2047.0f; // run PID controller uk = DCL_runPID_C4(&pid1, rk, yk, lk); // external clamp for anti-windup reset //clampactive = DCL_runClamp_C1(&uk, upperlim, lowerlim); //clampactive = DCL_runClamp_C1(&uk, Umax, Umin); lk = (clampactive == 0U) ? 1.0f : 0.0f; // write u(k) to PWM Duty = (uk / 2.0f + 0.5f) * (float) EPwm1Regs.TBPRD; EPwm1Regs.CMPA.half.CMPA = (Uint16) Duty; // // If 40 conversions have been logged, start over // if(ConversionCount == 9) { ConversionCount = 0; } else { ConversionCount++; } // // Reinitialize for next ADC sequence // AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // Reset SEQ1 AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1; // Clear INT SEQ1 bit PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE return; } // // End of File //
Please suggest this issue accordingly
Thanks
Regards
Arsalan
Hi Arsalan,
You're seeing the correct ADC samples now, correct? It seems when voltage is at 1.2V, uk is -1 which is incorrect and I believe should be 0? That way duty is 50% of the TBPRD. Why is it that when you're at 1.2V, uk is -1V, I believe uk is returning an incorrect floating point value. Sen Wang Is there something missing here?
Best,
Ryan Ma
Hi Arsalan,
Yes, for one the value of yk should be bounded to +/-1 due to line 309, but in picture 5 you have 12.8. With Kp being 9.0f, the controller would return a maximum control effort, which is bounded to +/-1, which may explain why you only see duty cycle being either 0 or 100%. The controller is overreacting to the larger/smaller than expected input and thus is outputting an maximum control effort.
Best,
Sen Wang
Hi Seng,
Thanks for your reply.
for one the value of yk should be bounded to +/-1
As you said, the yk value should be either 1 or -1. But according this formula line 309, this value should be in between -1 to +1 with +1 & -1 inclusion. It can be any value between them depending upon my adc input ADCINA0 from 0 -3V.
With Kp being 9.0f, the controller would return a maximum control effort, which is bounded to +/-1, which may explain why you only see duty cycle being either 0 or 100%
Should i change Kp so that i can have a duty cycle change in between (0 to 100%) ?? not exactly either 0 or 100%. Because here the purpose is generate varying duty cycle depending upon my ADC input. As Ryan said that, if i am applying 1.2V then I must have duty near around 50%. Could you please how i can generate varying duty cycle like 10, 20 ,50 55,68,77,90 % etc ??? should i set other parameters in the program
The code is attached here
//########################################################################### // // FILE: Example_2833xAdcSoc.c // // TITLE: ADC Start of Conversion Example // //! \addtogroup f2833x_example_list //! <h1> ADC Start of Conversion (adc_soc)</h1> //! //! This ADC example uses ePWM1 to generate a periodic ADC SOC on SEQ1. //! Two channels are converted, ADCINA3 and ADCINA2. //! //! \b Watch \b Variables \n //! - Voltage1[10] - Last 10 ADCRESULT0 values //! - Voltage2[10] - Last 10 ADCRESULT1 values //! - ConversionCount - Current result number 0-9 //! - LoopCount - Idle loop counter // //########################################################################### // $TI Release: $ // $Release Date: $ // $Copyright: // Copyright (C) 2009-2023 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 "DSP28x_Project.h" // Device Headerfile and Examples Include File #include "DSP2833x_Device.h" #include "DSP2833x_Examples.h" #include "DSP2833x_GlobalPrototypes.h" #include "DCLF32.h" // // Function Prototypes // void gpio_select(void); void Setup_ePWM1(void); __interrupt void adc_isr(void); // // Globals // Uint16 LoopCount; Uint16 ConversionCount; Uint16 Voltage1[10]; Uint16 Voltage2[10]; //typedef struct dcl_pid pid1; struct dcl_pid pid1; //DCL_PID pid1 = PID_DEFAULTS; DCL_PID pid1 = PID_DEFAULTS; float32_t yk; float32_t lk; float32_t uk; float32_t rk; // initial value for control reference float32_t lk; // control loop not saturated float Duty; float upperlim = 0.95; float lowerlim = 0.05; unsigned int clampactive; // // Main // void main(void) { pid1.Kp = 9.0f; pid1.Ki = 0.015f; pid1.Kd = 0.35f; pid1.Kr = 1.0f; pid1.c1 = 188.0296600613396f; pid1.c2 = 0.880296600613396f; pid1.d2 = 0.0f; pid1.d3 = 0.0f; pid1.i10 = 0.0f; pid1.i14 = 1.0f; pid1.Umax = 1.0f; pid1.Umin = -1.0f; rk = 0.25; // initial value for control reference lk = 1.0; // control loop not saturated // // Step 1. Initialize System Control: // PLL, WatchDog, enable Peripheral Clocks // This example function is found in the DSP2833x_SysCtrl.c file. // InitSysCtrl(); EALLOW; #if (CPU_FRQ_150MHZ) // Default - 150 MHz SYSCLKOUT // // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3) = 25.0 MHz // #define ADC_MODCLK 0x3 #endif #if (CPU_FRQ_100MHZ) // // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2) = 25.0 MHz // #define ADC_MODCLK 0x2 #endif EDIS; // // Define ADCCLK clock frequency ( less than or equal to 25 MHz ) // Assuming InitSysCtrl() has set SYSCLKOUT to 150 MHz // EALLOW; SysCtrlRegs.HISPCP.all = ADC_MODCLK; EDIS; // // Step 2. Initialize GPIO: // This example function is found in the DSP2833x_Gpio.c file and // illustrates how to set the GPIO to it's default state. // // InitGpio(); // Skipped for this example // // Step 3. Clear all interrupts and initialize PIE vector table: // Disable CPU interrupts // DINT; // // Initialize the PIE control registers to their default state. // The default state is all PIE interrupts disabled and flags // are cleared. // This function is found in the DSP2833x_PieCtrl.c file. // InitPieCtrl(); // // Disable CPU interrupts and clear all CPU interrupt flags: // IER = 0x0000; IFR = 0x0000; // // Initialize the PIE vector table with pointers to the shell Interrupt // Service Routines (ISR). // This will populate the entire table, even if the interrupt // is not used in this example. This is useful for debug purposes. // The shell ISR routines are found in DSP2833x_DefaultIsr.c. // This function is found in DSP2833x_PieVect.c. // InitPieVectTable(); // // Interrupts that are used in this example are re-mapped to // ISR functions found within this file. // EALLOW; // This is needed to write to EALLOW protected register PieVectTable.ADCINT = &adc_isr; EDIS; // This is needed to disable write to EALLOW protected registers //* configure ePWM1 */ EALLOW; SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; EDIS; //InitEPwm1Gpio(); // [F2806x_EPwm.c] gpio_select(); Setup_ePWM1(); // init ePWM1A EALLOW; SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; EDIS; // // Step 4. Initialize all the Device Peripherals: // This function is found in DSP2833x_InitPeripherals.c // // InitPeripherals(); // Not required for this example InitAdc(); // For this example, init the ADC // // Step 5. User specific code, enable interrupts: // // // Enable ADCINT in PIE // PieCtrlRegs.PIEIER1.bit.INTx6 = 1; IER |= M_INT1; // Enable CPU Interrupt 1 EINT; // Enable Global interrupt INTM ERTM; // Enable Global realtime interrupt DBGM LoopCount = 0; ConversionCount = 0; // // Configure ADC // AdcRegs.ADCMAXCONV.all = 0x0001; // Setup 2 conv's on SEQ1 AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0; // Setup ADCINA0 as 1st SEQ1 conv. AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x2; // Setup ADCINA2 as 2nd SEQ1 conv. // // Enable SOCA from ePWM to start SEQ1 // AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1; AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1; // Enable SEQ1 interrupt (every EOS) // // Assumes ePWM1 clock is already enabled in InitSysCtrl(); // // Wait for ADC interrupt // for(;;) { LoopCount++; } } // void Setup_ePWM1(void) { //EPwm1Regs.TBCTL.bit.CTRMODE = 3; // freeze TB counter EPwm1Regs.TBCTL.bit.CTRMODE = 0; // count up and start EPwm1Regs.TBCTL.bit.PRDLD = 1; // immediate load EPwm1Regs.TBCTL.bit.PHSEN = 0; // disable phase loading EPwm1Regs.TBCTL.bit.SYNCOSEL = 3; // disable SYNCOUT signal EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0; // TBCLK = SYSCLKOUT EPwm1Regs.TBCTL.bit.CLKDIV = 0; // clock divider = /1 EPwm1Regs.TBCTL.bit.FREE_SOFT = 2; // free run on emulation suspend //EPwm1Regs.TBPRD = 0x2328; // set period for ePWM1 (0x2328 = 10kHz) EPwm1Regs.TBPRD = 0xFFFF; // set period for ePWM1 (0x2328 = 10kHz) EPwm1Regs.TBPHS.all = 0; // time-base Phase Register EPwm1Regs.TBCTR = 0; // time-base Counter Register EPwm1Regs.ETSEL.bit.SOCAEN = 1; // enable SOC on A group //EPwm1Regs.ETSEL.bit.SOCASEL = 1; // select SOC from zero match EPwm1Regs.ETSEL.bit.SOCASEL = 1; // select SOC from zero match //EPwm1Regs.ETSEL.bit.SOCASEL = 4; // select SOC from zero match EPwm1Regs.ETPS.bit.SOCAPRD = 1; // generate pulse on 1st event EPwm1Regs.CMPCTL.bit.SHDWAMODE = 0; // enable shadow mode EPwm1Regs.CMPCTL.bit.LOADAMODE = 2; // reload on CTR=zero EPwm1Regs.CMPA.half.CMPA = 0x0080; // set compare A value EPwm1Regs.AQCTLA.bit.CAU = AQ_SET; // HIGH on CMPA up match EPwm1Regs.AQCTLA.bit.ZRO = AQ_CLEAR; // LOW on zero match } void gpio_select(void) { EALLOW; GpioCtrlRegs.GPAPUD.bit.GPIO0 = 0; // Enable pullup on GPIO0 GpioCtrlRegs.GPAPUD.bit.GPIO1 = 0; // Enable pullup on GPIO1 GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1; // GPIO0 = PWM1A GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1; // GPIO0 = PWM1B EDIS; } // adc_isr - // __interrupt void adc_isr(void) { Voltage1[ConversionCount] = AdcRegs.ADCRESULT0 >>4; Voltage2[ConversionCount] = AdcRegs.ADCRESULT1 >>4; // read ADC channel yk = ((float) AdcRegs.ADCRESULT0 - 2048.0f) / 2047.0f; // run PID controller uk = DCL_runPID_C4(&pid1, rk, yk, lk); // external clamp for anti-windup reset //clampactive = DCL_runClamp_C1(&uk, upperlim, lowerlim); //clampactive = DCL_runClamp_C1(&uk, Umax, Umin); lk = (clampactive == 0U) ? 1.0f : 0.0f; // write u(k) to PWM Duty = (uk / 2.0f + 0.5f) * (float) EPwm1Regs.TBPRD; EPwm1Regs.CMPA.half.CMPA = (Uint16) Duty; // // If 40 conversions have been logged, start over // if(ConversionCount == 9) { ConversionCount = 0; } else { ConversionCount++; } // // Reinitialize for next ADC sequence // AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // Reset SEQ1 AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1; // Clear INT SEQ1 bit PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE return; } // // End of File //
Thanks
Regards
Arsalan
Hi Arsalan,
My concern was that yk is well outside of the range of +/-1, it was showing 12.9f on image 5 and it shouldn't be the case. If we do a quick calculation, P-path's control effort would be abs(yk - rk) * Kp = (12.9 - 0.25) * 9 = 113.85, which would be clamped to 1.
Could you please how i can generate varying duty cycle like 10, 20 ,50 55,68,77,90 % etc ???
Currently, do you have any feedback mechanism that allows you to use the duty cycle and influence your adc input? If there isn't any feedback, PID controller will never be able to reach the target voltage and its I-path would eventually saturate the feedback. And if you don't have a consistent input influence, the controller would eventually settle at 0% as it reaches the target value. The only way for a controller to constantly provide a control effort is if you have an constantly applied influence to begin with.
if i am applying 1.2V then I must have duty near around 50%
If you just need proportional duty cycles based on ADC readings, you don't need a PID to achieve that, directly assigning ADC reading to the duty cycle register would suffice, provided no external influence to the system.
Best,
Sen Wang