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: F28335: ADC interrupt / SOCA / ISR execution time / GPIO frequency mismatch

Part Number: TMS320F28335

Dear all,

I implemented voltage and current measurement on ADCINA5 and ADCINB5, respectively using simultaneous mode. The SOCA bit is generated by ePWM1 module, which is in up-count mode with 50% duty cycle. Furthermore, the SOCA bit is generated when COMPA and the time base counter (when incrementing) match.  The PWM waveforms are as expected.

I also get proper ADC readings within the ADC ISR and am therefore now trying to measure the ISR execution time. For that, I set a GPIO at the beginning of the ISR, which I clear at the end of the ISR. However, my measurements show that the ISR is entered much more often than the ePWM is generated. I attached a waveform of the PWM (magenta, 50% duty with approx. 600ns dead band) and the GPIO within the ISR (blue).

I also attached my theoretical waveforms based on my understanding when reading the documents SPRU812A and SPRAAP6A.

Any ideas on what I am doing wrong? Much appreciated.

/*
 * 23. Nov. 2016: Open loop for now - only PWM generation - no phase shift implemented
 * 13. Dec. 2016: Program to flash functionality added
 * 16. Dec. 2016: Phase shift for open loop PWM generation added
 * 04. Jan. 2017: ADC measurements added - to be cont'd
 */

#include "DSP28x_Project.h"

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

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

// Variables
#define DT 100 // Dead time calc: DT = Value/CLKfreq -> 100/150MHz = 666ns
Uint16 phaseshift;
Uint16 ConversionCount;
Uint16 Voltage1[128];
Uint16 Current[128];

void main(void) {

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

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

	// Initialize ePWM
	InitEPwmGpio();

	// 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;
	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();
	PieCtrlRegs.PIEIER1.bit.INTx6 = 1;	// Group 1, bit 6 for ADC
	IER |= M_INT1;						// Sets the interrupt enable bit of group 1
	EINT;								// Enable global interrupts INTM

	ConversionCount = 0;				// Initialize the count

	// Initialize the ADC
	InitAdc();

	phaseshift = 102;	// Initializing the phase shift
	InitialADC();
	InitePWM1();
	InitePWM2();
	InitePWM5();
	InitePWM6();

	/*
	 * 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;
	
	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 128 conversions have been logged, start over
	  if(ConversionCount == 127)
	  {
	     ConversionCount = 0;
	  }
	  else
	  {
	      ConversionCount++;
	  }

	  /*
	   * Calculating the phaseshift between two bridges
	   */
		EPwm5Regs.TBPHS.half.TBPHS = phaseshift;
		EPwm6Regs.TBPHS.half.TBPHS = phaseshift;

		// 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;
}

void InitialADC(void)
{
	// Configure ADC
	AdcRegs.ADCTRL3.bit.ADCCLKPS = 12;	// Set the ADC sampling rate: 25MHz/(2*12+1) = 1MHz
	AdcRegs.ADCTRL1.bit.SEQ_CASC = 0;	// Cascaded mode
	AdcRegs.ADCTRL1.bit.CONT_RUN = 1;	// Continuous conversion 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


	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 = 2;		// 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


	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 = 2;		// 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.
	EPwm5Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN; // 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


	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 = 2;		// 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_SYNC_IN; // 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


	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 = 2;		// Active high complementary

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

  • Hi Alexander,

    I don't think you want the CONT_RUN bit to be set.  I think this is causing the ADC to run continuously once started instead of being triggered once each ePWM event.

    Not directly related to your issue, but you may also want to increase the ADCCLK from 1MHz to a more nominal value like 12.5MHz or 25MHz.  We have an erratum stating that operating the ADC at low clock frequencies can lead to performance issues:

  • Alexander,

    Not sure.  Can you start by moving these lines to immediately before the for() loop:

    PieCtrlRegs.PIEIER1.bit.INTx6 = 1;	// Group 1, bit 6 for ADC
    IER |= M_INT1;						// Sets the interrupt enable bit of group 1
    EINT;								// Enable global interrupts INTM

    The way you have it now, interrupts are active and modifying PWM registers before you've even initialized those peripherals.  I'm wondering if initialization is getting corrupted somehow.

    Regards,

    Richard

  • Hi Devin and Richard,
    thank you for your replies, which helped me solve the issue. I am now getting the waveforms according to the theoretical drawing from my first post. Thank you very much for that.

    I have several questions on this matter, though, to better understand the ADC module.
    1. I set the Cont_Run bit to 0 in order to operate the ADC in start/stop mode, which decreased the frequency for entering the ISR. However, based on my understanding, I thought that entering the ISR would depend on the SOC bit from the ePWM register. Although the ADC would continuously run, the ISR would be entered when the ePWM register sets the SOCA bit, which is set to happen when COMPA and the time base counter match. And this particular scenario happens at the PWM frequency, hence I would expect the ISR frequency to be the same as the PWM independently on the setting of CONT_RUN. Apparently, my understanding is not completely correct, so could you please elaborate on my thoughts?

    2. I increased the ADCCLK to 8.33MHz (as opposed to initially 1MHz) which further improved my ADC performance. I would have never guessed to look at the ADCCLK, so thanks for the hint. However, I chose 1MHz for a reason. I am using your controlCard, whose schematic shows an RC filter of 56Ohm and 3.3nF. This yields a time constant of 184ns, hence the ADC pin on the DSP sees the full measured voltage at 5*tau=5*184ns=924ns. To my understanding, I need to set the ADC frequency low enough to satisfy these 924ns. That is at approx. 1MHz, unless my understanding and calculations are completely off. In fact, my thoughts are confirmed because increasing the ADCCLK to 25MHz gives me wrong values in the ADC measurements. So I guess I need to be as low as possible to account for the RC filter. Is my understanding correct on this particular issue? It would be helpful if you could elaborate on that as well.

    All the best,
    Alexander
  • 1. I think what is happening with CONT_RUN set is that the first ePWM trigger starts the ADC running, and then it just keeps looping around the sequencer as fast as it can, hence the faster interrupts which aren't correlated with the ePWM frequency. With the bit cleared, each trigger starts a set of conversions, and the ADC stops when it finishes one sequence. You then issue some instructions at the end of the ISR to reset the sequencer.

    2. You are correct here that you need to consider the ADC input settling time, and ADCCLK is one way to control it.

    The calculations here are as follows:

    If the external capacitor is greater than 4096*4*Cadc, then all the external charge comes from the external capacitor to settle the input to within 1/4LSBs and we can ignore the external RC time constant and just use the minimum S+H window duration. (We can use the minimum S+H time, but need to limit the sample rate to prevent the external capacitor from being slowly drained from each sampling event.) This would be at least 4096*4*11.64pF = 191nF. (for internal ADC C values, see DS Figure 5-38. ADC Analog Input Impedance Model). This cap is too small for this, so we use the alternate calculation...

    The RC time constant for the system can be approximated as (Rs+Ron)*Ch + Rs*Cp where
    Rs = 56
    Cp = 3.3nF + 10pF = 3.3nF
    Ch = 1.64pF
    Ron = 1kohm
    So tau = 187ns (pretty much what you have, just slightly higher due to internal RC effects)

    We typically shoot for 1/4 LSBs settling and the resolution is 12-bits, so we need -ln(0.25*(1/4096) = 9.7 taus

    S+H time should therefore be 1.814us

    To control the S+H time, you can adjust both the ADCCLK and the ACQ_PS value. ACQ_PS only adjusts the S+H time and not the rest of the conversion time, so this is the preferable way to go. The maximum value of ACQ_PS is 15, which yields a S+H time of 16 ADCCLKs. This means the ADCCLK needs to be at most (1/1.814ns)*16 = 8.8MHz. Therefore you should be good at 8.333MHz ADCCLK, just make sure to set ACQ_PS to max (if it isn't already).