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.

Question on ADC on TMS320F28027

Hi all,

I am using the C2000 launchpad and have a working ADC code, however I would like to refine and need to be sure I am interpreting the actions correctly.  I have been reading the ADC and comparator application note spruge5f.pdf.  I have some bits of my code below to illustrate my setup.

The ADC is triggered by the ePWM, which is running at 15kHz, I have the following PWM functions enabled:

    // Enables the pulse width modulation (PWM) start of conversion (SOC) A pulse generation
    PWM_enableSocAPulse(myPwm1);
    // Enables the pulse width modulation (PWM) start of conversion (SOC) A pulse generation
    PWM_setSocAPulseSrc(myPwm1, PWM_SocPulseSrc_CounterEqualCmpAIncr);
    // Sets the pulse width modulation (PWM) start of conversion (SOC) A interrupt period
    PWM_setSocAPeriod(myPwm1, PWM_SocPeriod_SecondEvent);

The ADC code is then setup as follows, I use all 16 Soc's but not added them all here just to keep the code shorter:

void init_ADC(void)
{
	    // Enables the ADC band gap circuit
	    ADC_enableBandGap(myAdc);

	    // Enables the ADC reference buffers circuit
	    ADC_enableRefBuffers(myAdc);

	    // Powers up the ADC
	    ADC_powerUp(myAdc);

	    // Enables the ADC
	    ADC_enable(myAdc);

	    // Sets the voltage reference source
	    ADC_setVoltRefSrc(myAdc, ADC_VoltageRefSrc_Int);

	    // Clear ADCINT1 flag reinitialize for next SOC
	    ADC_clearIntFlag(myAdc, ADC_IntNumber_1);

	    // Enables the specified ADC interrupt
	    PIE_enableAdcInt(myPie, ADC_IntNumber_1);

	    // Enables the ADC interrupt
	    ADC_enableInt(myAdc, ADC_IntNumber_1);

	    // Sets the interrupt pulse generation mode
	    ADC_setIntPulseGenMode(myAdc, ADC_IntPulseGenMode_Prior);		//ADCINT1 trips after AdcResults latch

	    // Enables ADC interrupt
	    ADC_enableInt(myAdc, ADC_IntNumber_1);
	    // Sets the interrupt mode
	    ADC_setIntMode(myAdc, ADC_IntNumber_1, ADC_IntMode_ClearFlag);	// Disable ADCINT1 Continuous mode

	    // Sets the interrupt source
	    ADC_setIntSrc(myAdc, ADC_IntNumber_1, ADC_IntSrc_EOC15);		// Setup EOC15 (all 16 soc's used) to trigger ADCINT1

	    // Sets the start-of-conversion (SOC) channel number
	    ADC_setSocChanNumber (myAdc, ADC_SocNumber_0, ADC_SocChanNumber_A0);
	    ADC_setSocChanNumber (myAdc, ADC_SocNumber_1, ADC_SocChanNumber_A1);
	    ADC_setSocChanNumber (myAdc, ADC_SocNumber_2, ADC_SocChanNumber_A2);

	    // Sets the start-of-conversion (SOC) trigger source
	    ADC_setSocTrigSrc(myAdc, ADC_SocNumber_0, ADC_SocTrigSrc_EPWM1_ADCSOCA);
	    ADC_setSocTrigSrc(myAdc, ADC_SocNumber_1, ADC_SocTrigSrc_EPWM1_ADCSOCA);
	    ADC_setSocTrigSrc(myAdc, ADC_SocNumber_2, ADC_SocTrigSrc_EPWM1_ADCSOCA);

	    // Sets the start-of-conversion (SOC) sample delay
	    ADC_setSocSampleWindow(myAdc, ADC_SocNumber_0, ADC_SocSampleWindow_20_cycles);
	    ADC_setSocSampleWindow(myAdc, ADC_SocNumber_1, ADC_SocSampleWindow_20_cycles);
	    ADC_setSocSampleWindow(myAdc, ADC_SocNumber_2, ADC_SocSampleWindow_20_cycles);

}

Then I read the ADC buffers with the following code, and my question mainly involves this, but the way I have it setup is relevant so included for completeness:

	while(AdcRegs.ADCINTFLG.bit.ADCINT1 == 0){}  // Wait until ADCINT1 is tripped ADC_getIntStatus(myAdc, ADC_IntNumber_1);

    //Voltage1[ConversionCount] = ADC_readResult(myAdc, ADC_ResultNumber_1);
    //Voltage2[ConversionCount] = ADC_readResult(myAdc, ADC_ResultNumber_2);
	ADC0 = AdcResult.ADCRESULT0;	// Input Volts		-	First of 1st sample block
	ADC1 = AdcResult.ADCRESULT1;	// Input Current	-
	ADC2 = AdcResult.ADCRESULT2;	// Output Volts		-
	ADC3 = AdcResult.ADCRESULT3;	// Output Current	-	Last of 1st sample block
	ADC4 = AdcResult.ADCRESULT4;	// Input Volts		-	First of 2nd sample block
	ADC5 = AdcResult.ADCRESULT5;	// Input Current	-
	ADC6 = AdcResult.ADCRESULT6;	// Output Volts		-
	ADC7 = AdcResult.ADCRESULT7;	// Output Current	-	Last of 2nd sample block
	ADC8 = AdcResult.ADCRESULT8;	// Input Volts		-	First of 3rd sample block
	ADC9 = AdcResult.ADCRESULT9;	// Input Current	-
	ADC10 = AdcResult.ADCRESULT10;	// Output Volts		-
	ADC11 = AdcResult.ADCRESULT11;	// Output Current	-	Last of 3rd sample block
	ADC12 = AdcResult.ADCRESULT12;	// Input Volts		-	First of 4th sample block
	ADC13 = AdcResult.ADCRESULT13;	// Input Current	-
	ADC14 = AdcResult.ADCRESULT14;	// Output Volts		-
	ADC15 = AdcResult.ADCRESULT15;	// Output Current	-	Last of 4th sample block

	// Clear ADCINT1 flag reinitialize for next SOC
	AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; 	// Clear ADCINT1 ADC_clearIntFlag(myAdc, ADC_IntNumber_1);

Ok, so my understanding is that the ePWM will trigger a the ADC to capture at 15kHz.  I could set my code to read the ADC buffers at 40kHz, but it would still only read the data from the ADC buffer at 15kHz as my code would be waiting for the line of code below

while(AdcRegs.ADCINTFLG.bit.ADCINT1 == 0){}

and then it would clear this flag and repeat the cycle with the line of code below

AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;

I am pretty sure that is right but if not can someone put me straight?

Cheers,

Ant

  • Hi Ant,

    I think your understanding is correct.  

    Maybe the next step in expanding your code would be to have an interrupt which moves the results out of the results registers (and possibly does some calculations with them) instead of spin-waiting on the interrupt flag bit.

    Another good thing to do would be to verify the ePWM frequency by outputting the ePWM on a pin (if you aren't already).  Depending on the counting mode (specifically up-down mode instead of up) the frequency might not just be ePWM PRD * ePWM clock rate.  Also, it is good to verify the ePWM clock is as expected (no unexpected clock dividers).

    Do be aware that some S+H window settings are invalid (register definition from the ADC users guide copied below).  This won't cause the ADC to quit working completely, but you many not get datasheet specified performance.  I think we need to modify the driver library defines to make this clear.  

      

  • Hi Devin,

    Thanks for the confirmation.

    I have been doing some testing earlier as basically I needed to extend the read time, instead of using 16 soc's I only use 4.  Then I use a for loop to read the ADC buffers, this way I can tailor how long I want to sample for and then average out.  I had some really strange results using a basic Perturb and Observe algorithm before but now it works very well, and all the samples finish in ample time despite doing 1000 reads.  I have calibrated the ADC data to a accurate HP multimeter, and getting near mA accuracy.  I am using 20 cycles for the S/H and used the CCS graphing tool to check the data each time for the best results.  Have put my code below as might help someone else.

    void Data_Update(void)
    {
    	float sum_of_ADC_samples_Array[4]={0};
    	int numberOfSamples = 1000;
    	int i = 0;
    
    	for(i=0;i<numberOfSamples;i++)
    	{
    		while(AdcRegs.ADCINTFLG.bit.ADCINT1 == 0){}
    		sum_of_ADC_samples_Array[0] += AdcResult.ADCRESULT0;;
    		sum_of_ADC_samples_Array[1] += AdcResult.ADCRESULT1;;
    		sum_of_ADC_samples_Array[2] += AdcResult.ADCRESULT2;;
    		sum_of_ADC_samples_Array[3] += AdcResult.ADCRESULT3;;
    		AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;
    	}
    
    	ADC_A0 = sum_of_ADC_samples_Array[0]/1000;
    	ADC_A1 = sum_of_ADC_samples_Array[1]/1000;
    	ADC_A2 = sum_of_ADC_samples_Array[2]/1000;
    	ADC_A3 = sum_of_ADC_samples_Array[3]/1000;
    
    
    	// Multiple by the ADC resolution to convert to the actual read voltage
    	ADC_A0 = (ADC_12bit * ADC_A0);
    	ADC_A1 = (ADC_12bit * ADC_A1);
    	ADC_A2 = (ADC_12bit * ADC_A2);
    	ADC_A3 = (ADC_12bit * ADC_A3);
    
    	// Divide the ADC read voltages by the constant calibrated values to produce a real world decimal value
    	IP_Volt = (ADC_A0 / IP_Volt_Const);
    	IP_Amp = (ADC_A1 / IP_Amp_Const);
    	OP_Volt = (ADC_A2 / OP_Volt_Const);
    	OP_Amp = (ADC_A3 / OP_Amp_Const);
    	// Use the input and output volts and amp readings to calculate power in watts
    	New_PW_In = IP_Volt*IP_Amp;
    	New_PW_Out = OP_Volt*OP_Amp;
    
    }

    Cheers,

    Ant