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.

ADC sampling rate

Other Parts Discussed in Thread: CONTROLSUITE

Hi,

I am using C2000 Launchpad to acquire a waveform through analog channel 1, using EPWM to trigger ADCINT1. 

I modified the existing example program adc_temp_sensor. CPU clock frequency is set at 50 MHz. The value loaded into CMPA register is 50 so that I get an ADC sampling rate of 1MHz.

However, the acquired waveform when plotted in the Graph facility available in CCS looks very distorted.

Kindly help with this issue. Thanks in advance. Following is the code:

****************************

//#############################################################################
//
// File: f2802x_examples_ccsv4/adc_temp_sensor/Example_F2802xAdcTempSensor.c
//
// Title: F2802x ADC Temperature Sensor Example Program.
//
// Group: C2000
// Target Device: TMS320F2802x
//
//! \addtogroup example_list
//! <h1>ADC Temperature Sensor</h1>
//!
//! Interrupts are enabled and the ePWM1 is set up to generate a periodic
//! ADC SOC interrupt - ADCINT1. One channel is converted - ADCINA5, which is internally
//! connected to the temperature sensor.
//!
//! Watch Variables:
//!
//! - TempSensorVoltage[10] Last 10 ADCRESULT0 values
//! - ConversionCount Current result number 0-9
//! - LoopCount Idle loop counter
//
// (C) Copyright 2012, Texas Instruments, Inc.
//#############################################################################
// $TI Release: LaunchPad f2802x Support Library v100 $
// $Release Date: Wed Jul 25 10:45:39 CDT 2012 $
//#############################################################################

//#############################################################################
//
// File: f2802x_examples_ccsv4/adc_temp_sensor/Example_F2802xAdcTempSensor.c
//
// Title: F2802x ADC Temperatrue Sensor Example Program.
//
// Group: C2000
// Target Device: TMS320F2802x
//
//! \addtogroup example_list
//! <h1>ADC Temperatrue Sensor</h1>
//!
//! Interrupts are enabled and the ePWM1 is set up to generate a periodic
//! ADC SOC interrupt - ADCINT1. One channel is converted - ADCINA5, which is internally
//! connected to the temperature sensor.
//!
//! Watch Variables:
//!
//! - TempSensorVoltage[10] Last 10 ADCRESULT0 values
//! - ConversionCount Current result number 0-9
//! - LoopCount Idle loop counter
//
// (C) Copyright 2012, Texas Instruments, Inc.
//#############################################################################
// $TI Release: LaunchPad f2802x Support Library v100 $
// $Release Date: Wed Jul 25 10:45: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"

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

// Global variables used in this example:
uint16_t LoopCount;
uint16_t ConversionCount;
uint16_t Result[50];


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

void main()
{

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));

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

// Perform basic system initialization
WDOG_disable(myWDog);
CLK_enableAdcClock(myClk);
(*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_10, 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

// Step 2. Initialize GPIO:
// This example function is found in the F2802x_Gpio.c file and
// illustrates how to set the GPIO to it's default state.
// InitGpio(); // Skipped for this example

// 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 F2802x_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 F2802x_DefaultIsr.c.
// This function is found in F2802x_PieVect.c.
// InitPieVectTable();
PIE_setDebugIntVectorTable(myPie);
PIE_enable(myPie);

// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file.
EALLOW; // This is needed to write to EALLOW protected register
// PieVectTable.ADCINT1 = &adc_isr;
((PIE_Obj *)myPie)->ADCINT1 = &adc_isr;
EDIS; // This is needed to disable write to EALLOW protected registers

// Step 4. Initialize the ADC:
// This function is found in F2802x_Adc.c
// InitAdc(); // For this example, init the ADC
ADC_enableBandGap(myAdc);
ADC_enableRefBuffers(myAdc);
ADC_powerUp(myAdc);
ADC_enable(myAdc);
ADC_setVoltRefSrc(myAdc, ADC_VoltageRefSrc_Int);

// Step 5. Configure ADC to sample the temperature sensor on ADCIN5:
// The output of Piccolo temperature sensor can be internally connected to the ADC through ADCINA5
// via the TEMPCONV bit in the ADCCTL1 register. When this bit is set, any voltage applied to the external
// ADCIN5 pin is ignored.
// EALLOW;
// AdcRegs.ADCCTL1.bit.TEMPCONV = 1; //Connect internal temp sensor to channel ADCINA5.
// ADC_enableTempSensor(myAdc);
// EDIS;

// Step 6. Continue configuring ADC to sample the temperature sensor on ADCIN5:
// Since the temperature sensor is connected to ADCIN5, configure the ADC to sample channel ADCIN5
// as well as the ADC SOC trigger and ADCINTs preferred. This example uses EPWM1A to trigger the ADC
// to start a conversion and trips ADCINT1 at the end of the conversion.

//Note: The temperature sensor will be double sampled to apply the workaround for rev0 silicon errata for the ADC 1st sample issue
// EALLOW;
// AdcRegs.ADCCTL1.bit.INTPULSEPOS = 1; //ADCINT1 trips after AdcResults latch
ADC_setIntPulseGenMode(myAdc, ADC_IntPulseGenMode_Prior);
// AdcRegs.INTSEL1N2.bit.INT1E = 1; //Enabled ADCINT1
ADC_enableInt(myAdc, ADC_IntNumber_1);
// AdcRegs.INTSEL1N2.bit.INT1CONT = 0; //Disable ADCINT1 Continuous mode
ADC_setIntMode(myAdc, ADC_IntNumber_1, ADC_IntMode_ClearFlag);
// AdcRegs.INTSEL1N2.bit.INT1SEL = 1; //setup EOC1 to trigger ADCINT1 to fire
ADC_setIntSrc(myAdc, ADC_IntNumber_1, ADC_IntSrc_EOC1);
// AdcRegs.ADCSOC0CTL.bit.CHSEL = 5; //set SOC0 channel select to ADCINA5 (which is internally connected to the temperature sensor)
ADC_setSocChanNumber (myAdc, ADC_SocNumber_0, ADC_SocChanNumber_A1);
// AdcRegs.ADCSOC1CTL.bit.CHSEL = 5; //set SOC1 channel select to ADCINA5 (which is internally connected to the temperature sensor) errata workaround
ADC_setSocChanNumber (myAdc, ADC_SocNumber_1, ADC_SocChanNumber_A1);
// AdcRegs.ADCSOC0CTL.bit.TRIGSEL = 5; //set SOC0 start trigger on EPWM1A
ADC_setSocTrigSrc(myAdc, ADC_SocNumber_0, ADC_SocTrigSrc_EPWM1_ADCSOCA);
// AdcRegs.ADCSOC1CTL.bit.TRIGSEL = 5; //set SOC1 start trigger on EPWM1A errata workaround
ADC_setSocTrigSrc(myAdc, ADC_SocNumber_1, ADC_SocTrigSrc_EPWM1_ADCSOCA);
// AdcRegs.ADCSOC0CTL.bit.ACQPS = 6; //set SOC0 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)
ADC_setSocSampleWindow(myAdc, ADC_SocNumber_0, ADC_SocSampleWindow_7_cycles);
// AdcRegs.ADCSOC1CTL.bit.ACQPS = 6; //set SOC1 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1) errata workaround
ADC_setSocSampleWindow(myAdc, ADC_SocNumber_1, ADC_SocSampleWindow_7_cycles);
// EDIS;


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

// Enable ADCINT1 in PIE
// PieCtrlRegs.PIEIER1.bit.INTx1 = 1; // Enable INT 1.1 in the PIE
PIE_enableAdcInt(myPie, ADC_IntNumber_1);
// IER |= M_INT1; // Enable CPU Interrupt 1
CPU_enableInt(myCpu, CPU_IntNumber_10);
// EINT; // Enable Global interrupt INTM
CPU_enableGlobalInts(myCpu);
// ERTM; // Enable Global realtime interrupt DBGM
CPU_enableDebugInt(myCpu);

LoopCount = 0;
ConversionCount = 0;

CLK_enablePwmClock(myClk,PWM_Number_1);
// Assumes ePWM1 clock is already enabled in InitSysCtrl();
// EPwm1Regs.ETSEL.bit.SOCAEN = 1; // Enable SOC on A group
PWM_enableSocAPulse(myPwm);
// EPwm1Regs.ETSEL.bit.SOCASEL = 4; // Select SOC from from CPMA on upcount
// PWM_setSocAPulseSrc(myPwm, PWM_SocPulseSrc_CounterEqualCmpAIncr);
PWM_setSocAPulseSrc(myPwm, PWM_SocPulseSrc_CounterEqualPeriod);
// EPwm1Regs.ETPS.bit.SOCAPRD = 1; // Generate pulse on 1st event
PWM_setSocAPeriod(myPwm, PWM_SocPeriod_FirstEvent);
// EPwm1Regs.CMPA = 0x0080; // Set compare A value
((PWM_Obj *)myPwm)->CMPA = 0x0080;
// EPwm1Regs.TBPRD = 0xFFFF; // Set period for ePWM1
// PWM_setPeriod(myPwm, 0x00FF);
PWM_setPeriod(myPwm, 0x0032);
// EPwm1Regs.TBCTL.bit.CTRMODE = 0; // count up and start
PWM_setCounterMode(myPwm, PWM_CounterMode_Up);
CLK_enableTbClockSync(myClk);
// Wait for ADC interrupt
for(;;)
{
LoopCount++;
}

}


interrupt void adc_isr(void)
{

// TempSensorVoltage[ConversionCount] = AdcResult.ADCRESULT1; //discard ADCRESULT0 as part of the workaround to the 1st sample errata for rev0
Result[ConversionCount] = ADC_readResult(myAdc, ADC_ResultNumber_1);


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

// AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; //Clear ADCINT1 flag reinitialize for next SOC
ADC_clearIntFlag(myAdc, ADC_IntNumber_1);
// PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE
PIE_clearInt(myPie, PIE_GroupNumber_10);

return;
}

  • However, the acquired waveform when plotted in the Graph facility available in CCS looks very distorted.

    You can check the same using DSO by toggling a GPIO. Let us know what you observe.

    Regards,

    Gautam

  • Hi Gautam,

    I did as you suggested. The toggle rate of GPIO pin as observed in an oscilloscope is only about 125 kHz. Why is this happening?

  • Why is this happening?

    The best way of checking the sampling frequency is: generate the PWM signal using some TBPRD value  and then check it on DSO.

    The value loaded into CMPA register is 50 so that I get an ADC sampling rate of 1MHz.

    This is not how one gets 1MHz ADC sampling rate. You've to change the TBPRD value of ePWM module that triggers the ADC.

    Regards,

    Gautam

  • Instead of using CMPA to trigger, I loaded a value of 50 into TBPRD and then configured EPWM to trigger ADCINT when TBCTR matches TBPRD. Even then, I get a toggle frequency of only 125 kHz. Code is shown below:

    PWM_enableSocAPulse(myPwm);

    PWM_setSocAPulseSrc(myPwm, PWM_SocPulseSrc_CounterEqualPeriod);

    PWM_setSocAPeriod(myPwm, PWM_SocPeriod_FirstEvent);

    PWM_setPeriod(myPwm, 0x0032);

    PWM_setCounterMode(myPwm, PWM_CounterMode_Up);

    CLK_enableTbClockSync(myClk);

  • Here's the data for achieving maximum frequency:

    Regards,

    Gautam

  • Nisha,

    Two thoughts:

    It may be easier to put the ePWM output directly on a pin to observe the frequency in addition to / instead of toggling the GPIO in the ISR, especially because the ISR is happening at high frequency so you don't have much room in the ISR before you overflow and start missing interrupts (50MHZ/1MHZ = 50 cycles - 14 cycles of ISR overhead = 36 cycles to finish all ISR work).  

    Once you get this working, you won't have much CPU bandwidth left for anything else.  To reduce the CPU bandwidth needed for sample collection, do the following:

    *Set the ADC in /2 mode and non-overlap mode to avoid the 1st same issue.

    *Set the ADC in 'one-shot' mode.  

    *Setup all 16 SOCs to convert the same channel / use same ACQPS / use same trigger

    *Set an interrupt to occur only at the EOC for SOC15.

    *In the ISR, read result registers 0 through 15 (these will contain 16 successive samples). 

    The above scheme will reduce the ISR frequency by 16 times, giving an ISR frequency of 62.5KHz, which will use significantly less CPU bandwidth than an ISR frequency of 1MHz.

  • Hi Devin,

    Thanks for the reply.

    There is still some issue with the way this is working. Though I have used a circular buffer of size 50 to read the ADC results, my ConversionCount variable inside the ISR continues to increment much beyond 50. The waveform too is not what I expect. Kindly help me. I am attaching the relevant section of the code below:

    *******************************************

    EALLOW;
    AdcRegs.SOCPRICTL.bit.ONESHOT = 1; //One shot mode enabled

    //Last channel number in chain - 1 should be plugged in here

    AdcRegs.SOCPRICTL.bit.RRPOINTER = 15; //Round Robin Pointer to soc15+1=0

    //Highest priority channel number should be used here

    AdcRegs.SOCPRICTL.bit.SOCPRIORITY = 0; //SOC0 is high priority, rest of channels are in round robin mode

    //AdcRegs.ADCCTL1.all = 0;

    AdcRegs.ADCCTL2.all = 0; //Simultaneous sampling enabled for all SOC0-15

    AdcRegs.ADCCTL2.bit.ADCNONOVERLAP = 1; // Enable non-overlap mode

    AdcRegs.ADCCTL2.bit.CLKDIV2EN=1; // Enable /2 for ADC clock

    AdcRegs.ADCCTL1.bit.INTPULSEPOS = 1; // ADCINT1 trips after AdcResults latch

    AdcRegs.INTSEL1N2.bit.INT1E = 1; // Enabled ADCINT1

    AdcRegs.INTSEL1N2.bit.INT1CONT = 0; // Disable ADCINT1 Continuous mode

    //Channel number to generate interrupt should be plugged in here

    AdcRegs.INTSEL1N2.bit.INT1SEL = 15; // setup EOC15 to trigger ADCINT1 to fire

    AdcRegs.ADCSOC0CTL.bit.CHSEL = 1; // set SOC0 channel select to ADCINA1,

    AdcRegs.ADCSOC1CTL.bit.CHSEL = 1; // set SOC1 channel select to ADCINA1,

    AdcRegs.ADCSOC2CTL.bit.CHSEL = 1; // set SOC2 channel select to ADCINA1,

    AdcRegs.ADCSOC3CTL.bit.CHSEL = 1; // set SOC3 channel select to ADCINA1,

    AdcRegs.ADCSOC4CTL.bit.CHSEL = 1; // set SOC4 channel select to ADCINA1,

    AdcRegs.ADCSOC5CTL.bit.CHSEL = 1; // set SOC5 channel select to ADCINA1,

    AdcRegs.ADCSOC6CTL.bit.CHSEL = 1; // set SOC6 channel select to ADCINA1,

    AdcRegs.ADCSOC7CTL.bit.CHSEL = 1; // set SOC7 channel select to ADCINA1,

    AdcRegs.ADCSOC8CTL.bit.CHSEL = 1; // set SOC8 channel select to ADCINA1,

    AdcRegs.ADCSOC9CTL.bit.CHSEL = 1; // set SOC9 channel select to ADCINA1,

    AdcRegs.ADCSOC10CTL.bit.CHSEL = 1; // set SOC10 channel select to ADCINA1,

    AdcRegs.ADCSOC11CTL.bit.CHSEL = 1; // set SOC11 channel select to ADCINA1,

    AdcRegs.ADCSOC12CTL.bit.CHSEL = 1; // set SOC12 channel select to ADCINA1,

    AdcRegs.ADCSOC13CTL.bit.CHSEL = 1; // set SOC13 channel select to ADCINA1,

    AdcRegs.ADCSOC14CTL.bit.CHSEL = 1; // set SOC14 channel select to ADCINA1,

    AdcRegs.ADCSOC15CTL.bit.CHSEL = 1; // set SOC15 channel select to ADCINA1,

    AdcRegs.ADCSOC0CTL.bit.TRIGSEL = 0; // set SOC0 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC1CTL.bit.TRIGSEL = 0; // set SOC1 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC2CTL.bit.TRIGSEL = 0; // set SOC2 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC3CTL.bit.TRIGSEL = 0; // set SOC3 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC4CTL.bit.TRIGSEL = 0; // set SOC4 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC5CTL.bit.TRIGSEL = 0; // set SOC5 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC6CTL.bit.TRIGSEL = 0; // set SOC6 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC7CTL.bit.TRIGSEL = 0; // set SOC7 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC8CTL.bit.TRIGSEL = 0; // set SOC8 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC9CTL.bit.TRIGSEL = 0; // set SOC9 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC10CTL.bit.TRIGSEL = 0; // set SOC10 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC11CTL.bit.TRIGSEL = 0; // set SOC11 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC12CTL.bit.TRIGSEL = 0; // set SOC12 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC13CTL.bit.TRIGSEL = 0; // set SOC13 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC14CTL.bit.TRIGSEL = 0; // set SOC14 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC15CTL.bit.TRIGSEL = 0; // set SOC15 start trigger on ADCTRIG0 - Software only.

    AdcRegs.ADCSOC0CTL.bit.ACQPS = 6; // set SOC0 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC1CTL.bit.ACQPS = 6; // set SOC1 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC2CTL.bit.ACQPS = 6; // set SOC2 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC3CTL.bit.ACQPS = 6; // set SOC3 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC4CTL.bit.ACQPS = 6; // set SOC4 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC5CTL.bit.ACQPS = 6; // set SOC5 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC6CTL.bit.ACQPS = 6; // set SOC6 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC7CTL.bit.ACQPS = 6; // set SOC7 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC8CTL.bit.ACQPS = 6; // set SOC8 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC9CTL.bit.ACQPS = 6; // set SOC9 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC10CTL.bit.ACQPS = 6; // set SOC10 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC11CTL.bit.ACQPS = 6; // set SOC11 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC12CTL.bit.ACQPS = 6; // set SOC12 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC13CTL.bit.ACQPS = 6; // set SOC13 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC14CTL.bit.ACQPS = 6; // set SOC14 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

    AdcRegs.ADCSOC15CTL.bit.ACQPS = 6; // set SOC15 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)


    AdcRegs.ADCSAMPLEMODE.all = 0; //Simultaneous sampling disabled for all channels
    AdcRegs.ADCSOCFRC1.all = 0xFFFF;

    EDIS;

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

    // Enable ADCINT1 in PIE
    // PieCtrlRegs.PIEIER1.bit.INTx1 = 1; // Enable INT 1.1 in the PIE
    PIE_enableAdcInt(myPie, ADC_IntNumber_1);
    // IER |= M_INT1; // Enable CPU Interrupt 1
    CPU_enableInt(myCpu, CPU_IntNumber_10);
    // EINT; // Enable Global interrupt INTM
    CPU_enableGlobalInts(myCpu);
    // ERTM; // Enable Global realtime interrupt DBGM
    CPU_enableDebugInt(myCpu);

    LoopCount = 0;
    ConversionCount = 0;


    // Wait for ADC interrupt
    for(;;)
    {
    LoopCount++;
    }
    }


    interrupt void adc_isr(void)
    {
    ADC_clearIntFlag(myAdc, ADC_IntNumber_1);
    // TempSensorVoltage[ConversionCount] = AdcResult.ADCRESULT1; //discard ADCRESULT0 as part of the workaround to the 1st sample errata for rev0
    Result[ConversionCount++] = AdcResult.ADCRESULT1;
    Result[ConversionCount++] = AdcResult.ADCRESULT2;
    Result[ConversionCount++] = AdcResult.ADCRESULT3;
    Result[ConversionCount++] = AdcResult.ADCRESULT4;
    Result[ConversionCount++] = AdcResult.ADCRESULT5;
    Result[ConversionCount++] = AdcResult.ADCRESULT6;
    Result[ConversionCount++] = AdcResult.ADCRESULT7;
    Result[ConversionCount++] = AdcResult.ADCRESULT8;
    Result[ConversionCount++] = AdcResult.ADCRESULT9;
    Result[ConversionCount++] = AdcResult.ADCRESULT10;
    Result[ConversionCount++] = AdcResult.ADCRESULT11;
    Result[ConversionCount++] = AdcResult.ADCRESULT12;
    Result[ConversionCount++] = AdcResult.ADCRESULT13;
    Result[ConversionCount++] = AdcResult.ADCRESULT14;
    Result[ConversionCount++] = AdcResult.ADCRESULT15;
    // If 50 conversions have been logged, start over

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

    // AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; //Clear ADCINT1 flag reinitialize for next SOC

    // PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE
    PIE_clearInt(myPie, PIE_GroupNumber_10);
    EALLOW;
    AdcRegs.ADCSOCFRC1.all = 0xFFFF;
    EDIS;
    return;
    }

  • Hi Gautam,

    I did refer this document for setting the values in EPWM registers to trigger ADCINT1 but the resulting sampling frequency is way too low than expected. Also tried the workaround suggested by Devin, but again the issue is no conversions are taking place. Kindly help.

    Regards

    Nisha

  • I did refer this document for setting the values in EPWM registers to trigger ADCINT1 but the resulting sampling frequency is way too low than expected.

    Nisha, what I feel is whatever sampling frequency you're able to observe would be maximum limit. That is exactly why even after reducing the TBPRD value you're not able to achieve higher sampling frequency.

    Also, you can achieve maximum sampling rates by using Ping-pong sampling method 7217.ping-pong.c  suggested by Devin in one of the forums.

    For more info check: http://e2e.ti.com/support/microcontrollers/c2000/f/171/t/241498.aspx

    Regards,

    Gautam

  • Nisha,

    I see a couple problems with the code:

    *The reason the array you are putting the results into is overflowing is that you are incrementing the conversion count by more than one each time, but only resetting the count when it is exactly equal to 50 after these increments. Try making the buffer size evenly divisible by 16, maybe 64?  

    *You are incrementing by one each time you store a sample, but also incrementing by one again after the check if equal to 50, so each ISR you have 17 increments but only read 16 samples

    *It looks like you have changed the triggering method to software (maybe just for debug). I think you will want ePWM driven approach. 

  • Hi Devin,

    Thanks for pointing that out. My count variable is now incrementing properly. I have a few doubts:

    • Should ADCRESULT0 always be discarded? In that case, the limiting value of the count variable should be a multiple of 15.
    • I have applied a 1 kHz , 3 V peak-to-peak sine wave to ADCIN1. Using the The waveform still looks distorted. Is this because I have configured the graph properties wrong? In the graph window, I have set the buffer size to 60, Sampling Rate to 1 MHz. Can this be the problem?
    • Would the following approach offer higher sampling frequency than the ISR approach?

    while(1) {

    AdcRegs.ADCSOCFRC1.all = 0xFFFF;
    while(AdcRegs.ADCINTFLG.bit.ADCINT1==0);                                                             AdcRegs.ADCINTFLGCLR.bit.ADCINT1=1;
    Result[ConversionCount++] = AdcResult.ADCRESULT1;
    Result[ConversionCount++] = AdcResult.ADCRESULT2;
    Result[ConversionCount++] = AdcResult.ADCRESULT3;
    Result[ConversionCount++] = AdcResult.ADCRESULT4;
    Result[ConversionCount++] = AdcResult.ADCRESULT5;
    Result[ConversionCount++] = AdcResult.ADCRESULT6;
    Result[ConversionCount++] = AdcResult.ADCRESULT7;
    Result[ConversionCount++] = AdcResult.ADCRESULT8;
    Result[ConversionCount++] = AdcResult.ADCRESULT9;
    Result[ConversionCount++] = AdcResult.ADCRESULT10;
    Result[ConversionCount++] = AdcResult.ADCRESULT11;
    Result[ConversionCount++] = AdcResult.ADCRESULT12;
    Result[ConversionCount++] = AdcResult.ADCRESULT13;
    Result[ConversionCount++] = AdcResult.ADCRESULT14;
    Result[ConversionCount++] = AdcResult.ADCRESULT15;
    if(ConversionCount == 60) ConversionCount=0;
    }

    Regards

    Nisha

  • Hi Gautam,

    Of late, I tried oneshot sampling. Will ping-pong sampling offer higher sampling rates? I need a sampling frequency of atleast 1 MHz. How do I ensure that I achieve this? Is there some way I can find out what my current sampling rate is?

  • Hi

    Could someone help me with the following doubts?

    • Should ADCRESULT0 always be discarded? 
    • Would I get higher sampling frequency than the ISR approach if I use an ADCINT controlled loop?
    • Would ping-pong sampling result in higher sampling frequencies than oneshot ADC? If yes, does this also require non-overlap mode to be enabled and clock to be divided by 2?

    Thanks in advance

    Nisha

  • Is there some way I can find out what my current sampling rate is?

    Refer: http://e2e.ti.com/support/microcontrollers/c2000/f/171/t/221654.aspx

    Regards,

    Gautam

  • Hi Gautam,

    Thanks for the link. Following is the conclusion I arrived at after going through the post.

    If CPU runs at 60 MHz and ADC clock is also set to 60 MHz, then to get an effective sampling rate of 1 MHz, each conversion should be done in 60/1= 60 ADC clocks. The minimum number of clock cycles required for a single conversion is 13 cycles(if my acquisition window is 7 cycles and overlap is enabled).

    If I have set my ADC to sample in oneshot mode, my ADC clock is 30 MHz ( CPU clock/2 ). Then, each conversion should be done in 30 ADC clocks, which again is more than  the minimum no of clocks for a single conversion (20 cycles in non-overlap mode).

    However, neither method is giving me a good sampled waveform. I cannot seem to find out what is going wrong here. 

    Regards

    Nisha

  • Here's However, neither method is giving me a good sampled waveform. I cannot seem to find out what is going wrong here.

    Here's the exact table mentioning the role of ACQPS and giving you the complete processing time:

    Nisha, I've not used ping-pong method personally so I'm unsure how you can check the sampling time. You can try this though: using this method just go for a single ADC conversion -> put it in a while loop -> keep extracting the values -> Put a GPIO toggle command before/after extracting the ADC value.

    while(1)
    {
     Digit1  = AdcResult.ADCRESULT0;
     GpioDataRegs.GPATOGGLE.bit.GPIO31 = 1;
    }

    Regards,

    Gautam

  • Gautam,

    I am sorry I did not follow you. 

    Gautam Iyer said:
    using this method just go for a single ADC conversion -> put it in a while loop

    The ping pong method configures all 16 SOCs to point to one channel and use two ADCINTs to trigger 8 SOCs at a time, right? How do I use this method to do a single conversion?

    Thanks

    Nisha

  • The ping pong method configures all 16 SOCs to point to one channel and use two ADCINTs to trigger 8 SOCs at a time, right? How do I use this method to do a single conversion?

    Yup, I got your point. You won't be able to! So I guess, only theoretical calculations seem to be possible.

  • Nisha,

    • Should ADCRESULT0 always be discarded? 

    No.  If the ADC is set in non-overlap and 30MHz ADCCLK, then no samples need to be discarded.  

    Otherwise, the first sample should be discarded after the ADC has become idle (no pending or active conversions).  This will not necessarily be ADCRESULT0.

    • Would I get higher sampling frequency than the ISR approach if I use an ADCINT controlled loop?

    ISR vs ADCINT flag doesn't determine sampling frequency directly, but will affect how much CPU bandwidth is used. 

    • Would ping-pong sampling result in higher sampling frequencies than oneshot ADC? If yes, does this also require non-overlap mode to be enabled and clock to be divided by 2?

    Yes, you can get up to 4.6MSPS using 'ping-pong' sampling in 60MHz, overlap mode.  Because the ADC is continuously sampling, only the first ADCRESULT0 reading needs to be thrown away.  'Ping-pong' sampling at maximum speed will use most of the CPU bandwidth, whereas 1MHz one-shot sampling will only use a fraction, so you would use the first method for sampling in short bursts, while you would probably use the second method if you need to keep sampling all the time.

    • Overlap vs. Non-overlap conversion timings

    The total time for a conversion in both modes (acquisition window through conversion complete) is the same - about 20 ADCCLK cycles.  In non-overlap mode, the next conversion will start after the 20 ADCCLK cycles, whereas in overlap mode the next conversion will actually start after 13 ADCCLK cycles (next S+H begins while sample is still converting).  For one-shot mode, sampling rate won't be different for one-shot vs non-overlap, but it will make a difference for 'ping-pong' style sampling.  

    • Measuring actual sample rate

    Put a high series resistor (say 10k) between a DC signal source and your ADC channel.  Make sure the capacitance on the ADC pin is not more than 10x or so the ADC S+H capcitor (less than 20pF or so). Put a scope probe on the channel. When the ADC samples, the S+H capacitor will draw charge from the external capacitance, which you should see on the scope. You may need to adjust R and C to improve visibility.

    • However, neither method is giving me a good sampled waveform.

    Can you comment on what you are actually getting based on what you expect?  Is it the correct waveform, but highly distorted, or just garbage?  What signal source are you using?

      

  • Hi Devin,

    Thanks for the reply.

    I am applying a 3 V, 500 Hz sine wave at the input. But no matter which sampling method I use (tried PWM and ISR, ADCINT controlled loop, oneshot sampling), I get a very low sampling frequency, something as low as 4 kHz. I have attached a snapshot of the graph obtained by plotting the values in the result buffer. 

    Regards

    Nisha

  • I think using Gautam's method of toggling a GPIO inside the ISR is a good debug strategy.  Use an oscilloscope to capture both the GPIO toggling and whatever signal you are inputting to the ADC on the same time scale, then compare to your actual conversion results to determine if the sampling rate is off or if the conversion results are bad.

    You may also want to check which SOC is triggering ADCINT1.  I think you will want it to be EOC15, but I didn't see it on a quick look through your code:

    ADC_setIntSrc(myAdc, ADC_IntNumber_1, ADC_IntSrc_EOC15);  

  • Hello All,

    I tried the above code posted by Nisha on my Launchpad and I am getting a toggle rate of 41 Khz with lot of jittering. Can someone please shed some light?

    I am using CCS6.4.6 whenever i compile i get 4 warnings

    Build variable 'XDAIS_CG_ROOT' cannot be resolved.

    entry-point symbol other than "_c_int00" specified: "code_start" Example_F2802xLaunchPadDemo C/C++ Problem

    Invalid project path: Include path not found (\packages\ti\xdais). Example_F2802xLaunchPadDemo pathentry Path Entry Problem

    Not sure if this is affecting and also i read about _c_int00 but i am not able to understand.. I am just ignoring these errors and flashing codes so far they worked but i need to implement atleast 100khz adc sampling with ePWM.

    This toggling rate go down to 27Khz when i am using sci to transmit data

     

  • Hi Srinivasan,

    Reading through the thread, I don't think Nisha ever posted code that was confirmed working, so there is probably some debug needed to get it working.

    It looks like the ADC example provided in ControlSUITE, Example_F2802xAdcSoc.c, is ePWM driven. Why not start with that?
  • Hi Devin,
    Thanks for your reply! I tried that too same issue can't ramp up above. I have another question is it possible to use ADC_SOC (triggered with ePWM1) and ePWM2 together ?
    Is there any example or way to overcome this ?
    I like this ADC_SOC with epwm because it works for my closed loop application! if not i simply will try other methods....

  • It should be possible to use ePWM1 and ePMW2 top each trigger a subset of SOCs. This may result in some SOCs being delayed sometimes if the two triggers are asynchronous or sometimes occur at about the same time.

    If you are having additional trouble when the SCI is also running, it seems like you are running out of CPU bandwidth. You may want to run the communications in the main loop, and let the ISRs run the control loop. If you don't need to run the control loop after every sample, you can also reduce the overhead by using one-shot mode as described further up the thread (or switch to a device like F2806x that has both a higher CPU rate and a DMA).