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.

TMS320F28069M: Simultaneous Sampling Mode in ADC

Part Number: TMS320F28069M

I have following queries regarding Simultaneous Sampling Mode in ADC:

1. Is there any example which I can refer for using simultaneous sampling mode of ADC

2. In the user guide, under Section 8.6: Simultaneous Sampling Mode, It is written:

Typically in an application it is expected that only the even SOCx of the pair will be used. However, it is possible
to use the odd SOCx instead, or even both. In the latter case, both SOCx triggers will start a conversion.
Therefore, caution is urged as both SOCx's will store their results to the same ADCRESULTx registers, possibly
overwriting each other.

I want to use both even and odd SOCx's. I want to understand how to avoid the data overwriting in ADCRESULTx register.

User Guide Link: www.ti.com/.../spruh18i.pdf

  • Hi Dev,

    Unfortunately there is not an existing example for simultaneous sampling, but I have made some quick modifications to the AdcSoc example to answer your second question. At the bottom of this message, I have included a new .c file that you can use to replace the Example_2806xAdcSoc.c file in the AdcSoc example. 

    With the new file, I have enabled simultaneous sampling for SOC0 and SOC1, both triggered by the same signal from the ePWM. SOC0 samples ADCINA2/ADCINB2 while SOC1 samples ADCINA4/ADCINB4. When either SOC finishes an ADCINA conversion, the result is stored in ADCRESULT0, while ADCINB conversions are stored in ADCRESULT1.

    I have set up an ADC interrupt named adc_isr which is triggered by the EOC1 signal. Using the INTPULSEPOS register, EOC1 is set to trigger adc_isr when the ADCRESULT1 is latched and ready, which is always after ADCRESULT0 is ready. This timing is shown in the Figure 8-27 from the datasheet and in the screenshot below. 

    The adc_isr just takes the result from ADCRESULT0 and puts it into an array called Voltage1, and puts ADCRESULT1 into Voltage2. Since SOC0 and SOC1 will always be triggered at the same time, the results in each 'Voltage' array are alternating from between SOC0 and SOC1. In my example, I have connected the sampled pins from SOC0 to ground, while connecting SOC1 to 3.3V. This makes it so the value in the arrays both alternate between high (~4000) and low (~0) like shown below. 

    In my example, I have connected with jumper wires:

    ADCINA2 => GPIO14 (low) 

    ADCINB2 => GPIO15 (low)

    ADCINA4 => GPIO11 (high)

    ADCINB4 => GPIO12 (high)

    As long as you connect the ADC inputs to the correct high and low voltages, you should see the alternating values in the Voltage1 and Voltage2 arrays. This shows the adc_isr saving the ADC results somewhere else in memory before the ADCRESULT registers are overwritten. 

    Best Regards,
    Ben Collier

    //###########################################################################
    //
    // FILE:   Example_2806xAdcSoc.c
    //
    // TITLE:  ADC Start of Conversion Example
    //
    //! \addtogroup f2806x_example_list
    //! <h1> ADC Start of Conversion (adc_soc)</h1>
    //! 
    //! This ADC example uses ePWM1 to generate a periodic ADC SOC - ADCINT1.
    //! Two channels are converted, ADCINA4 and ADCINA2.
    //! 
    //! \b Watch \b Variables \n
    //! - Voltage1[10]    - Last 10 ADCRESULT0 values
    //! - Voltage2[10]    - Last 10 ADCRESULT1 values
    //! - ConversionCount - Current result number 0-9
    //! - LoopCount       - Idle loop counter
    //
    //###########################################################################
    // $TI Release: $
    // $Release Date: $
    // $Copyright:
    // Copyright (C) 2009-2022 Texas Instruments Incorporated - http://www.ti.com/
    //
    // Redistribution and use in source and binary forms, with or without 
    // modification, are permitted provided that the following conditions 
    // are met:
    // 
    //   Redistributions of source code must retain the above copyright 
    //   notice, this list of conditions and the following disclaimer.
    // 
    //   Redistributions in binary form must reproduce the above copyright
    //   notice, this list of conditions and the following disclaimer in the 
    //   documentation and/or other materials provided with the   
    //   distribution.
    // 
    //   Neither the name of Texas Instruments Incorporated nor the names of
    //   its contributors may be used to endorse or promote products derived
    //   from this software without specific prior written permission.
    // 
    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
    // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    // $
    //###########################################################################
    
    //
    // Included Files
    //
    #include "DSP28x_Project.h"     // Device Headerfile and Examples Include File
    
    //
    // Function Prototypes
    //
    __interrupt void adc_isr(void);
    void Adc_Config(void);
    
    //
    // Globals
    //
    Uint16 LoopCount;
    Uint16 ConversionCount;
    Uint16 Voltage1[100];
    Uint16 Voltage2[100];
    
    //
    // Main
    // 
    void main(void)
    {
        //
        // Step 1. Initialize System Control:
        // PLL, WatchDog, enable Peripheral Clocks
        // This example function is found in the F2806x_SysCtrl.c file.
        //
        InitSysCtrl();
    
        //
        // Step 2. Initialize GPIO:
        // This example function is found in the F2806x_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 F2806x_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 F2806x_DefaultIsr.c.
        // This function is found in F2806x_PieVect.c.
        //
        InitPieVectTable();
    
        //
        // 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;
        EDIS;    // This is needed to disable write to EALLOW protected registers
    
        //
        // Step 4. Initialize all the Device Peripherals:
        // This function is found in F2806x_InitPeripherals.c
        // InitPeripherals(); // Not required for this example
        //
        InitAdc();  // For this example, init the ADC
        AdcOffsetSelfCal();
    
        //
        // Step 5. User specific code, enable interrupts:
        //
    
        //
        // Enable ADCINT1 in PIE
        //
        PieCtrlRegs.PIEIER1.bit.INTx1 = 1; // Enable INT 1.1 in the PIE
        IER |= M_INT1; 					   // Enable CPU Interrupt 1
        EINT;          					   // Enable Global interrupt INTM
        ERTM;          					   // Enable Global realtime interrupt DBGM
    
        LoopCount = 0;
        ConversionCount = 0;
    
        //
        // Configure ADC
        //
        EALLOW;
    
        //
        // Enable an GPIO output on GPIO14 and GPIO15, set it high
        //
        GpioCtrlRegs.GPAPUD.bit.GPIO14 = 1;   // Enable pullup on GPIO14
        GpioDataRegs.GPASET.bit.GPIO14 = 0;   // Load output latch
        GpioCtrlRegs.GPAMUX1.bit.GPIO14 = 0;  // GPIO14 = GPIO14
        GpioCtrlRegs.GPADIR.bit.GPIO14 = 1;   // GPIO14 = output
    
        GpioCtrlRegs.GPAPUD.bit.GPIO15 = 1;   // Enable pullup on GPIO15
        GpioDataRegs.GPASET.bit.GPIO15 = 0;   // Load output latch
        GpioCtrlRegs.GPAMUX1.bit.GPIO15 = 0;  // GPIO15 = GPIO15
        GpioCtrlRegs.GPADIR.bit.GPIO15 = 1;   // GPIO15 = output
    
        GpioCtrlRegs.GPAPUD.bit.GPIO11 = 0;   // Enable pullup on GPIO11
        GpioDataRegs.GPASET.bit.GPIO11 = 1;   // Load output latch
        GpioCtrlRegs.GPAMUX1.bit.GPIO11 = 0;  // GPIO11 = GPIO11
        GpioCtrlRegs.GPADIR.bit.GPIO11 = 1;   // GPIO11 = output
    
        GpioCtrlRegs.GPAPUD.bit.GPIO12 = 0;   // Enable pullup on GPIO12
        GpioDataRegs.GPASET.bit.GPIO12 = 1;   // Load output latch
        GpioCtrlRegs.GPAMUX1.bit.GPIO12 = 0;  // GPIO12 = GPIO12
        GpioCtrlRegs.GPADIR.bit.GPIO12 = 1;   // GPIO12 = output
    
        //
        // Enable Simultaneous Sampling
        //
        AdcRegs.ADCSAMPLEMODE.bit.SIMULEN0 = 1;
    
        //
        // Set SOC0 and SOC1 to high priority
        //
        //AdcRegs.SOCPRICTL.bit.SOCPRIORITY = 2;
    
    
    
        AdcRegs.ADCCTL2.bit.ADCNONOVERLAP = 1; // Enable non-overlap mode
        
        //
        // ADCINT1 trips after AdcResults latch, called "Late Interrupt Pulse" in datasheet / TRM
        //
        AdcRegs.ADCCTL1.bit.INTPULSEPOS	= 1;
        
        AdcRegs.INTSEL1N2.bit.INT1E     = 1;  // Enabled ADCINT1
        AdcRegs.INTSEL1N2.bit.INT1CONT  = 0;  // Disable ADCINT1 Continuous mode
        
        //
        // setup EOC1 to trigger ADCINT1 to fire
        //
        AdcRegs.INTSEL1N2.bit.INT1SEL 	= 1;
        
        AdcRegs.ADCSOC0CTL.bit.CHSEL 	= 2;  // set SOC0 channel select to ADCINA2
        AdcRegs.ADCSOC1CTL.bit.CHSEL 	= 4;  // set SOC1 channel select to ADCINA4
        
        //
        // set SOC0 start trigger on EPWM1A, due to round-robin SOC0 converts
        // first then SOC1
        //
        AdcRegs.ADCSOC0CTL.bit.TRIGSEL 	= 5;
        
        //
        // set SOC1 start trigger on EPWM1A, due to round-robin SOC0 converts 
        // first then SOC1
        //
        AdcRegs.ADCSOC1CTL.bit.TRIGSEL 	= 5;
        
        //
        // set SOC0 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)
        //
        AdcRegs.ADCSOC0CTL.bit.ACQPS 	= 6;
        
        //
        // set SOC1 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)
        //    
        AdcRegs.ADCSOC1CTL.bit.ACQPS 	= 6;
        EDIS;
    
        //
        // Assumes ePWM1 clock is already enabled in InitSysCtrl();
        //
        EPwm1Regs.ETSEL.bit.SOCAEN	= 1;		// Enable SOC on A group
        EPwm1Regs.ETSEL.bit.SOCASEL	= 4;		// Select SOC from CMPA on upcount
        EPwm1Regs.ETPS.bit.SOCAPRD 	= 1;		// Generate pulse on 1st event
        EPwm1Regs.CMPA.half.CMPA 	= 0x0080;	// Set compare A value
        EPwm1Regs.TBPRD 			= 0xFFFF;	// Set period for ePWM1
        EPwm1Regs.TBCTL.bit.CTRMODE	= 0;		// count up and start
    
        //
        // Wait for ADC interrupt
        //
        for(;;)
        {
            LoopCount++;
        }
    }
    
    //
    // adc_isr - 
    //
    __interrupt void
    adc_isr(void)
    {
        Voltage1[ConversionCount] = AdcResult.ADCRESULT0;
        Voltage2[ConversionCount] = AdcResult.ADCRESULT1;
        
        //
        // If 20 conversions have been logged, start over
        //
        if(ConversionCount == 99)
        {
            ConversionCount = 0;
        }
        else
        {
            ConversionCount++;
        }
    
        //
        // Clear ADCINT1 flag reinitialize for next SOC
        //
        AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;
        
        PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;   // Acknowledge interrupt to PIE
    
        return;
    }
    
    //
    // End of File
    //
    
    

  • Hi,

    One more quick thing: if the ADC ISR takes too long to run, the ADC will have time to overwrite the results register before CPU can grab the value. This is the scenario in which the ADCRESULTx register will be overwritten. In my posted example, the ISR must execute in fewer than 20 ADCCLK Cycles. If the ISR is too long, you could slow down the ADCCLK or do something else to slow the ADC conversions. 

    Best Regards,

    Ben Collier

  • Hi ,

    I do have one more question regarding to the simultaneous sampling mode. Following is the configuration I am trying to get:

    1. All 16 ADC Channels to be used for ADC Conversion

    2. I am using even SOCx pair

    3. All SOCx triiger source is same, set as 5 (ePWM1.ADCSOCA)

    4. I have set SOCPRIORITY as 15, so all the SOC are high priority

    5. I have set INTSEL1N2.bit.IN1SEL as 15, I am understanding EOC15 would be last to occur and It should trigger my ADCINT1. Inside the ISR I am reading all the ADCRESULT Register

    6. ePWM CMPA is set as 0x1000 and TBPRD is set as 0x1010.

    The issue is my ADC Registers as reporting correct value till ADCINA3 and ADCINB3. Post that ADCRESULT Register is giving garbage data.

    Could you please suggest me what configuration I am doing wrong

  • Hi Dev,

    Could you please share the code that you are using? If you like, you could send it in a private message offline. There could be several settings that could cause this, so I want to make sure that everything is set up correctly.

    Best Regards,

    Ben Collier

  • Hi ,

    I have attached my file here. Could you please suggest a time when we can communicate here. So the time zone difference can be avoided.

    main.cpp

  • Hi Dev,

    I will try to debug the example you posted tomorrow morning. Did you try running the example code that I sent in my earlier response? If so, were you able to correctly sample channel A4 with that code? 

    I am mostly available 8AM-6PM CST (Chicago/Dallas time), so please expect responses at that time. 

    Best Regards,

    Ben Collier

  • Hi Dev,

    I have replicated your issue, but I am not entirely sure of the cause. I will reach out to some collogues to see what they think. 

    Best Regards,

    Ben Collier

  • Hi Dev,

    I apologize for the time it has taken to get back to you on this. I am still trying to find the cause of this issue, and I have another meeting with some more ADC experts this Friday. I will let you know when we find a solution.

    Best Regards,

    Ben Collier

  • Hi Dev,

    Could you try triggering your interrupt at the end of SOC 14 instead of 15? If that doesn't work, I can send my edited example that successfully triggers and samples all SOCs with the same trigger.

    Best Regards,

    Ben Collier

  • Hi Benjamin,

    EOC14 helped to close the issue. I want to understand why EOC15 did not worked.

  • Hi Dev,

    I will look into this and get back to you tomorrow. I apologize for the delay in my response.

    Best Regards,

    Ben Collier

  • Hi Dev,

    Regrettably I have been caught up with other projects, and I have not been able to dig into this. The colleagues I asked about this were also unable to provide a quick response. Please let me know if this is a critical issue.

    Best Regards,

    Ben Collier

  • Hi Benjamin,

    No problem. We can discuss this in the future. As per latest update if we keep EOC14 as interrupt source for ADCINT. Things are working fine as expected.