Because of the holidays, TI E2E™ design support forum responses will be delayed from Dec. 25 through Jan. 2. Thank you for your patience.

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.

CCS/LAUNCHXL-F28379D: ADC does not appear to sample as often as expected

Part Number: LAUNCHXL-F28379D
Other Parts Discussed in Thread: CONTROLSUITE

Tool/software: Code Composer Studio

Hello,

I am using the LAUNCHXL-F28379D microcontroller EVM along with an AD9833 waveform generator. I am currently at the stage where I am just trying to make sure that my ADC can effectively sample the sine waves that I am generating with the AD9833 but have run into some trouble. I am essentially just using the adc_soc_continuous_cpu01 example from ControlSuite combined with some of the SPI commands from the spi_loopback_cpu01 example. I know that my SPI communication with the waveform generator is operating properly because when I command a 5kHz signal, I can see the following on an Oscilloscope:

(note that the Oscilloscope probe is set to 1:10 amplification and the actual peak-peak value is only .66V)

Essentially when I try to return the same plot using the ADC, the waveform I get is a mess. I believe I have all my settings correct to guarantee the highest sampling rate I can (SYSCLK = 200MHz, ADCCLK = 50MHz) but from what I can tell, it appears that the ADC is sampling much less frequently than that, since it only takes about 20 samples to capture a full period of the 5kHz waveform.

Below is the code I am using, apologies if it is a bit messy:

//###########################################################################
// FILE:   Example_28X7xSpi_FFDLB.c
// TITLE:  SPI Digital Loop Back program.
//
//! \addtogroup cpu01_example_list
//! <h1>SPI Digital Loop Back</h1>
//!
//!  This program uses the internal loop back test mode of the peripheral.
//!  Other then boot mode pin configuration, no other hardware configuration
//!  is required. Interrupts are not used.
//!
//!  A stream of data is sent and then compared to the received stream.
//!  The sent data looks like this: \n
//!  0000 0001 0002 0003 0004 0005 0006 0007 .... FFFE FFFF \n
//!  This pattern is repeated forever.
//!
//!  \b Watch \b Variables \n
//!  - \b sdata , sent data
//!  - \b rdata , received data
//		
//###########################################################################
// $TI Release: F2837xD Support Library v100 $
// $Release Date: Mon Dec  9 12:58:09 CST 2013 $
//###########################################################################

#include "F28x_Project.h"     // Device Headerfile and Examples Include File
#include "math.h"

//definitions for selecting ADC resolution
#define RESOLUTION_12BIT 0 //12-bit resolution
#define RESOLUTION_16BIT 1 //16-bit resolution (not supported for all variants)
//definitions for selecting ADC signal mode
#define SIGNAL_SINGLE 0 //single-ended channel conversions (12-bit mode only)
#define SIGNAL_DIFFERENTIAL 1 //differential pair channel conversions

//buffer for storing conversion results (size must be multiple of 16)
#define RESULTS_BUFFER_SIZE 256
Uint16 AdcaResults[RESULTS_BUFFER_SIZE];
Uint16 resultsIndex;

// Prototype statements for functions found within this file.
// __interrupt void ISRTimer2(void);
void take_adc_conversion(void);
void ConfigureADC(void);
void SetupADCContinuous(Uint16 channel);
void delay_loop(void);
void spi_xmit(Uint16 a);
void spi_fifo_init(void);
void spi_init(void);
void error(void);
void declareFrequency(double freq);
Uint16 rdata;  // received data
Uint16 sdata1;
Uint16 sdata2;
Uint16 sdata3;
Uint16 sdata4;
Uint16 sdata5;
float refFreq = 25000000.0;
int MSB;
int LSB;
int i;
long freqWord;
float myPower = 268435456.0; //2^28
float desiredFreq = 5000.0;


void main(void)
{

// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the F2837xD_SysCtrl.c file.
   InitSysCtrl();

// Step 2. Initialize GPIO: 
// This example function is found in the F2837xD_Gpio.c file and
// illustrates how to set the GPIO to it's default state.
//This is needed to set ADC GPIOs
   InitGpio();  // Skipped for this example
// Setup only the GP I/O only for SPI-A functionality
// This function is found in F2837xD_Spi.c
   InitSpiaGpio();

// Step 3. Clear all __interrupts and initialize PIE vector table:
// Disable CPU __interrupts 
   DINT;

// Initialize PIE control registers to their default state.
// The default state is all PIE __interrupts disabled and flags
// are cleared.  
// This function is found in the F2837xD_PieCtrl.c file.
   InitPieCtrl();

// Disable CPU __interrupts and clear all CPU __interrupt flags:
   IER = 0x0000;
   IFR = 0x0000;
   
// Initialize the PIE vector table with pointers to the shell Interrupt 
// Service Routines (ISR).  
// This will populate the entire table, even if the __interrupt
// is not used in this example.  This is useful for debug purposes.
// The shell ISR routines are found in F2837xD_DefaultIsr.c.
// This function is found in F2837xD_PieVect.c.
   InitPieVectTable();
	
//Configure the ADC and power it up
   ConfigureADC();

//Setup the ADC for continuous conversions on channel 0
   SetupADCContinuous(3);

//Enable global Interrupts and higher priority real-time debug events:
   EINT;  // Enable Global interrupt INTM
   ERTM;  // Enable Global realtime interrupt DBGM

//Initialize results buffer
   for(resultsIndex = 0; resultsIndex < RESULTS_BUFFER_SIZE; resultsIndex++)
   {
       AdcaResults[resultsIndex] = 0;
   }
   resultsIndex = 0;


// Step 4. Initialize all the Device Peripherals:
// This function is found in F2837xD_InitPeripherals.c
// InitPeripherals(); // Not required for this example
   spi_fifo_init();	  // Initialize the Spi FIFO
   spi_init();		  // init SPI

// Step 5. User specific code:
// Interrupts are not used in this example.
   delay_loop();
   declareFrequency(desiredFreq);
//Take ADC conversions forever for all of time
   do{
       take_adc_conversion();
   }while(1);

} 	

// Step 7. Insert all local Interrupt Service Routines (ISRs) and functions here:	

void declareFrequency(double freq)
{
    //Converts desired frequency into MSB and LSB commands for Freq0 register on AD9833
    freqWord = (freq * myPower) / refFreq;
    MSB = (int)((freqWord & 0xFFFC000) >> 14);    //Only lower 14 bits are used for data
    LSB = (int)(freqWord & 0x3FFF);
    //Set control bits 15 ande 14 to 0 and 1, respectively, for frequency register 0
    LSB |= 0x4000;
    MSB |= 0x4000;


    Uint16 sdata1 = 0x2100; //Resets AD9833
    Uint16 sdata2 = LSB; //Freq0 register write LSBs
    Uint16 sdata3 = MSB; //Freq0 register write MSBs
    Uint16 sdata4 = 0xC000; //Phase register write
    Uint16 sdata5 = 0x2000; //Release reset
    for(i = 1; i < 6; i++)
       {
         // Transmit data
         spi_xmit(sdata1);
         // Wait until data is received
         while(SpiaRegs.SPIFFRX.bit.RXFFST !=1) { }
         //while(SpiaRegs.SPISTS.bit.INT_FLAG !=1) {}
         // Check against sent data
         rdata = SpiaRegs.SPIRXBUF;
         if(rdata != sdata1) error();
         sdata1 = sdata2;
         sdata2 = sdata3;
         sdata3 = sdata4;
         sdata4 = sdata5;
         delay_loop();
       }
}

void delay_loop()
{
    long      i;
    for (i = 0; i < 200; i++) {}
}

void error(void)
{
   asm("     ESTOP0");						// Test failed!! Stop!
    for (;;);
}

void spi_init()
{    
	SpiaRegs.SPICCR.all =0x0004F;	             // Reset on, rising edge, 16-bit char bits
	SpiaRegs.SPICTL.all =0x0006;    		    // Enable master mode, normal phase,
	                                            // enable talk, and SPI int disabled.
	SpiaRegs.SPIBRR =0x007F;
	SpiaRegs.SPIPRI.bit.TRIWIRE = 1;             //Enable 3 wire mode
    SpiaRegs.SPICCR.all =0x00DF;		         // Relinquish SPI from Reset
    SpiaRegs.SPIPRI.bit.FREE = 1;                // Set so breakpoints don't disturb xmission
}

void spi_xmit(Uint16 a)
{
    SpiaRegs.SPITXBUF=a;
}    

void spi_fifo_init()										
{
// Initialize SPI FIFO registers
    SpiaRegs.SPIFFTX.all=0xE040;
    SpiaRegs.SPIFFRX.all=0x2044;
    SpiaRegs.SPIFFCT.all=0x0;
}  

void take_adc_conversion()
{
    //enable ADCINT flags
            AdcaRegs.ADCINTSEL1N2.bit.INT1E = 1;
            AdcaRegs.ADCINTSEL1N2.bit.INT2E = 1;
            AdcaRegs.ADCINTSEL3N4.bit.INT3E = 1;
            AdcaRegs.ADCINTSEL3N4.bit.INT4E = 1;
            AdcaRegs.ADCINTFLGCLR.all = 0x000F;

            //initialize results index
            resultsIndex = 0;
            //software force start SOC0 to SOC7
            AdcaRegs.ADCSOCFRC1.all = 0x00FF;
            //keep taking samples until the results buffer is full
            while(resultsIndex < RESULTS_BUFFER_SIZE)
            {
                //wait for first set of 8 conversions to complete
                while(0 == AdcaRegs.ADCINTFLG.bit.ADCINT3);
                //clear both INT flags generated by first 8 conversions
                AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;
                AdcaRegs.ADCINTFLGCLR.bit.ADCINT3 = 1;

                //save results for first 8 conversions
                //
                //note that during this time, the second 8 conversions have
                //already been triggered by EOC6->ADCIN1 and will be actively
                //converting while first 8 results are being saved
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT0;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT1;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT2;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT3;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT4;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT5;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT6;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT7;

                //wait for the second set of 8 conversions to complete
                while(0 == AdcaRegs.ADCINTFLG.bit.ADCINT4);
                //clear both INT flags generated by second 8 conversions
                AdcaRegs.ADCINTFLGCLR.bit.ADCINT2 = 1;
                AdcaRegs.ADCINTFLGCLR.bit.ADCINT4 = 1;

                //save results for second 8 conversions
                //
                //note that during this time, the first 8 conversions have
                //already been triggered by EOC14->ADCIN2 and will be actively
                //converting while second 8 results are being saved
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT8;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT9;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT10;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT11;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT12;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT13;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT14;
                AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT15;
            }

            //disable all ADCINT flags to stop sampling
            AdcaRegs.ADCINTSEL1N2.bit.INT1E = 0;
            AdcaRegs.ADCINTSEL1N2.bit.INT2E = 0;
            AdcaRegs.ADCINTSEL3N4.bit.INT3E = 0;
            AdcaRegs.ADCINTSEL3N4.bit.INT4E = 0;

            //at this point, AdcaResults[] contains a sequence of conversions
            //from the selected channel

            //software breakpoint, hit run again to get updated conversions
            //asm("   ESTOP0");
}

//Write ADC configurations and power up the ADC for both ADC A and ADC B
void ConfigureADC(void)
{
    EALLOW;

    //write configurations
    AdcaRegs.ADCCTL2.bit.PRESCALE = 6; //set ADCCLK divider to /4
    AdcaRegs.ADCCTL2.bit.RESOLUTION = RESOLUTION_12BIT;
    AdcaRegs.ADCCTL2.bit.SIGNALMODE = SIGNAL_SINGLE;

    //Set pulse positions to late
    AdcaRegs.ADCCTL1.bit.INTPULSEPOS = 1;

    //power up the ADC
    AdcaRegs.ADCCTL1.bit.ADCPWDNZ = 1;

    //delay for 1ms to allow ADC time to power up
    DELAY_US(1000);

    EDIS;
}

//setup the ADC to continuously convert on one channel
void SetupADCContinuous(Uint16 channel)
{
    Uint16 acqps;

    //determine minimum acquisition window (in SYSCLKS) based on resolution
    if(RESOLUTION_12BIT == AdcaRegs.ADCCTL2.bit.RESOLUTION){
        acqps = 14; //75ns
    }
    else { //resolution is 16-bit
        acqps = 63; //320ns
    }

    EALLOW;
    AdcaRegs.ADCSOC0CTL.bit.CHSEL  = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC1CTL.bit.CHSEL  = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC2CTL.bit.CHSEL  = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC3CTL.bit.CHSEL  = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC4CTL.bit.CHSEL  = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC5CTL.bit.CHSEL  = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC6CTL.bit.CHSEL  = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC7CTL.bit.CHSEL  = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC8CTL.bit.CHSEL  = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC9CTL.bit.CHSEL  = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC10CTL.bit.CHSEL = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC11CTL.bit.CHSEL = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC12CTL.bit.CHSEL = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC13CTL.bit.CHSEL = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC14CTL.bit.CHSEL = channel;  //SOC will convert on channel
    AdcaRegs.ADCSOC15CTL.bit.CHSEL = channel;  //SOC will convert on channel

    AdcaRegs.ADCSOC0CTL.bit.ACQPS  = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC1CTL.bit.ACQPS  = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC2CTL.bit.ACQPS  = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC3CTL.bit.ACQPS  = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC4CTL.bit.ACQPS  = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC5CTL.bit.ACQPS  = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC6CTL.bit.ACQPS  = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC7CTL.bit.ACQPS  = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC9CTL.bit.ACQPS  = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC10CTL.bit.ACQPS = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC11CTL.bit.ACQPS = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC12CTL.bit.ACQPS = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC13CTL.bit.ACQPS = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC14CTL.bit.ACQPS = acqps;    //sample window is acqps + 1 SYSCLK cycles
    AdcaRegs.ADCSOC15CTL.bit.ACQPS = acqps;    //sample window is acqps + 1 SYSCLK cycles

    AdcaRegs.ADCINTSEL1N2.bit.INT1E = 0; //disable INT1 flag
    AdcaRegs.ADCINTSEL1N2.bit.INT2E = 0; //disable INT2 flag
    AdcaRegs.ADCINTSEL3N4.bit.INT3E = 0; //disable INT3 flag
    AdcaRegs.ADCINTSEL3N4.bit.INT4E = 0; //disable INT4 flag

    AdcaRegs.ADCINTSEL1N2.bit.INT1CONT = 0;
    AdcaRegs.ADCINTSEL1N2.bit.INT2CONT = 0;
    AdcaRegs.ADCINTSEL3N4.bit.INT3CONT = 0;
    AdcaRegs.ADCINTSEL3N4.bit.INT4CONT = 0;

    AdcaRegs.ADCINTSEL1N2.bit.INT1SEL = 6;  //end of SOC6 will set INT1 flag
    AdcaRegs.ADCINTSEL1N2.bit.INT2SEL = 14; //end of SOC14 will set INT2 flag
    AdcaRegs.ADCINTSEL3N4.bit.INT3SEL = 7;  //end of SOC7 will set INT3 flag
    AdcaRegs.ADCINTSEL3N4.bit.INT4SEL = 15; //end of SOC15 will set INT4 flag

    //ADCINT2 will trigger first 8 SOCs
    AdcaRegs.ADCINTSOCSEL1.bit.SOC0 = 2;
    AdcaRegs.ADCINTSOCSEL1.bit.SOC1 = 2;
    AdcaRegs.ADCINTSOCSEL1.bit.SOC2 = 2;
    AdcaRegs.ADCINTSOCSEL1.bit.SOC3 = 2;
    AdcaRegs.ADCINTSOCSEL1.bit.SOC4 = 2;
    AdcaRegs.ADCINTSOCSEL1.bit.SOC5 = 2;
    AdcaRegs.ADCINTSOCSEL1.bit.SOC6 = 2;
    AdcaRegs.ADCINTSOCSEL1.bit.SOC7 = 2;

    //ADCINT1 will trigger second 8 SOCs
    AdcaRegs.ADCINTSOCSEL2.bit.SOC8 = 1;
    AdcaRegs.ADCINTSOCSEL2.bit.SOC9 = 1;
    AdcaRegs.ADCINTSOCSEL2.bit.SOC10 = 1;
    AdcaRegs.ADCINTSOCSEL2.bit.SOC11 = 1;
    AdcaRegs.ADCINTSOCSEL2.bit.SOC12 = 1;
    AdcaRegs.ADCINTSOCSEL2.bit.SOC13 = 1;
    AdcaRegs.ADCINTSOCSEL2.bit.SOC14 = 1;
    AdcaRegs.ADCINTSOCSEL2.bit.SOC15 = 1;
}
//===========================================================================
// No more.
//===========================================================================

Any tips or advice at all would be appreciated. Thank you!

Austin Allen

  • I don't think you want to repeatedly call take_adc_conversion() function to force the conversions using ADCSOCFRC. Doing the triggering in software might be introducing some variability in the timing. If you turn on continuous mode (INT1CONT, INT2CONT) the interrupts themselves will continuously trigger conversions and you'll only need to use ADCSOCFRC once to kick off the the initial 8 conversions.

    Whitney

  • Thank you for the response!

    I tried turning on continuous mode by setting INT1CONT and INT2CONT to 1 and then pulled take_adc_conversion() out of the while loop so that it only runs once to kick things off. Now it appears that I am only filling the data buffer once and then stopping. 

    I also tried taking out the ADSOCFRC from the take_adc_conversion() and just running it once at the start but repeatedly running take_adc_conversion(). That just gave me the same issue I started with.

    Any other suggestions? Or registers I could monitor to figure out what's going on?

    Best

    Austin

  • You'll need to break take_adc_conversion into pieces I think--the part where you do the initialization (like enabling the interrupts and setting the ADCSOCFRC) will only need to be called once outside of the while(1) and the part you check the interrupt flags, fill up the buffer, and reset the buffer index when it gets too full will stay inside the loop.

    The part where you disable the interrupts can be commented out since I'm guessing you'll want to keep filling and refilling the buffer indefinitely?

    Whitney

  • Okay that makes sense. I'll try that out!

    Should I still manually clear the interrupt flags after waiting for the conversions to complete? 

    Best

    Austin

  • Definitely clear 3 and 4 since you're using them to check whether or not the conversions are finished. As long as you have continuous mode turned on for 1 and 2 though, it shouldn't matter whether or not you clear 1 and 2.

    Whitney

  • Thank you for the advice! That worked pretty well but I ended up going with an EPWM trigger for the ADC instead and that seemed to make everything better. 

    Best

    Austin