Other Parts Discussed in Thread: TMS320F28335
Hi all,
I'm working with a digital PID controller for a SMPS and I've got a problem with the A/D converter result. I'm using an eZdsp board with the chip I mentioned and basically I've done nothing more than just added some lines to an example code (Example_2833xEPwmUpDownAQ) so that I have a crude interface with two pushbuttons (for open-loop duty cycle adjust) and the desired functionality - which comprise a PWM output and a A/D converter synchronized to PWM cycle start.
Everything seems to work fine, I get the PWM I want to, even can adjust the duty cycle and the A/D converter fires at the desired point. These I verified with an oscilloscope. The thing is that the actual A/D conversion results are inconsistent and invalid with respect to the A/D input signal (DC voltage). I've read several similar posts on this forum but so far found no breakthrough. Here's my code (beg your pardon for a lengthy post!):
//////////////////////////////////////////////////////////////////////////////
// PMW generation for Buck converter (or any converter with 0 < D < 100%) //
// Open-loop mode, duty cycle altered with two pushbuttons //
// Switching frequency fs = 100 kHz //
// A/D converter synchronized to ePWM1A //
// - Start conversion on beginning of switching cycle //
// - GPIO5 toggled after each completed conversion //
// ------------------------------------------------------------------------ //
// TMS320F28335 with eZdsp evaluation board //
// Boot to SARAM //
// ------------------------------------------------------------------------ //
// Corresponding example file : Example_2833xEPwmUpDownAQ.c //
// ------------------------------------------------------------------------ //
// (c) Juha Huusari, TUT/DEEE //
// juha.huusari (a) tut.fi //
//////////////////////////////////////////////////////////////////////////////
#include "DSP28x_Project.h" // Device Headerfile and Examples Include File
typedef struct
{
volatile struct EPWM_REGS *EPwmRegHandle;
Uint16 EPwm_CMPA_Direction;
Uint16 EPwm_CMPB_Direction;
Uint16 EPwmTimerIntCount;
Uint16 EPwmMaxCMPA;
Uint16 EPwmMinCMPA;
Uint16 EPwmMaxCMPB;
Uint16 EPwmMinCMPB;
}EPWM_INFO;
// Prototype statements for functions found within this file.
interrupt void adc_isr(void);
void InitEPwm1Example(void);
void InitButtons(void);
int ButtonIsPressed(int *delta_duty);
// Global variables used in this example
EPWM_INFO epwm1_info;
Uint16 LoopCount;
Uint16 ConversionCount;
Uint16 Voltage[20];
// Configure the period for the timer
#define EPWM1_TIMER_TBPRD 1500 // 1500 cycles corresponds to 10 us
#define EPWM1_MAX_CMPA 1425 // Dmax = 0.95 -> max. on-time = 9.5 us
#define EPWM1_MIN_CMPA 100 // Dmin = 0.05 -> min. on-time = 0.5 us
void main(void)
{
Uint16 duty = 0;
int delta_duty = 0;
LoopCount = 0;
ConversionCount = 0;
InitSysCtrl();
EALLOW;
#if (CPU_FRQ_150MHZ) // Default - 150 MHz SYSCLKOUT
#define ADC_MODCLK 0x5 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*5) = 15.00 MHz
#endif
#if (CPU_FRQ_100MHZ)
#define ADC_MODCLK 0x4 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*4) = 12.50 MHz
#endif
EDIS;
DINT;
InitPieCtrl();
IER = 0x0000;
IFR = 0x0000;
InitPieVectTable();
InitEPwm1Gpio();
InitButtons(); // Includes also the GPIO5 settings
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
// 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
// Configure ADC
AdcRegs.ADCMAXCONV.all = 0; // Setup 1 conv's on SEQ1
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0; // Setup ADCINA0 as 1st 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)
AdcRegs.ADCTRL1.bit.ACQ_PS = 2; // Define the S/H window width (max=16)
// ADclock period = 66.666 ns
// ACQ_PS = 2 -> window = 200 ns
InitAdc(); // For this example, init the ADC
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;
EDIS;
InitEPwm1Example();
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;
EDIS;
// Load the minimum duty cycle:
duty = EPWM1_MIN_CMPA;
EPwm1Regs.CMPA.half.CMPA = duty;
// Main PWM loop:
while(1)
{
if (ButtonIsPressed(&delta_duty))
{
duty = duty + delta_duty;
// Check that the duty isn't smaller than dmin:
if (duty < EPWM1_MIN_CMPA)
{
duty = EPWM1_MIN_CMPA;
}
// Check that the duty isn't larger than dmax:
if (duty > EPWM1_MAX_CMPA)
{
duty = EPWM1_MAX_CMPA;
}
// Update duty cycle.
EPwm1Regs.CMPA.half.CMPA = duty;
}
asm(" NOP");
}
}
void InitEPwm1Example()
{
// Setup TBCLK
EPwm1Regs.TBPRD = EPWM1_TIMER_TBPRD;// Set timer period
EPwm1Regs.TBPHS.half.TBPHS = 0x0000; // Phase is 0
EPwm1Regs.TBCTR = 0x0000; // Clear Counter 1
// Set Compare values
EPwm1Regs.CMPA.half.CMPA = EPWM1_MAX_CMPA; // Set compare A value
// Setup counter mode
EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP; // Count up
EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;
EPwm1Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1; // Clock ratio
// to SYSCLKOUT
EPwm1Regs.TBCTL.bit.CLKDIV = TB_DIV1;
// Setup shadowing
EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO; // Load on Zero
// Set actions
EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET; // Set PWM1A on start
// of Counter 1
EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR; // Clear PWM1A on event A
// Setup A/D conversion parameters
EPwm1Regs.ETSEL.bit.SOCAEN = 1; // Enable SOC on A group
EPwm1Regs.ETSEL.bit.SOCASEL = 1; // Select SOC from Counter = Zero
EPwm1Regs.ETPS.bit.SOCAPRD = 1; // Generate pulse on 1st event
}
void InitButtons()
{
// This routine initializes two GPIO pins as input for corresponding
// pushbuttons. When the button is not pressed, the pin stays at logical
// high due to pullup resistor. When the button is pressed, the pin is
// pulled to logical low resulting in change in the pin data register.
// There are two pushbuttons connected to connector P8 pins 38 (GPIO12)
// and 39 (GPIO33):
// - Set these pins as input
// - Enable pullup
// - Set pins as GPIO
// Set connector P8 pin 14 (GPIO05):
// - Set as output
// - Disable pullup
// - Set pin as GPIO
EALLOW;
GpioCtrlRegs.GPADIR.bit.GPIO12 = 0; // GPIO12 = input
GpioCtrlRegs.GPBDIR.bit.GPIO33 = 0; // GPIO33 = input
GpioCtrlRegs.GPADIR.bit.GPIO5 = 1; // GPIO05 = output
GpioCtrlRegs.GPAPUD.bit.GPIO12 = 0; // Enable pullup on GPIO0
GpioCtrlRegs.GPBPUD.bit.GPIO33 = 0; // Enable pullup on GPIO1
GpioCtrlRegs.GPAPUD.bit.GPIO5 = 0; // Disable pullup on GPIO05
GpioCtrlRegs.GPAMUX1.bit.GPIO12 = 0; // GPIO12 = GPIO
GpioCtrlRegs.GPBMUX1.bit.GPIO33 = 0; // GPIO33 = GPIO
GpioCtrlRegs.GPAMUX1.bit.GPIO5 = 0; // GPIO05 = GPIO
EDIS;
// Finished initializing.
}
int ButtonIsPressed(int *delta_duty)
{
Uint32 loop = 0;
// The button is pressed when the corresponding bit is low.
// GPIO12 corresponds to duty cycle increase.
if (GpioDataRegs.GPADAT.bit.GPIO12 == 0)
{
// Button is pressed, wait for some time as the switch bounces and read
// the state again.
// NOTE: This delay loop should be realized more elegantly!
for(loop=0;loop<100000;loop++)
{
asm(" NOP");
}
if (GpioDataRegs.GPADAT.bit.GPIO12 == 0)
{
// Confirmed button operation, execute change in duty cycle.
// In this case, the button operation results in increase in duty.
*delta_duty = 1;
return 1;
}
}
if (GpioDataRegs.GPBDAT.bit.GPIO33 == 0)
{
// Button is pressed, wait for some time as the switch bounces and read
// the state again.
// NOTE: This delay loop should be realized more elegantly!
for(loop=0;loop<100000;loop++)
{
asm(" NOP");
}
if (GpioDataRegs.GPBDAT.bit.GPIO33 == 0)
{
// Confirmed button operation, execute change in duty cycle.
// In this case, the button operation results in decrease in duty.
*delta_duty = -1;
return 1;
}
}
return 0;
}
interrupt void adc_isr(void)
{
Voltage[ConversionCount] = AdcRegs.ADCRESULT0 >>4;
// If 20 conversions have been logged, start over
if(ConversionCount == 19)
{
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
// Toggle GPIO05 to visualize A/D conversion completion
GpioDataRegs.GPATOGGLE.bit.GPIO5 = 1;
return;
}
//////////////////////////////////////////////////////////////////////////////
// END //
//////////////////////////////////////////////////////////////////////////////
I've checked the following related issues:
- ADCLO tied to GND @ connector P9
- Tried several different ACQ_PS values without visible effect
- Tried using different A/D clock values, no effect
Here are some results vs. DC input voltage (presented as input / result):
0.5 / 16 , 1.0 / 0, 1.5 / 23 , 2.0 / 55 , 2.5 / 87 , 2.8 / 87.
I would be very happy if someone had some time to look at this!
- Juha