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.

Achieving 12.5 MSPS on F28335

Other Parts Discussed in Thread: CONTROLSUITE

Hi,

I am using the F28335 experimenters kit to sample a high-frequency signal. The highest frequency component of the signal is 5Mhz so I need to sample with at least 10MSPS. I have writted a short program based on the example adc_seqmodetest included in controlSUITE.

I want to:

1) Sample 1000 ADC values on A0 with 12.5MSPS and store the values in an array

2) Send the array to my PC via SCI

3) repeat

For test purposes I am sampling the serial TX of an arduino that sends '10101010' with 115200 baud. The pulse width is about 10us and with 12.5MSPS there should be approximately 100 samples/pulse. However, I only get 50 samples/pulse (see image below), suggesting that my sample rate is 5MSPS. 

Someone in the forum mentioned that I need to write the sampling loop in assembler to get the necessary performance, but it feels like it should be possible to get 12.5MSPS without assembler programming. Does anyone know how to achieve 12.5MSPS?

Here is my code:

//###########################################################################
//
// Original source by: S.S.
//
// $TI Release: 2833x/2823x Header Files and Peripheral Examples V133 $
// $Release Date: June 8, 2012 $
//###########################################################################

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

// ADC start parameters
#if (CPU_FRQ_150MHZ) // Default - 150 MHz SYSCLKOUT
#define ADC_MODCLK 0x3 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3) = 25.0 MHz
#endif
#if (CPU_FRQ_100MHZ)
#define ADC_MODCLK 0x2 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2) = 25.0 MHz
#endif
#define ADC_CKPS 0x1 // ADC module clock = HSPCLK/2*ADC_CKPS = 25.0MHz/(1*2) = 12.5MHz
#define ADC_SHCLK 0x0 // S/H width in ADC module periods = 0 ADC clocks?
#define AVG 1000 // Average sample limit
#define ZOFFSET 0x00 // Average Zero offset
#define BUF_SIZE 2048 // Sample buffer size

// Prototype statements for SCI functions
void scia_echoback_init(void);
void scia_fifo_init(void);
void scia_xmit(int a);
void scia_msg(char *msg);

// Ruis functions
void printNum(Uint16 num);
void pause(int time);
//char* int2str(Uint16 num);

// Global variable for this example
Uint16 SampleTable[BUF_SIZE];
int printTable[4];

main()
{
Uint16 i;

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

// Specific clock setting for this example:
EALLOW;
SysCtrlRegs.HISPCP.all = ADC_MODCLK; // HSPCLK = SYSCLKOUT/ADC_MODCLK
EDIS;

// Step 2. Initialize GPIO:
// For this example, only init the pins for the SCI-A port.
// This function is found in the DSP2833x_Sci.c file.
InitSciaGpio();

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

// Initialize the 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 DSP2833x_PieCtrl.c file.
InitPieCtrl();

// Disable CPU interrupts and clear all CPU interrupt flags:
IER = 0x0000;
IFR = 0x0000;

// Output on GPIO34
EALLOW;
GpioCtrlRegs.GPBMUX1.bit.GPIO34 = 0; // GPIO
GpioCtrlRegs.GPBDIR.bit.GPIO34 = 1; // output
EDIS;

// For this example, init the ADC
InitAdc();

// Specific ADC setup for this example:
AdcRegs.ADCTRL1.bit.ACQ_PS = ADC_SHCLK;
AdcRegs.ADCTRL3.bit.ADCCLKPS = ADC_CKPS;
AdcRegs.ADCTRL1.bit.SEQ_CASC = 1; // 1 Cascaded mode
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0;
AdcRegs.ADCTRL1.bit.CONT_RUN = 1; // Setup continuous run


// Step 5. User specific code, enable interrupts:

scia_fifo_init(); // Initialize the SCI FIFO
scia_echoback_init(); // Initalize SCI for echoback


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

// Start SEQ1
AdcRegs.ADCTRL2.all = 0x2000;

// Take ADC data and log the in SampleTable array
for(;;)
{
GpioDataRegs.GPBSET.bit.GPIO34 = 1; // GPIO34 is high

//for (i=0; i<AVG; i++)

for(i = AVG;i>0;--i)
{

while (AdcRegs.ADCST.bit.INT_SEQ1== 0) {} // Wait for interrupt
    AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;
    SampleTable[i] =AdcRegs.ADCRESULT0;
}
//msg = "\r\n\n\nSampling complete!\0";
//scia_msg(msg);
for(i = AVG;i>0;--i)
{
SampleTable[i] = SampleTable[i]>>4;
if(i%10 == 0)
scia_msg("\r\n");
else
scia_msg(" ; ");
printNum(SampleTable[i]);
}
scia_msg("\r\n...done!\r\n");

GpioDataRegs.GPBCLEAR.all = 0x4; // GPIO34 is low

pause(100);
}
}

Thanks,

Rui

  • Based on a cursory look at your code, there are a few things that could be issues:

    To achieve 12.5MSPS, you want an ADC clock of 25MHz (not sure if you have this or 12.5MHz) and an ACQ_PS value of 0 (I think you already have this). This results in an ADC clock period of 40ns, an acquisition window width of (ACQ_PS + 1) ADC clocks = 1 clock = 40ns, and a conversion time 1 cycle = 40ns, for a total time between samples = acquisition window + converter time = 80ns.

    You will not necessarily need to write assembly to achieve 12.5MSPS. Right now you only use ADCRESULT0, which will require alot of overhead. To reduce the required CPU bandwidth and ensure continous sampling with no interruptions, you will want to take full advantage of the hardware sequencers.

    I think you will want to do something like: Setup the sequencer in cascaded mode, sequencer override, and continous run, with max conversion set to 7 (8 conversions). Set all 16 chanel selects to the chanel of interest.

    Start the sequence by writing 1 to SOC_SEQ1 bit.

    Start:
    *Poll until INT_SEQ1 bit is set, then clear the bit.
    *Move ADCRESULT0 to ADCRESULT7 to your RAM buffer
    *Poll until INT_SEQ1 bit is set, then clear the bit.
    *Move ADCRESULT8 to ADCRESULT15 to your RAM buffer
    *Goto Start if buffer not full

  • Hi Devin,

    Thanks for your reply!

    I've tried to implement your suggestions but I don't really get it to work. First of all, I dont understand how to set the ADC clock to 25MHz. The ADC clock seems to be set to HSPCLK/2*ADC_CKPS, to avoid zero division the smallest integer value ADC_CKPS can have is 1, which it is set to. HSPCLK is SYSCLKOUT/2*ADC_MODCLK2, SYSCLKOUT is 150MHz and ADC_MODCLK2 is set to 3 which gives HSPCLK a value of 25MHz, to set HSPCLK to 50MHz would require ADC_MODCLK2 to be 1.5 which is not an integer...

    I also tried to setup the sequencer as you suggested. My ADC settings are:

    // ADC start parameters
    #if (CPU_FRQ_150MHZ) // Default - 150 MHz SYSCLKOUT
    #define ADC_MODCLK 0x3 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3) = 25.0 MHz
    #endif
    #if (CPU_FRQ_100MHZ)
    #define ADC_MODCLK 0x2 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2) = 25.0 MHz
    #endif
    #define ADC_CKPS 0x1 // ADC module clock = HSPCLK/2*ADC_CKPS = 25.0MHz/(1*2) = 12.5MHz
    #define ADC_SHCLK 0x0 // S/H width in ADC module periods = 0 ADC clocks
    #define AVG 1000 // Average sample limit
    #define ZOFFSET 0x00 // Average Zero offset
    #define BUF_SIZE 2048 // Sample buffer size

    and

    // Specific ADC setup
    AdcRegs.ADCTRL1.bit.ACQ_PS = ADC_SHCLK;
    AdcRegs.ADCTRL3.bit.ADCCLKPS = ADC_CKPS;
    AdcRegs.ADCTRL1.bit.SEQ_CASC = 1; // 1 Cascaded mode
    AdcRegs.ADCTRL1.bit.CONT_RUN = 1; // Setup continuous run
    AdcRegs.ADCMAXCONV.all = 7; //set max conversion to 7, I think...
    AdcRegs.ADCCHSELSEQ1.all = 0x0; //set all 16 channels on SEQ1 to channel 0, I think...

    and here I copy the ADC values to my array

    for(i = AVG;i>0;--i*16)
    {
    while (AdcRegs.ADCST.bit.INT_SEQ1== 0) {} // Wait for interrupt
    AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;
    SampleTable[i] =AdcRegs.ADCRESULT0;
    SampleTable[i-1] =AdcRegs.ADCRESULT1;
    SampleTable[i-2] =AdcRegs.ADCRESULT2;
    SampleTable[i-3] =AdcRegs.ADCRESULT3;
    SampleTable[i-4] =AdcRegs.ADCRESULT4;
    SampleTable[i-5] =AdcRegs.ADCRESULT5;
    SampleTable[i-6] =AdcRegs.ADCRESULT6;
    SampleTable[i-7] =AdcRegs.ADCRESULT7;

    while (AdcRegs.ADCST.bit.INT_SEQ1== 0) {} // Wait for interrupt
    AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;
    SampleTable[i-8] =AdcRegs.ADCRESULT8;
    SampleTable[i-9] =AdcRegs.ADCRESULT9;
    SampleTable[i-10] =AdcRegs.ADCRESULT10;
    SampleTable[i-11] =AdcRegs.ADCRESULT11;
    SampleTable[i-12] =AdcRegs.ADCRESULT12;
    SampleTable[i-13] =AdcRegs.ADCRESULT13;
    SampleTable[i-14] =AdcRegs.ADCRESULT14;
    SampleTable[i-15] =AdcRegs.ADCRESULT15;
    }

    I'm not sure if this is what you meant. Anyway, when I do this I get about 0.5MSPS which is slower than before. What am I doing wrong?

    Thanks!

    Rui

  • Setting ADCCLKPS = 0 is divide by 1.  In this way you can get ADCLK = HSPCLK = 25MHz.

    For the sampling loop, the code "--i*16" will only result in i being decremented by 1 each time.  Try "i -= 16" or "i = i - 16" or something like...

    for(i = AVG - 1;i>0;)

    while (AdcRegs.ADCST.bit.INT_SEQ1== 0) {} // Wait for interrupt
    AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;
    SampleTable[i--] =AdcRegs.ADCRESULT0;
    SampleTable[i--] =AdcRegs.ADCRESULT1;
    ...
    SampleTable[i--] =AdcRegs.ADCRESULT7;

    while (AdcRegs.ADCST.bit.INT_SEQ1== 0) {} // Wait for interrupt
    AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;
    SampleTable[i--] =AdcRegs.ADCRESULT8;
    SampleTable[i--] =AdcRegs.ADCRESULT9;
    ...
    SampleTable[i--] =AdcRegs.ADCRESULT15;
    }

  • Thanks a lot, setting ADC_CKPS to 0x0 did it. However, I had to set AdcRegs.ADCMAXCONV.all to 15 in order to use ADCRESULT 0-15. If I set AdcRegs.ADCMAXCONV.all to 7 ADCRESULT 8-15 would all be 0. And I only waited for the ADC interrrupt once, i.e. 

    for(i = AVG - 1;i>0;)

    while (AdcRegs.ADCST.bit.INT_SEQ1== 0) {} // Wait for interrupt
    AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;
    SampleTable[i--] =AdcRegs.ADCRESULT0;
    SampleTable[i--] =AdcRegs.ADCRESULT1;
    ...
    SampleTable[i--] =AdcRegs.ADCRESULT15;
    }

    If I waited for the interrupt twice as you suggested the pulse I was sampling looked more strange, I suspect parts of the pulse were overwritten by old data. Is it correct to set AdcRegs.ADCMAXCONV.all to 15 and only wait for one interrupt? 

    Thanks again, your support has been most helpful!

    Rui


  • Rui- what you have done is correct.  When in cascaded mode (AdcRegs.ADCTRL1.bit.SEQ_CASC = 1) you can get up to 16 conversions with one SOC so yes one interrupt is enough, and ADCRESULT 8-15 wont be populated unless you increase ADCMAXCONV  beyond 7.

  • Thanks, I feel more confident using that solution now!