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.

High Speed Sampling ADC with Piccolo Launchpad

Hi,

There is a lot of questions and discussions about this topic but I should admit I got lost in these topics. As a result even I tried %70 of written suggestions I could not find exact solution. Maybe I missed.

I am using Piccolo Launchpad F28027 and I want to sample analog signal at 2-3 Msps. I tried a lot of things for it but I could not exceed 750kSps at all.

Some of trials are:
Trigger with PWM and ADC ISR - Programming to RAM - has problem when buffer set huge and SCI libraries included
Trigger with PWM and ADC ISR - Programming to Flash - #pragma routine for ISR - no problem about memory but I could not be sure ISR is written on RAM, interrupt does not work with high sampling rates

ADCINT Flag controlled loop - Programming to RAM - Max sampling achieved around 750kSps

Common problem of all is that, for example when I stated PWM period to 49 (60 Mhz / 50 = 1.2 Ms should be achieved) 550-600 kSps sampling is achieved (almost half of I want).

I paste code here, modified from ADC_SOC example and added missing lines.
Both ISR and loop versions are on it.

//#############################################################################
//
//  File:   f2802x_examples_ccsv4/adc_soc/Example_F2802xAdcSoc.c
//
//  Title:  F2802x ADC Start-Of-Conversion (SOC) Example Program.
//
//  Group:          C2000
//  Target Device:  TMS320F2802x
//
//! \addtogroup example_list
//!  <h1>ADC Start-Of-Conversion (SOC)</h1>
//!
//!   Interrupts are enabled and the ePWM1 is setup to generate a periodic
//!   ADC SOC - ADCINT1. Two channels are converted, ADCINA4 and ADCINA2.
//!
//!   Watch Variables:
//!
//!   - Voltage1[10] - Last 10 ADCRESULT0 values
//!   - Voltage2[10] - Last 10 ADCRESULT1 values
//!   - ConversionCount - Current result number 0-9
//!   - LoopCount - Idle loop counter
//
//  (C) Copyright 2012, Texas Instruments, Inc.
//#############################################################################
// $TI Release: f2802x Support Library v200 $
// $Release Date: Tue Jul 24 10:01:39 CDT 2012 $
//#############################################################################

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

#include "f2802x_common/include/adc.h"
#include "f2802x_common/include/clk.h"
#include "f2802x_common/include/flash.h"
#include "f2802x_common/include/gpio.h"
#include "f2802x_common/include/pie.h"
#include "f2802x_common/include/pll.h"
#include "f2802x_common/include/wdog.h"
#include "f2802x_headers/include/F2802x_CpuTimers.h"


// Prototype statements for functions found within this file.
interrupt void adc_isr(void);
void Adc_Config(void);

//#pragma CODE_SECTION(adc_isr, "ramfuncs");

// Global variables used in this example:

uint16_t AdcBuft[50];
static uint16_t *AdcBufPtr = AdcBuft;

//Sampling Rate Measurement Variables
uint32_t ctelapsed[2]={0};
long timeelapsed=0;
long adcintcount=0;
long srcorr=0;

int i=0;

ADC_Handle myAdc;
CLK_Handle myClk;
FLASH_Handle myFlash;
GPIO_Handle myGpio;
PIE_Handle myPie;
PWM_Handle myPwm;



void main(void)
{
    
    CPU_Handle myCpu;
    PLL_Handle myPll;
    WDOG_Handle myWDog;
    
    // Initialize all the handles needed for this application    
    myAdc = ADC_init((void *)ADC_BASE_ADDR, sizeof(ADC_Obj));
    myClk = CLK_init((void *)CLK_BASE_ADDR, sizeof(CLK_Obj));
    myCpu = CPU_init((void *)NULL, sizeof(CPU_Obj));
    myFlash = FLASH_init((void *)FLASH_BASE_ADDR, sizeof(FLASH_Obj));
    myGpio = GPIO_init((void *)GPIO_BASE_ADDR, sizeof(GPIO_Obj));
    myPie = PIE_init((void *)PIE_BASE_ADDR, sizeof(PIE_Obj));
    myPll = PLL_init((void *)PLL_BASE_ADDR, sizeof(PLL_Obj));
    myPwm = PWM_init((void *)PWM_ePWM1_BASE_ADDR, sizeof(PWM_Obj));
    myWDog = WDOG_init((void *)WDOG_BASE_ADDR, sizeof(WDOG_Obj));

    InitCpuTimers();

    // Perform basic system initialization
    WDOG_disable(myWDog);
    CLK_enableAdcClock(myClk);
    CLK_enableTbClockSync(myClk);
    CLK_enablePwmClock(myClk, PWM_Number_1);
    (*Device_cal)();
    
    //Select the internal oscillator 1 as the clock source
    CLK_setOscSrc(myClk, CLK_OscSrc_Internal);
    
    // Setup the PLL for x10 /2 which will yield 50Mhz = 10Mhz * 10 / 2
    PLL_setup(myPll, PLL_Multiplier_12, PLL_DivideSelect_ClkIn_by_2);
    
    // Disable the PIE and all interrupts
    PIE_disable(myPie);
    PIE_disableAllInts(myPie);
    CPU_disableGlobalInts(myCpu);
    CPU_clearIntFlags(myCpu);
    
// If running from flash copy RAM only functions to RAM   
#ifdef _FLASH
    memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);
#endif   

      // Initalize GPIO
    // Enable XCLOCKOUT to allow monitoring of oscillator 1
    GPIO_setMode(myGpio, GPIO_Number_18, GPIO_18_Mode_XCLKOUT);
    CLK_setClkOutPreScaler(myClk, CLK_ClkOutPreScaler_SysClkOut_by_1);

    // Setup a debug vector table and enable the PIE
    PIE_setDebugIntVectorTable(myPie);
    PIE_enable(myPie);

    // Register interrupt handlers in the PIE vector table
    PIE_registerPieIntHandler(myPie, PIE_GroupNumber_10, PIE_SubGroupNumber_1, (intVec_t)&adc_isr);

    // Initialize the ADC
    ADC_enableBandGap(myAdc);
    ADC_enableRefBuffers(myAdc);
    ADC_powerUp(myAdc);
    ADC_enable(myAdc);
    ADC_setVoltRefSrc(myAdc, ADC_VoltageRefSrc_Int);

    // Enable ADCINT1 in PIE
    //PIE_enableAdcInt(myPie, ADC_IntNumber_1);
    // Enable CPU Interrupt 1
    CPU_enableInt(myCpu, CPU_IntNumber_10);
    // Enable Global interrupt INTM
    CPU_enableGlobalInts(myCpu);
    // Enable Global realtime interrupt DBGM
    CPU_enableDebugInt(myCpu);

    // Configure ADC
    //Note: Channel ADCINA4  will be double sampled to workaround the ADC 1st sample issue for rev0 silicon errata  
    ADC_setIntPulseGenMode(myAdc, ADC_IntPulseGenMode_Prior);            
    ADC_enableInt(myAdc, ADC_IntNumber_1);                              
    ADC_setIntMode(myAdc, ADC_IntNumber_1, ADC_IntMode_ClearFlag);        
    ADC_setIntSrc(myAdc, ADC_IntNumber_1, ADC_IntSrc_EOC1);            
    ADC_setSocChanNumber (myAdc, ADC_SocNumber_0, ADC_SocChanNumber_A4);  
    ADC_setSocChanNumber (myAdc, ADC_SocNumber_1, ADC_SocChanNumber_A4);  
    ADC_setSocTrigSrc(myAdc, ADC_SocNumber_0, ADC_SocTrigSrc_EPWM1_ADCSOCA); 
    ADC_setSocTrigSrc(myAdc, ADC_SocNumber_1, ADC_SocTrigSrc_EPWM1_ADCSOCA);  

    ADC_setSocSampleWindow(myAdc, ADC_SocNumber_0, ADC_SocSampleWindow_7_cycles);  
    ADC_setSocSampleWindow(myAdc, ADC_SocNumber_1, ADC_SocSampleWindow_7_cycles);  


    // Enable PWM clock
    // Stated at begining
    //CLK_enablePwmClock(myClk, PWM_Number_1);
    
    // Setup PWM
    PWM_enableSocAPulse(myPwm);                                         // Enable SOC on A group
    PWM_setSocAPulseSrc(myPwm, PWM_SocPulseSrc_CounterEqualCmpAIncr); 
    PWM_setSocAPeriod(myPwm, PWM_SocPeriod_FirstEvent);               
    //PWM_setCmpA(myPwm, 0x0080);                                      

    PWM_setClkDiv(myPwm, PWM_ClkDiv_by_1);
    PWM_setCmpA(myPwm, 12);                                              // Set compare A value
    PWM_setPeriod(myPwm, 19);                                            // Set period for ePWM1

    PWM_setHighSpeedClkDiv(myPwm, PWM_HspClkDiv_by_1);
    PWM_setPeriodHr(myPwm, 19);
    PWM_setCmpAHr(myPwm,12);

    PWM_setCounterMode(myPwm, PWM_CounterMode_Up);                 

    // Wait for ADC interrupt
    StartCpuTimer0();
    ctelapsed[0]=ReadCpuTimer0Counter();
    adcintcount=0;

    while(1)
    {
        while (AdcRegs.ADCINTFLG.bit.ADCINT1 == 0);
        //AdcBuft[i]=ADC_readResult(myAdc, ADC_ResultNumber_1);


                *AdcBufPtr++ =     AdcResult.ADCRESULT1;
                if( AdcBufPtr == (AdcBuft + 50) )
                        {
                            AdcBufPtr = AdcBuft;                        // Rewind the pointer to beginning
                        }


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

         adcintcount++;
         i++;

         if(i==49){
         ctelapsed[1]=ReadCpuTimer0Counter();
         timeelapsed=(ctelapsed[0]-ctelapsed[1])/60000000;
         srcorr=adcintcount/timeelapsed;
         i=i-49;
         }

    }

}


interrupt void adc_isr(void)
{
    /*
    //discard ADCRESULT0 as part of the workaround to the 1st sample errata for rev0
    Voltage1[ConversionCount] = ADC_readResult(myAdc, ADC_ResultNumber_1);

    // If 10 conversions have been logged, start over
    if(ConversionCount == 9)
    {
        ConversionCount = 0;
    }
    else ConversionCount++;


     *AdcBufPtr++ = AdcResult.ADCRESULT1;


        if(ConversionCount == 9) {
                    ConversionCount = 0; }
        else ConversionCount++;

        if( AdcBufPtr == (AdcBuft + 10) )
        {
            AdcBufPtr = AdcBuft;                        // Rewind the pointer to beginning
        }



    // Clear ADCINT1 flag reinitialize for next SOC
    ADC_clearIntFlag(myAdc, ADC_IntNumber_1);
    // Acknowledge interrupt to PIE
    PIE_clearInt(myPie, PIE_GroupNumber_10);

    adcintcount++;
*/
    return;
}


If anybody could help, I would be appreciate.
If assembly coding only way to reach 2-3 Msps, could anybody send a starting point, tutorial or example code?
Thank you in advance
  • One way you can sample at max speed is to do ping-pong sampling. This avoids using the epwm for triggering and interrupts for moving results to a buffer (which make it harder, but not impossible, to achieve max speed).

    Ping-pong sampling essentially sets the converter up in such a way that it will automatically convert non-stop.  You then just need to move the samples to a buffer as they are generated until you have the desired number of samples. It also uses the trick of pointing multiple SOCs to the same physical channel, such that the result registers provide some automatic buffering. I have attached some code that illustrates the idea (this is not guaranteed to compile or work without modification).  This is in the old bit-field headerfile structure, but it should be simple enough to port to the new structure.

    Let me know if this works for you.

    void setup_adc()
    {
    	//enable bandgap, clocks, powerup etc.
    	//set all SOCs to point to same channel
    
    	//ADCINT 1 and 2 enabled
    	AdcRegs.INTSEL1N2.bit.INT1E = 1;
    	AdcRegs.INTSEL1N2.bit.INT2E = 1;
    	//ADCINT 1 and 2 wait for confirmation
    	AdcRegs.INTSEL1N2.bit.INT1CONT = 0;
    	AdcRegs.INTSEL1N2.bit.INT2CONT = 0;
        //SOC 6 will trigger ADCINT1
    	AdcRegs.INTSEL1N2.bit.INT1SEL = 6;
    	//SOC 14 will trigger ADCINT2
    	AdcRegs.INTSEL1N2.bit.INT2SEL = 14;
    
    	//ADCINT2 will trigger first 8 SOCs
       	AdcRegs.ADCINTSOCSEL1.bit.SOC0 = 2;
       	AdcRegs.ADCINTSOCSEL1.bit.SOC1 = 2;
       	AdcRegs.ADCINTSOCSEL1.bit.SOC2 = 2;
       	AdcRegs.ADCINTSOCSEL1.bit.SOC3 = 2;
       	AdcRegs.ADCINTSOCSEL1.bit.SOC4 = 2;
       	AdcRegs.ADCINTSOCSEL1.bit.SOC5 = 2;
       	AdcRegs.ADCINTSOCSEL1.bit.SOC6 = 2;
       	AdcRegs.ADCINTSOCSEL1.bit.SOC7 = 2;
    	
    	//ADCINT1 will trigger second 8 SOCs
       	AdcRegs.ADCINTSOCSEL2.bit.SOC8 = 1;
       	AdcRegs.ADCINTSOCSEL2.bit.SOC9 = 1;
       	AdcRegs.ADCINTSOCSEL2.bit.SOC10 = 1;
       	AdcRegs.ADCINTSOCSEL2.bit.SOC11 = 1;
       	AdcRegs.ADCINTSOCSEL2.bit.SOC12 = 1;
       	AdcRegs.ADCINTSOCSEL2.bit.SOC13 = 1;
       	AdcRegs.ADCINTSOCSEL2.bit.SOC14 = 1;
       	AdcRegs.ADCINTSOCSEL2.bit.SOC15 = 1;
    }
    
    void take_samples(Uint16 size, Uint16* buffer)
    {
    	Uint16 index = 0;
    	
    	//enable adcint1 and adcint2 needed for ping-pong sampling
    	AdcRegs.INTSEL1N2.bit.INT1E = 1;
    	AdcRegs.INTSEL1N2.bit.INT2E = 1;
    	
    	AdcRegs.ADCSOCFRC1.all = 0x00FF; //software force SOC pending flag for first 8 SOCs 
    	while(index < sample_size) {
    		while (AdcRegs.ADCINTFLG.bit.ADCINT1 == 0){/*wait and do nothing*/}
    		AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; //clear ADCINT1
    		
    		//ADCINT1 was triggered by the end of SOC6.
    		//Since they are done, read SOC0-SOC7 into buffer 
    		//(this assumes SOC7 will be done converting by 
    		//the time the code reaches it to put in buffer).
    		//Also note that SOC6 triggered ADCINT1 which
    		//then triggered SOC8-SOC15 to be pending.
    		//These newly pending SOCs will begin converting 
    		//immediately after SOC7 finishes.
    		
    		buffer[index++] = AdcResult.ADCRESULT0;
    		buffer[index++] = AdcResult.ADCRESULT1;
    		buffer[index++] = AdcResult.ADCRESULT2;
    		buffer[index++] = AdcResult.ADCRESULT3;
    		buffer[index++] = AdcResult.ADCRESULT4;
    		buffer[index++] = AdcResult.ADCRESULT5;
    		buffer[index++] = AdcResult.ADCRESULT6;
    		buffer[index++] = AdcResult.ADCRESULT7;
    
    		while (AdcRegs.ADCINTFLG.bit.ADCINT2 == 0){/*wait and do nothing*/}
    		AdcRegs.ADCINTFLGCLR.bit.ADCINT2 = 1; //clear ADCINT2
    		
    		//ADCINT2 was triggered by the end of SOC14.
    		//Since they are done, read SOC8-SOC15 into buffer 
    		//(this assumes SOC15 will be done converting by 
    		//the time the code reaches it to put in buffer).
    		//Also note that SOC14 triggered ADCINT2 which
    		//then triggered SOC0-SOC7 to be pending.
    		//These newly pending SOCs will begin converting 
    		//immediately after SOC15 finishes.
    		
    		buffer[index++] = AdcResult.ADCRESULT8;
    		buffer[index++] = AdcResult.ADCRESULT9;
    		buffer[index++] = AdcResult.ADCRESULT10;
    		buffer[index++] = AdcResult.ADCRESULT11;
    		buffer[index++] = AdcResult.ADCRESULT12;
    		buffer[index++] = AdcResult.ADCRESULT13;
    		buffer[index++] = AdcResult.ADCRESULT14;
    		buffer[index++] = AdcResult.ADCRESULT15;
    	}
    
    	//disable adcint1 and adcint2 to STOP the ping-pong sampling
    	AdcRegs.INTSEL1N2.bit.INT1E = 0;
    	AdcRegs.INTSEL1N2.bit.INT2E = 0;
    }
    
      

  • Hello

    How do you get to know the sampling rate?

    Victor
  • Victor,

    With self-triggered ping-pong sampling, you would refer to the ADC timing diagrams in the datasheet to calculate the number of ADC CLKs per conversion.

    For example:

    -Tommy

  • Hello 

    How were the OP get to know the sampling rate of 750ksps instead of 2-3Msps?

    Regards,

    Victor