Other Parts Discussed in Thread: CONTROLSUITE
Hi guys,
I am using the F28335 controlCARD I would like to implement a closed loop control for a simple buck converter. In open loop, I successfully generate the necessary PWM signals and I managed to get some ADC readings, which directly brings me to my question:
How and where is the ADC reading optimally implemented in a closed loop control?
I generate my PWM signals with the ePWM module (UP/DOWN count) and enter the ISR for the ePWM module twice in every switching period (i.e. when the counter hits PRD and 0). Within the ePWM ISR, I perform the ADC reading AND the PWM generation.
Going through the examples in controlSUITE, the ADC reading uses its own ISR, which is triggered by the ePWM module. This aspect confuses me since it is different from my approach.
Could you maybe elaborate on how a proper implementation of the ADC is done (does the ADC reading have to have its own ISR or is it just as fine to perform the ADC reading within the ePWM ISR?)
Thank you very much.
Below my current implementation for the F28335 DSP:
#include "DSP28x_Project.h" // Device Headerfile and Examples Include File
#include <math.h>
// Prototype statements for functions found within this file.
void InitEPwm2(void);
void InitEPwm6(void);
void InitializeADC(void);
__interrupt void epwm2_isr(void);
// User defined
long freq;
double ADCgain;
double PRD;
#define RESULTS_BUFFER_SIZE 256 //buffer for storing conversion results
Uint16 AdcaResults[RESULTS_BUFFER_SIZE];
Uint16 resultsIndex;
volatile Uint16 bufferFull;
void main(void)
{
// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
InitSysCtrl();
// Set the switching PRD [kHz] in freq
freq = 16;
// Calculate the running phase: 2^32/(2*fsw)*50; -> 2^32/(16000*2)*50 = 6710886
PRD = 150000/(2*freq);
ADCgain = PRD/4096;
// Define ADCCLK clock frequency ( less than or equal to 25 MHz )
// Assuming InitSysCtrl() has set SYSCLKOUT to 150 MHz
EALLOW;
SysCtrlRegs.HISPCP.all = 3;
EDIS;
// Step 2. Initialize GPIO:
// This example function is found in the F2837xS_Gpio.c file and
// illustrates how to set the GPIO to its default state.
InitGpio();
// For this case just init GPIO pins for ePWM1, ePWM2, ePWM3
// These functions are in the F2837xS_EPwm.c file
InitEPwm2Gpio();
InitEPwm6Gpio();
InitAdc(); // For this example, init the ADC
// 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 F2837xS_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 F2837xS_DefaultIsr.c.
// This function is found in F2837xS_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 registers
PieVectTable.EPWM2_INT = &epwm2_isr; //function for ePWM2 interrupt
EDIS; // This is needed to disable write to EALLOW protected registers
// Step 4. Initialize the Device Peripherals:
// For this example, only initialize the ePWM and ADC
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;
EDIS;
InitEPwm2();
InitEPwm6();
InitializeADC();
//Initialize results buffer
for(resultsIndex = 0; resultsIndex < RESULTS_BUFFER_SIZE; resultsIndex++)
{
AdcaResults[resultsIndex] = 0;
}
resultsIndex = 0;
bufferFull = 0;
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC =1;
EDIS;
// Enable CPU INT3 which is connected to EPWM1-3 INT:
IER |= M_INT3;
// Enable EPWM INTn in the PIE: Group 3 interrupt 1-3
PieCtrlRegs.PIEIER3.bit.INTx2 = 1;
// Enable global Interrupts and higher priority real-time debug events:
EINT; // Enable Global interrupt INTM
ERTM; // Enable Global realtime interrupt DBGM
// Step 6. IDLE loop. Just sit and loop forever (optional):
//take conversions indefinitely in loop
do
{
//wait while ePWM causes ADC conversions, which then cause interrupts,
//which fill the results buffer, eventually setting the bufferFull
//flag
while(!bufferFull);
bufferFull = 0; //clear the buffer full flag
}while(1);
}
/* Interrupt service routine reading the output voltage and generating the PWM */
__interrupt void epwm2_isr(void)
{
AdcaResults[resultsIndex++] = AdcRegs.ADCRESULT0 >>4;
if(RESULTS_BUFFER_SIZE <= resultsIndex)
{
resultsIndex = 0;
bufferFull = 1;
}
// Calculates the duty cycles
EPwm6Regs.CMPA.half.CMPA=AdcaResults[resultsIndex]*ADCgain;
EPwm2Regs.CMPA.half.CMPA=0.5*PRD;
// Clear INT flag for this timer
EPwm2Regs.ETCLR.bit.INT = 1;
// Acknowledge this interrupt to receive more interrupts from group 3
PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;
}
void InitEPwm2()
{
EPwm2Regs.TBPRD = PRD; // Set timer period
EPwm2Regs.TBPHS.half.TBPHS = 0x0000; // Phase is 0
EPwm2Regs.TBCTR = 0x0000; // Clear counter
// Setup TBCLK
EPwm2Regs.ETSEL.bit.SOCAEN = 1; //enable SOCA
EPwm2Regs.ETSEL.bit.SOCASEL = 4; // Select SOC on up-count
EPwm2Regs.ETPS.bit.SOCAPRD = 1; // Generate pulse on 1st event
EPwm2Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN; // Count up
EPwm2Regs.TBCTL.bit.PHSEN = TB_ENABLE; // Disable phase loading
EPwm2Regs.TBCTL.bit.PRDLD = TB_SHADOW;
EPwm2Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;
EPwm2Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm2Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
EPwm2Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
EPwm2Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
EPwm2Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1; // Clock ratio to SYSCLKOUT
EPwm2Regs.TBCTL.bit.CLKDIV = TB_DIV1; // Slow just to observe on the
// scope
// Set actions
EPwm2Regs.AQCTLA.bit.CAU = AQ_CLEAR; // Set PWM2A on Zero
EPwm2Regs.AQCTLA.bit.CAD = AQ_SET;
EPwm2Regs.AQCTLB.bit.CAU = AQ_SET; // Set PWM2A on Zero
EPwm2Regs.AQCTLB.bit.CAD = AQ_CLEAR;
// Active Low complementary PWMs - setup the deadband
EPwm2Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
EPwm2Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;
// EPwm2Regs.DBCTL.bit.IN_MODE = DBA_ALL;
EPwm2Regs.DBRED = 100;
EPwm2Regs.DBFED = 100;
// Interrupt where we will modify the deadband
EPwm2Regs.ETSEL.bit.INTSEL = ET_CTR_ZERO; // Select INT on Zero event
EPwm2Regs.ETSEL.bit.INTEN = 1; // Enable INT
EPwm2Regs.ETPS.bit.INTPRD = ET_1ST; // Generate INT on 1st event
}
void InitEPwm6()
{
EPwm6Regs.TBPRD = PRD; // Set timer period
EPwm6Regs.TBPHS.half.TBPHS = 0x0000; // Phase is 0
EPwm6Regs.TBCTR = 0x0000; // Clear counter
// Setup TBCLK
EPwm6Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN; // Count up
EPwm6Regs.TBCTL.bit.PHSEN = TB_ENABLE; // Disable phase loading
EPwm6Regs.TBCTL.bit.PRDLD = TB_SHADOW;
EPwm6Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;
EPwm6Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm6Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
EPwm6Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
EPwm6Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
EPwm6Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1; // Clock ratio to SYSCLKOUT
EPwm6Regs.TBCTL.bit.CLKDIV = TB_DIV1; // Slow just to observe on the
// scope
// Set actions
EPwm6Regs.AQCTLA.bit.CAU = AQ_CLEAR; // Set PWM2A on Zero
EPwm6Regs.AQCTLA.bit.CAD = AQ_SET;
EPwm6Regs.AQCTLB.bit.CAU = AQ_SET; // Set PWM2A on Zero
EPwm6Regs.AQCTLB.bit.CAD = AQ_CLEAR;
// Active Low complementary PWMs - setup the deadband
EPwm6Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
EPwm6Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;
// EPwm6Regs.DBCTL.bit.IN_MODE = DBA_ALL;
EPwm6Regs.DBRED = 100;
EPwm6Regs.DBFED = 100;
}
void InitializeADC()
{
// Configure ADC
AdcRegs.ADCMAXCONV.all = 0x0001; // Setup 2 conv's on SEQ1
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0; // Setup ADCINA3 as 1st SEQ1 conv.
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x2; // Setup ADCINA2 as 2nd SEQ1 conv.
AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1;// Enable SOCA from ePWM to start SEQ1
AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1; // Enable SEQ1 interrupt (every EOS)
}