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.

TMS320F28377D: Correct sample-time of a ADC

Part Number: TMS320F28377D
Other Parts Discussed in Thread: OPA320, THS4031

Hi community, I got problems to get the data out of a adc in the correct speed-setting. The base of my configuration is the "adc_soc_epwm" (out of the control suite). I want to measure every time-step of 1us (10^-6 s) and save the data, so that I can use CCS7 to watch the data in graph and export to csv-file to save and analyse it later.

I changed the speed of the epwm and set the prescale for adc-clk to 0. The suggested configutration for 1us is:
    EPwm1Regs.CMPA.bit.CMPA = 50;     // Set compare A value
    EPwm1Regs.TBPRD = 99;             // Set period
But by analysing the saved data it seems, that every sample-point is timespaced by round about 8,7us.

Is my configuration wrong? What can I do to improve the speed of the adc and saving the data.

regards basti

  • //###########################################################################
    //
    // FILE:   adc_soc_epwm_cpu01.c
    //
    // TITLE:  ADC triggering via epwm for F2837xD.
    //
    //! \addtogroup cpu01_example_list
    //! <h1> ADC ePWM Triggering (adc_soc_epwm)</h1>
    //!
    //! This example sets up the ePWM to periodically trigger the ADC.
    //!
    //! After the program runs, the memory will contain:\n
    //! - \b AdcaResults \b: A sequence of analog-to-digital conversion samples from
    //! pin A0. The time between samples is determined based on the period
    //! of the ePWM timer.
    //
    //###########################################################################
    // $TI Release: F2837xD Support Library v210 $
    // $Release Date: Tue Nov  1 14:46:15 CDT 2016 $
    // $Copyright: Copyright (C) 2013-2016 Texas Instruments Incorporated -
    //             http://www.ti.com/ ALL RIGHTS RESERVED $
    //###########################################################################
    
    //
    // Included Files
    //
    #include "F28x_Project.h"
    
    //
    // Function Prototypes
    //
    void ConfigureADC(void);
    void ConfigureEPWM(void);
    void SetupADCEpwm(Uint16 channel);
    interrupt void adca1_isr(void);
    
    //
    // Defines
    //
    #define RESULTS_BUFFER_SIZE 1024
    
    
    //
    // Globals
    //
    Uint16 AdcaResults[RESULTS_BUFFER_SIZE];
    Uint16 resultsIndex;
    volatile Uint16 bufferFull;
    
    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.
    //
        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 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();
    
    //
    // Map ISR functions
    //
        EALLOW;
        PieVectTable.ADCA1_INT = &adca1_isr; //function for ADCA interrupt 1
        EDIS;
    
    //
    // Configure the ADC and power it up
    //
        ConfigureADC();
    
    //
    // Configure the ePWM
    //
        ConfigureEPWM();
    
    //
    // Setup the ADC for ePWM triggered conversions on channel 0
    //
        SetupADCEpwm(0);
    
    //
    // Enable global Interrupts and higher priority real-time debug events:
    //
        IER |= M_INT1; //Enable group 1 interrupts
        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;
        bufferFull = 0;
    
    //
    // enable PIE interrupt
    //
        PieCtrlRegs.PIEIER1.bit.INTx1 = 1;
    
    //
    // sync ePWM
    //
        EALLOW;
        CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 1;
    
    //
    //take conversions indefinitely in loop
    //
        do
        {
            //
            //start ePWM
            //
            EPwm1Regs.ETSEL.bit.SOCAEN = 1;  //enable SOCA
            EPwm1Regs.TBCTL.bit.CTRMODE = 0; //unfreeze, and enter up count mode
    
            //
            //wait while ePWM causes ADC conversions, which then cause interrupts,
            //which fill the results buffer, eventually setting the bufferFull
            //flag
            //
            while(!bufferFull);
            bufferFull = 0; //clear the buffer full flag
    
            //
            //stop ePWM
            //
            EPwm1Regs.ETSEL.bit.SOCAEN = 0;  //disable SOCA
            EPwm1Regs.TBCTL.bit.CTRMODE = 3; //freeze counter
    
            //
            //at this point, AdcaResults[] contains a sequence of conversions
            //from the selected channel
            //
    
            //
            //software breakpoint, hit run again to get updated conversions
            //
            asm("            NOP");
        }while(1); //ESTOP0 ); -->          NOP");
    }
    
    //
    // ConfigureADC - 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 = 0; //set ADCCLK divider to /4
        AdcSetMode(ADC_ADCA, ADC_RESOLUTION_12BIT, ADC_SIGNALMODE_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;
    }
    
    //
    // ConfigureEPWM - Configure EPWM SOC and compare values
    //
    void ConfigureEPWM(void)
    {
        EALLOW;
        // Assumes ePWM clock is already enabled
        EPwm1Regs.ETSEL.bit.SOCAEN    = 0;    // Disable SOC on A group
        EPwm1Regs.ETSEL.bit.SOCASEL    = 1;//4;   // Select SOC on up-count
        EPwm1Regs.ETPS.bit.SOCAPRD = 1;       // Generate pulse on 1st event
        EPwm1Regs.CMPA.bit.CMPA = 100;     // Set compare A value to 2048 counts
        EPwm1Regs.TBPRD = 199;             // Set period to 4096 counts
        //EPwm1Regs.TBCTL.bit.CTRMODE = 3;      // freeze counter
        EDIS;
    }
    
    //
    // SetupADCEpwm - Setup ADC EPWM acquisition window
    //
    void SetupADCEpwm(Uint16 channel)
    {
        Uint16 acqps;
    
        //
        //determine minimum acquisition window (in SYSCLKS) based on resolution
        //
        if(ADC_RESOLUTION_12BIT == AdcaRegs.ADCCTL2.bit.RESOLUTION)
        {
            acqps = 14; //75ns
        }
        else //resolution is 16-bit
        {
            acqps = 63; //320ns
        }
    
        //
        //Select the channels to convert and end of conversion flag
        //
        EALLOW;
        AdcaRegs.ADCSOC0CTL.bit.CHSEL = channel;  //SOC0 will convert pin A0
        AdcaRegs.ADCSOC0CTL.bit.ACQPS = acqps; //sample window is 100 SYSCLK cycles
        AdcaRegs.ADCSOC0CTL.bit.TRIGSEL = 5; //trigger on ePWM1 SOCA/C
        AdcaRegs.ADCINTSEL1N2.bit.INT1SEL = 0; //end of SOC0 will set INT1 flag
        AdcaRegs.ADCINTSEL1N2.bit.INT1E = 1;   //enable INT1 flag
        AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; //make sure INT1 flag is cleared
        EDIS;
    }
    
    //
    // adca1_isr - Read ADC Buffer in ISR
    //
    interrupt void adca1_isr(void)
    {
        AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT0;
        if(RESULTS_BUFFER_SIZE <= resultsIndex)
        {
            resultsIndex = 0;
            bufferFull = 1;
        }
    
        AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; //clear INT1 flag
        PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
    }
    
    //
    // End of file
    //
    

  • Hi Basti,

    Unless you are running the CPU at 50MHz or less, setting the ADCCLK divider to /1 will result in garbage ADC results.

    Similarly, you should ensure the ePWM is operating at 100MHz or less and the CPU is operating at 200MHz or less for correct operation.
  • Hello Devin,

    thanks for these tips. Than I´ve got to correct the Prescale for the ADC. Yesterday I recognized that the outputted data seems to be wrong by different signalforms that the ADC has to measure.
    But I thought that a Prescale Ratio of 1 is possible by the folling table 10-10. ADC Timings in 12-bit Mode (SYSCLK Cycles) in the document SPRUHM8G.
    In my Configuration the the CPU is operating at 200MHz, because I need the ePWMs to run with 100MHz.
    And for this configuration the maximum possible clk is 50 MHz for the ADC-Modules.

    regards Basti

  • Hello Devin,
    Is it general possible to get ADC-samples in such a high rate of 1us (~1 MHz)?
    I changed my ADC-Prescaler and tried to measure some sine-waves with different frequencies.
    AdcaRegs.ADCCTL2.bit.PRESCALE = 2;
    (I started by 100 Hz and end by 1kHz by a stepsize of 100 Hz and at the most frequencies the output data in the CCS showes me not the setted up sine-wave.)
    Still this configuration:
    EPwm1Regs.CMPA.bit.CMPA = 100;
    EPwm1Regs.TBPRD = 199;
    For my tests I use a voltage divider to feed a sine-wave-signal between - 3 V and + 3 V to the ADC, scaled to the input-voltage between 0 and + 3.3 V.


    Now I changed back my configuration to the orignal of the project and I get data of the sine-wave that seems to fit to the shape of a sine-wave.
    EPwm1Regs.CMPA.bit.CMPA = 2048;
    EPwm1Regs.TBPRD = 4096;

    1.Is there more I have to pay attention by configuration the ePWM for the ADC?

    2.Or is this configuration the maximum speed?

    3.Is there a other mistake in my Code?
    I only changed some values for the beginnig.

    Thanks in Advance for your help.

    regards Basti

  • Hi Basti,

    AdcaRegs.ADCCTL2.bit.PRESCALE = 2; will result in an ADC clock divider of /2.  You actually want PRESCALE = 6, resulting in a divider of /4.

    The ADC (in 12-bit mode) can run at around 3.5MSPS and in 16-bit mode at around 1.1MSPS, so this is definitely achievable.  

    For maximum speed, you might want to see the adc_soc_continuous example.  This removes the interrupt and directly collects the ADC data as it is generated with conversions setup back-to-back.

    It should be possible to take an ISR at 1MHz, but you might want to be careful that the ISR isn't the limiting factor. 

    As long as your samples are evenly spaced and the input is a sinewave you'll get a sinewave output.  However, if the sample rate is too slow the frequency may appear different due to aliasing.  

    What is the impedance of your voltage divider?  You will usually need to drive the ADC with a high-speed and low noise op-amp for high speed sampling. 

  • Hi Devin,
    //AdcaRegs.ADCCTL2.bit.PRESCALE = 2; will result in an ADC clock divider of / 2. You really want PRESCALE = 6, resulting in a divider of / 4.
    -> Why I want a Prescale = 6 (/ 4)?
    Is there a technical need, because I want to take Samples with 1 MHz? And these Prescale=6 will slow down the ADC to ~8,3 MHz.
    Or did these "wanted Prescale" refers to the comment in the code?

    //It should be possible to take an ISR at 1MHz, but you might want to be careful that the ISR isn't the limiting factor.
    -->This refers to the "adc_soc_continus"?

    //What is the impedance of your voltage divider? You will usually need to drive the ADC with a high-speed and low noise op-amp for high speed sampling.

    --> Vcc
    ---
    |
    +-+
    |    | R1
    +-+
    |
    +---o Uadc
    |
    +-+
    |    | R2
    +-+
    |
    Uin- o---+

    Vcc= 3,3 V; R1=R2= 7,5 kOhm; Uin- =Sine-wave from Generator; Uadc= Voltage at the ADC-Port

    Can you name a example product (not to high priced and easy, would be awesome)?

    Thanks for the other tips and comments. I will take a look at the other example.

    regards Basti

  • Hi Basti,

    Please refer to the technical documentation when setting the device configurations. I'm also pretty sure everything is setup correctly in the examples.

    For the max ADC speed, refer to the datasheet table "ADC Operating Conditions (12-Bit Single-Ended Mode)" which lists the maximum ADCCLK as 50MHz.

    You can see the timings in Datasheet table "ADC Timings in 12-Bit Mode (SYSCLK Cycles)". This shows that PRESCALE = 6 results in ADCCLK = SYSCLK/4.

    Furthermore, you can see in the ADC operating conditions table that the minimum S+H duration is 75ns. Adding this to Teoc from the timings table gives an ADC conversion time of 75ns + 41*5ns = 280ns, which is well within 1us.

    As far as the op-amp, we typically use OPA320 or THS4031, but we are typically using very good op-amps to characterize performance. You should be OK with an op-amp that is ~5MHz BW and has noise, offset, and THD acceptable for your application (5MHz may still necessitate that you increase the S+H a little bit from the minimum of 75ns). If you need more help the precision amplifier e2e forum can help.

    You can still sample the signal w/o the op-amp, but you will probably get some distortion unless you really blow out the S+H time (since your input impedance is 7.5k/2 and the minimum S+H of 75ns is appropriate with ~50 ohms input impedance). This shouldn't affect the signal too bad at the 'eye test' level, so for the sake of your testing it should be OK to use the divider as-is to get the general setup OK (just increase the S+H to something tolerable...e.g. S+H of 295ns should still give total ADC conv. time around 500ns).

    Once you have the ADC setup such that the ADC time < your target trigger period, you next need to setup the ePWM. You should look at the ePWM examples and documentation and get the ePMW to toggle a pin each period in addition to triggering the ADC. You can then verify on a scope that the trigger frequency is indeed the intended 1MHz.

    Note that this assumes that you only want to sample 1 ADC channel per ePWM period. If you want up to 4 ADC conversions, you can do them all in parallel using the 4 ADCs. If you want more than this, you need to setup the ADCs such that one trigger causes more than one SOC. In this case, you need to ensure that ADC time * number of conversions per ADC < (ePWM trigger time + ISR overhead).
  •  Now it works.
    The problems lay at the commands for "updated conversions data" to the PC.

    Thanks for your support.