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.

TMS320F28335 gives invalid A/D result

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

  • Ok now it works, it seems that the A/D clock setup was not done correctly. I added a following line

    AdcRegs.ADCTRL3.bit.ADCCLKPS             = 1;

    which now sets the AD module clock to 12.5 MHz. After this, the conversion results are in agreement with the DC input voltage! Now I'll move on to implementing the PID controller. Hopefully someone will find this post useful if problems with A/D conversion arise.

    - Juha

  • Juha:

     

    My question is for another subject.  Do you have the PDF file for "Exercise 5: Buck converter output inductor design?"

    C Potter

    cwptt@yahoo.com

     

  • Hello Charles,

     

    I do have the mentioned PDF file explaining the spreadsheet you obviously found from the course website. However, due to our policies I'm not allowed to distribute the file unless you take part in the course. I'm sure that you find the necessary information from the spreadsheet with some 'reverse engineering', as there are all vital equations within the corresponding cells! I'm happy to help you in any way I'm allowed to so feel free to ask more!

    Best regards,

    Juha Huusari, firstname.lastname (a) tut.fi