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.

Problem with too much ADC gain

Other Parts Discussed in Thread: TMS320F28335

Hi All,

The ADC module of a TMS320F28335 on an eZdsp borard seems to have too much gain. Using a slightly modified TI example, the ADC value is about 2570 for a 1 V input. This corresponds to saturation at about 1.6 V. I have found that the gain can be controlled using the 7 LSBs of ADCREFSEL. By changing these I can adjust the full-scale value between 1.8 V and 1.1 V. The largest value of 1.8 V is far away from the 3 V given in the ADC manual. Can anyone give some idea on what might be affecting the gain? Thanks for any assistance.

Jim Monte

8712.main.c
#include "DSP28x_Project.h"

#define N_AVG      10000  // Number of values to average
#define BUF_SIZE   4  // Sample buffer size

static void init_adc_custom_cal(void);


main()
{
	Uint16 i;
	Uint32 SampleTable[BUF_SIZE];

	InitSysCtrl();// DSP2833x_SysCtrl.c file; PLL, Watchdog, Peripheral Clocks
	
	DINT;// Disable CPU interrupts

	InitPieCtrl();// DSP2833x_PieCtrl.c file

	IER = 0x0000;// Disable CPU interrupts
	IFR = 0x0000;// Clear all CPU interrupts

	InitPieVectTable();// DSP2833x_PieVect.c file

	//ADC setup for this example
#if 1
	init_adc_custom_cal();// DSP2833x_Adc.c file
#else
	InitAdc();
#endif

	AdcRegs.ADCTRL1.bit.ACQ_PS = 0xF;// acquisition window size
	AdcRegs.ADCTRL1.bit.CPS = 0;
	AdcRegs.ADCTRL3.bit.ADCCLKPS = 1;// ADC clock = 75MHz/(2*3*(0+1))=12.5MHz
	AdcRegs.ADCTRL1.bit.SEQ_CASC = 1;// Cascaded mode
	AdcRegs.ADCTRL3.bit.SMODE_SEL = 0;// Sequential sampling mode 
	AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 3;
	AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0;// ADC input channel: ADCINA0
	AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 1;// ADC input channel: ADCINA0
	AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 2;// ADC input channel: ADCINA0
	AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 3;// ADC input channel: ADCINA0
	AdcRegs.ADCTRL1.bit.CONT_RUN = 1;//  Continuous conversion mode
	AdcRegs.ADCTRL2.all = 0x2000;// Start SEQ1; INT_SEQ1 is set at the end of every SEQ1 sequence

	for ( ; ; ) {
		for (i = 0; i < BUF_SIZE; i++) {
	   		SampleTable[i] = 0;
	    }

		for (i = 0; i < N_AVG; i++) {
			while (AdcRegs.ADCST.bit.INT_SEQ1== 0) {} // Wait for interrupt
			AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;
			SampleTable[0] += AdcRegs.ADCRESULT0 >> 4;
			SampleTable[1] += AdcRegs.ADCRESULT1 >> 4;
			SampleTable[2] += AdcRegs.ADCRESULT2 >> 4;
			SampleTable[3] += AdcRegs.ADCRESULT3 >> 4;
		}

		SampleTable[0] /= N_AVG;
		SampleTable[1] /= N_AVG;
		SampleTable[2] /= N_AVG;
		SampleTable[3] /= N_AVG;
	}
}




static void init_adc_custom_cal(void)
{

    extern void DSP28x_usDelay(Uint32 Count);


	// *IMPORTANT*
	// The ADC_cal function, which  copies the ADC calibration values from TI reserved
	// OTP into the ADCREFSEL and ADCOFFTRIM registers, occurs automatically in the
	// Boot ROM. If the boot ROM code is bypassed during the debug process, the
	// following function MUST be called for the ADC to function according
	// to specification. The clocks to the ADC MUST be enabled before calling this
	// function.
	// See the device data manual and/or the ADC Reference
	// Manual for more information.

    EALLOW;
	SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 1;

	/* Calibration */
#if 1
	AdcRegs.ADCOFFTRIM.all = 505; /* 0:8, 505 from factory */
//	AdcRegs.ADCREFSEL.all = 7490; /* 0:13, 7460 from factory */
	AdcRegs.ADCREFSEL.all = 0x0000; /* 0:13, 7460 from factory */
#else
	asm("  MOVW  DP,   #0x711C >> 6");
	asm("  MOV   @28,  #0x16383");
	asm("  MOV   @29,  #0x511");
#endif

	EDIS;


    // To powerup the ADC the ADCENCLK bit should be set first to enable
    // clocks, followed by powering up the bandgap, reference circuitry, and ADC core.
    // Before the first conversion is performed a 5ms delay must be observed
	// after power up to give all analog circuits time to power up and settle

    // Please note that for the delay function below to operate correctly the
	// CPU_RATE define statement in the DSP2833x_Examples.h file must
	// contain the correct CPU clock period in nanoseconds.

    AdcRegs.ADCTRL3.all = 0x00E0;  // Power up bandgap/reference/ADC circuits
    DELAY_US(5000L);         // Delay before converting ADC channels

	return;
} /* end of function init_adc_custom_cal */



  • Hi Jim,
    Can you check what HISPCLK is being set to in InitSysCtrl()? The max ADCCLK is 25MHz so this pre-scale needs to be set based on your other ADCCLKPS and CPS settings.

    Are you using internal or external reference mode?

    Regards,
    Joe
  • Hi Joe,

    Thanks for the reply. The HISPCP is set to 1, which means it divides the system clock by 1 * 2 if I understood that right, so 150 MHz / 2 = 75 MHz.

    // HISPCP/LOSPCP prescale register settings, normally it will be set to default values

    SysCtrlRegs.HISPCP.all = 0x0001;
    SysCtrlRegs.LOSPCP.all = 0x0002;

    The ADCCLKPS was supposed to be 3 to match the original program that was having an issue with the ADC values (not 1 as shown yesterday). With CPS = 0, that would give a ADC clock frequency of 75 / ((2 * 3) *(1+0)) = 12.5 MHz (matching the comment).

    The sample and hold time is 15+1 times longer = 1.28 us

    The eZdsp board uses the internal voltage reference, and I confirmed that the register selecting the voltage reference is set to select the internal reference.

    I tried many possible combinations of the clock and hold times (some out of spec) and got very different results, even when meeting the clock frequency and hold time requirements. The four ADC channels were connected to a stable power supply voltage set to 1.50 V during all of the testing. The results are averages over 10000 measurement for each data point. Does anyone have any suggestions about what might be happening? I am thinking it must be something simple but just am not seeing it.

    Jim

    4370.main.c
    #include <stdio.h>
    
    #include "DSP28x_Project.h"
    
    #define N_AVG      10000  // Number of values to average
    #define BUF_SIZE   5  // Sample buffer size
    
    static void init_adc_custom_cal(void);
    static void measure(void);
    
    
    
    void main()
    {
    
    	InitSysCtrl();// DSP2833x_SysCtrl.c file; PLL, Watchdog, Peripheral Clocks
    	
    	DINT;// Disable CPU interrupts
    
    	InitPieCtrl();// DSP2833x_PieCtrl.c file
    
    	IER = 0x0000;// Disable CPU interrupts
    	IFR = 0x0000;// Clear all CPU interrupts
    
    	InitPieVectTable();// DSP2833x_PieVect.c file
    
    	//ADC setup for this example
    #if 0
    	init_adc_custom_cal(); /* Custom init of offset and gain */
    #else
    	InitAdc(); /* Standard init of offset and gain */
    #endif
    
    	/* ADC clock frequency:
    	 *
    	 * High-speed peripheral clock (HSPCLK) frequency =
         *	 	System clock frequency / SysCtrlRegs.HISPCP[3:0]
    	 * Note that only the ADC uses the high-speed peripheral clock.
    	 *
    	 *
    	 * ADC clock frequency = HSPCLK / (2 * ADCCLKPS[3:0] * (CPS + 1))
    	 * unless ADCCLKPS[3:0] = 0. Then
    	 * ADC clock frequency = HSPCLK / (1 * (CPS + 1))
    	 *
    	 * Acquisition window size is the corresponding ADC clock period
    	 * times (ACQ_PS + 1)
    	 */
    
    	AdcRegs.ADCTRL1.bit.SEQ_CASC = 1;// Cascaded mode
    	AdcRegs.ADCTRL3.bit.SMODE_SEL = 0;// Sequential sampling mode 
    	AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 3;
    	AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0;// ADC input channel: ADCINA0
    	AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 1;// ADC input channel: ADCINA0
    	AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 2;// ADC input channel: ADCINA0
    	AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 3;// ADC input channel: ADCINA0
    	AdcRegs.ADCTRL1.bit.CONT_RUN = 1;//  Continuous conversion mode
    	AdcRegs.ADCTRL2.all = 0x2000;// Start SEQ1; INT_SEQ1 is set at the end of every SEQ1 sequence
    
    	/* Use ADC to measure voltage */
    	measure();
    	return;
    }    
    
    
    
    /* Measure ADC signals with different parameters */
    #define N_ITER_PER_TEST	5
    static void measure(void)
    {
    	Uint32 msmt0, msmt1, msmt2, msmt3, msmt_ave;
    	Uint16 cps; /* timing */
    	int rc_print = 0; /* Init print return code */
    
    	/* Print header */
    	rc_print = fprintf(stdout,
    		"Average over %lu samples,,,,,,,,,,\n"
    		"CPS,ADCCLKPS,ACQ_PS,ADC_SAMP_FREQ (MHz),ACQ_PERIOD (us),ITER,CH0,CH1,CH2,CH3,AVE\n",
    		(Uint32) N_AVG);
    
    	/* Loops over meaasurement parameters */
    	for (cps = 0; cps <= 1; cps++) { /* Vary CPS between 0 and 1 */
    		Uint16 adcclkps;
    		AdcRegs.ADCTRL1.bit.CPS = cps;
    		for (adcclkps = 0; adcclkps <= 15; adcclkps++) {
    			Uint16 acq_ps;
    			AdcRegs.ADCTRL3.bit.ADCCLKPS = adcclkps;
    			/* Note: starting at 1 because program hung at
    			 * adcclkps=1 acq_ps = 0 */
    			for (acq_ps = 1; acq_ps <= 15; acq_ps++) {
    				Uint16 iter;
    				float freq_temp = adcclkps == 0 ?
    						75.0f : 75.0f / (2 * adcclkps);
    				float freq_samp_mhz = cps == 0 ?
    						freq_temp : freq_temp / 2;
    				float sample_hold_time_us = (1 + acq_ps) / freq_samp_mhz;
    				AdcRegs.ADCTRL1.bit.ACQ_PS = acq_ps; /* acquisition window */
    				
    				for (iter = 1; iter <= N_ITER_PER_TEST; iter++) {
    					Uint16 i;
    
    					msmt0 = msmt1 = msmt2 = msmt3 = 0; /* init accum to 0 */
    
    					for (i = 0; i < N_AVG; i++) {
    						/* Wait for interrupt */
    						while (AdcRegs.ADCST.bit.INT_SEQ1== 0) {}
    
    						AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;
    
    						msmt0 += AdcRegs.ADCRESULT0 >> 4;
    						msmt1 += AdcRegs.ADCRESULT1 >> 4;
    						msmt2 += AdcRegs.ADCRESULT2 >> 4;
    						msmt3 += AdcRegs.ADCRESULT3 >> 4;
    					}
    
    					/* Overall average */
    					msmt_ave = (msmt0 + msmt1 + msmt2 + msmt3) /
    							((Uint32) 4 *  (Uint32) N_AVG); 
    
    					/* Individual results */
    					msmt0 /= N_AVG;
    					msmt1 /= N_AVG;
    					msmt2 /= N_AVG;
    					msmt3 /= N_AVG;
    
    					/* Output results */ 
    					rc_print |=  fprintf(stdout,
    							"%u,%u,%u,%G,%G,%u,%lu,%lu,%lu,%lu,%lu\n",
    							cps, adcclkps, acq_ps,
    							freq_samp_mhz, sample_hold_time_us,
    							iter, msmt0, msmt1, msmt2, msmt3, msmt_ave);
    				} /* end of loop over iterations */
    			} /* end of loop over acq_ps */
    		} /* end of loop over adcclkps */
    	} /* end of loop over ps */
    
    	/* Test for print error */
    	if (rc_print < 0) {
    		(void) fprintf(stderr, "Unable to print data to output file.\n");
    	}
    
    	return;
    } /* end of function measure */
    
    
    
    static void init_adc_custom_cal(void)
    {
    
        extern void DSP28x_usDelay(Uint32 Count);
    
    
    	// *IMPORTANT*
    	// The ADC_cal function, which  copies the ADC calibration values from TI reserved
    	// OTP into the ADCREFSEL and ADCOFFTRIM registers, occurs automatically in the
    	// Boot ROM. If the boot ROM code is bypassed during the debug process, the
    	// following function MUST be called for the ADC to function according
    	// to specification. The clocks to the ADC MUST be enabled before calling this
    	// function.
    	// See the device data manual and/or the ADC Reference
    	// Manual for more information.
    
        EALLOW;
    	SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 1;
    
    	/* Calibration */
    #if 1
    	AdcRegs.ADCOFFTRIM.all = 505; /* 0:8, 505 from factory */
    //	AdcRegs.ADCREFSEL.all = 7490; /* 0:13, 7460 from factory */
    	AdcRegs.ADCREFSEL.all = 0x0000; /* 0:13, 7460 from factory */
    #else
    	asm("  MOVW  DP,   #0x711C >> 6");
    	asm("  MOV   @28,  #0x7460");
    	asm("  MOV   @29,  #0x505");
    #endif
    
    	EDIS;
    
    
        // To powerup the ADC the ADCENCLK bit should be set first to enable
        // clocks, followed by powering up the bandgap, reference circuitry, and ADC core.
        // Before the first conversion is performed a 5ms delay must be observed
    	// after power up to give all analog circuits time to power up and settle
    
        // Please note that for the delay function below to operate correctly the
    	// CPU_RATE define statement in the DSP2833x_Examples.h file must
    	// contain the correct CPU clock period in nanoseconds.
    
        AdcRegs.ADCTRL3.all = 0x00E0;  // Power up bandgap/reference/ADC circuits
        DELAY_US(5000L);         // Delay before converting ADC channels
    
    	return;
    } /* end of function init_adc_custom_cal */
    
    
    
    
    1500mv_adc_msmt.txt

  • Hey Jim,
    Normally I would also point to and want to checkout some HW details particularly around REFP, REFM, and RESEXT but in this case you mentioned ezDSP and I expect a TI design to be known good HW. However if this is happening on only one board out of many or the board is not new and history unknown, you can make sure the caps/resister on those pins are intact and correct value.
    The only other SW thing that comes to mind is the delay after the ADC is enabled before first conversion. Again, since you are using code based on TI examples I presume the InitADC includes the needed DELAY_US, but I have seen the delay function not execute properly if the DSP2833x_usDelay.asm isn't in the project. You could add a noticeable amount of delay (i.e. 1-2 seconds) to make sure it is executing properly. If not, then the BG wont be stable when conversions begin and would cause different conversion results depending on the ADCLK and S/H.
    Again, with TI SW/HW I expect the ADC to perform per the DS and feel it is something simple, as you say, but what else it could be is escaping me at the moment.
    Regards,
    Joe
  • I can do some testing with a different eZdsp board tomorrow. Hopefully that will give some useful information.

  • Looks like it was a hardware issue since the other eZdsp did not have the problem. Thanks for the suggestions.

  • Jim,

    Glad to hear you up an running again and thank you for posting the resolution.

    best regards,

    Joe