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.

C2000WARE: Improving effective ADC resolution/accuracy

Part Number: C2000WARE

Hello,

When I was programming my ADC previously I was directed to use oversampling techniques to improve the resolution of my ADC. Here is how I was told to do it on the C2000 F2837xD device:

// Main computational interrupt triggered from ADC INT1 (PIE)
// This ISR operates at a frequency of 1MHz, since it is triggered by ADCA INT1,
// Which is set by an end-of-conversion of SOC8 on EPWM1, itself operating at 1MHz
interrupt void MainCPU_ISR(void)
{
// Read sensor data across the ADCs
// Read all in CPU1 to make easier to distribute between cores
inputVolts_CLA1 = (float) AdcbResultRegs.ADCRESULT5; // Read the ADC value placed into ADC register
inputVolts_CPU2 = inputVolts_CLA1; // Place a copy into CLA shared RAM in form

// Oversample the results 8 times for voltage and average, 4 times and average for currents
flyVolts = (((float) AdcaResultRegs.ADCRESULT1
+ (float) AdcaResultRegs.ADCRESULT2
+ (float) AdcaResultRegs.ADCRESULT3
+ (float) AdcaResultRegs.ADCRESULT4
+ (float) AdcaResultRegs.ADCRESULT5
+ (float) AdcaResultRegs.ADCRESULT6
+ (float) AdcaResultRegs.ADCRESULT7
+ (float) AdcaResultRegs.ADCRESULT8)/8); // Compute the average reading on the over-sampled Cathode Output Voltage SOCs
// 8x over-sampling for cathode voltage


flyCurr = (((float) AdccResultRegs.ADCRESULT5
+ (float) AdccResultRegs.ADCRESULT6
+ (float) AdccResultRegs.ADCRESULT7
+ (float) AdccResultRegs.ADCRESULT8)/4); // Compute the average reading on the over-sampled Fly-back current SOCs
// 4x over-sampling for cathode current

// Return from interrupt
AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // Clear ADC INT1 flag
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge PIE group 1 to enable further interrupts
}

Would it not be easier to do this not as float values? I get an error about needing to relax the floating point arithmetic to allow division. Maybe this is not as efficient? Here's an example of something similar I found by SIlicon Labs:

https://www.cypress.com/file/236481/download

 

Is there a similar way we can perform this in the c2000 series? From the above attached document, there are also multiple other ways SI suggests to increase resolution of the ADC measurements, are any of these possible on the c2000 series of MCU's?

As a final question, is oversampling and averaging like the above example possible for differential, 16-bit ADC measurements? How does it differ, and how would it be best to implement this in code? 

Best regards,
Joel

  • Would it not be easier to do this not as float values?

    Yes, easier for humans to follow.

    Maybe this is not as efficient?

    Fixed point operations are more efficient than floating point operations. It would be better to accumulate in fixed point and then perform a single floating point divide (or inverse multiply).

    The C28x C Compiler Guide has some good general tidbits.

    Is there a similar way we can perform this in the c2000 series? From the above attached document, there are also multiple other ways SI suggests to increase resolution of the ADC measurements, are any of these possible on the c2000 series of MCU's?

    It's generally up to the user to determine what approach to use. You can certainly implement the math algorithm in software.

    As a final question, is oversampling and averaging like the above example possible for differential, 16-bit ADC measurements? How does it differ, and how would it be best to implement this in code? 

    The theory behind oversampling is not restricted to any particular resolution. It would work the same between 12-bit or 16-bit results. (A+B) / C

  • Hi Tlee, 

    Thank you very much for the information and guide, it is helpful. Can you direct me in the right direction for how to do these accumulations/divisions in C2000Ware? My confusion is based around the fact that if I am accumulating at least 8 different 16-bit values, the result may not fit into 16-bits. Is it as simple as assigning the result to say, a 32-bit value, and then casting to a float and doing the division? I find it quite confusing process.

    Is it also true that the level of oversampling available on the C2000 boards is quite low, since there are only 8 channels in each ADC that you can sample the signal with? Or can you oversample with a single ADC channel? 

    I understand the final point, but what I meant was that 16-bit differential signals require two channels per measurement, meaning 4 measurements per ADC and thus halving the amount by which you can oversample this certain signal in the ADC's.

    Thanks again for explaining all of this to me.

    Best regards,

    Joel

  • Is it as simple as assigning the result to say, a 32-bit value, and then casting to a float and doing the division?

    Yes, using a 32-bit integer to hold the accumulation with a singular float divide operation is a good approach.

    This is more in the realm of the C language, rather than a device-specific requirement.

    Or can you oversample with a single ADC channel? 

    Oversampling is commonly implemented using a single channel.

    I understand the final point, but what I meant was that 16-bit differential signals require two channels per measurement, meaning 4 measurements per ADC and thus halving the amount by which you can oversample this certain signal in the ADC's.

    Oversampling is only a function of the sampling rate. There is no explicit requirement as the number of channels used.

  • Joel,

    It has come to my attention that there is a driverlib-based oversampling example in C2000Ware:

    ~\C2000Ware_XXXX\driverlib\f2837xd\examples\cpu1\adc\adc_ex14_soc_oversampling.c

    -Tommy

  • Hi Tommy, 

    Thanks for your help, I have accepted your answer to the problem. I do not use driverlib programming in my code, I have found it more difficult to understand since learning the other way. I assume that this is sampling a single ended source with 12-bit, and assigning the value to a 16-bit before doing some kind of bit shift. The important part of the code:  

    adcAResult2 =
    (ADC_readResult(ADCARESULT_BASE, ADC_SOC_NUMBER2) +
    ADC_readResult(ADCARESULT_BASE, ADC_SOC_NUMBER3) +
    ADC_readResult(ADCARESULT_BASE, ADC_SOC_NUMBER4) +
    ADC_readResult(ADCARESULT_BASE, ADC_SOC_NUMBER5)) >> 2;

    Why has the author of this code chose to shift by 2? I have already set up my ADC channels to oversample:

    // Program SOC Control Registers to 7x Over Sample the Cathode Output Voltage on ADCINA2
    // Fly-back voltage and current triggered by ePWM1 SOCA
    // Resonant converter voltage and current triggered by ePWM4 SOCA
    // By triggering the cathode and collector voltages according to ePWM1 and ePWM4 respectively,
    // they can trigger IPC and CLA tasks at different frequencies
    AdcaRegs.ADCSOC1CTL.bit.CHSEL = 2; // SOC1 will convert ADCINA2
    AdcaRegs.ADCSOC1CTL.bit.ACQPS = 15; // SOC1 will use sample duration of 20 SYSCLK cycles
    AdcaRegs.ADCSOC1CTL.bit.TRIGSEL = 5; // SOC1 will begin conversion on ePWM1 SOCA

    AdcaRegs.ADCSOC2CTL.bit.CHSEL = 2; // SOC2 will convert ADCINA2
    AdcaRegs.ADCSOC2CTL.bit.ACQPS = 15; // SOC2 will use sample duration of 20 SYSCLK cycles
    AdcaRegs.ADCSOC2CTL.bit.TRIGSEL = 5; // SOC2 will begin conversion on ePWM1 SOCA

    AdcaRegs.ADCSOC3CTL.bit.CHSEL = 2; // SOC3 will convert ADCINA2
    AdcaRegs.ADCSOC3CTL.bit.ACQPS = 15; // SOC3 will use sample duration of 20 SYSCLK cycles
    AdcaRegs.ADCSOC3CTL.bit.TRIGSEL = 5; // SOC3 will begin conversion on ePWM1 SOCA

    AdcaRegs.ADCSOC4CTL.bit.CHSEL = 2; // SOC4 will convert ADCINA2
    AdcaRegs.ADCSOC4CTL.bit.ACQPS = 15; // SOC4 will use sample duration of 20 SYSCLK cycles
    AdcaRegs.ADCSOC4CTL.bit.TRIGSEL = 5; // SOC4 will begin conversion on ePWM1 SOCA

    AdcaRegs.ADCSOC5CTL.bit.CHSEL = 2; // SOC5 will convert ADCINA2
    AdcaRegs.ADCSOC5CTL.bit.ACQPS = 15; // SOC5 will use sample duration of 20 SYSCLK cycles
    AdcaRegs.ADCSOC5CTL.bit.TRIGSEL = 5; // SOC5 will begin conversion on ePWM1 SOCA

    AdcaRegs.ADCSOC6CTL.bit.CHSEL = 2; // SOC6 will convert ADCINA2
    AdcaRegs.ADCSOC6CTL.bit.ACQPS = 15; // SOC6 will use sample duration of 20 SYSCLK cycles
    AdcaRegs.ADCSOC6CTL.bit.TRIGSEL = 5; // SOC6 will begin conversion on ePWM1 SOCA

    AdcaRegs.ADCSOC7CTL.bit.CHSEL = 2; // SOC7 will convert ADCINA2
    AdcaRegs.ADCSOC7CTL.bit.ACQPS = 15; // SOC7 will use sample duration of 20 SYSCLK cycles
    AdcaRegs.ADCSOC7CTL.bit.TRIGSEL = 5; // SOC7 will begin conversion on ePWM1 SOCA

    AdcaRegs.ADCSOC8CTL.bit.CHSEL = 2; // SOC8 will convert ADCINA2
    AdcaRegs.ADCSOC8CTL.bit.ACQPS = 15; // SOC8 will use sample duration of 20 SYSCLK cycles
    AdcaRegs.ADCSOC8CTL.bit.TRIGSEL = 5; // SOC8 will begin conversion on ePWM1 SOCA

    I suppose my confusion is, how do we set up the "timing" of these oversamples? If all SOC's are triggered by the same source, will they not all just have the exact same value on their input, or is this going to be seperated by however long it takes to execute each line of code in the CPU?

    Is it true that the maximum that you can oversample a signal is 8 equal to the amount of SOCs on the ADC? For example, there are 8 SOCs on the ADC, so the maximum number of oversamples is 8? This seems quite low, allowing only 1 bit of extra resolution if I remember correctly the equation.

    Best regards,
    Joel


  • Why has the author of this code chose to shift by 2?

    A right bit-shift of 2 is the logical equivalent of an integer divide by 4. It's a little more efficient in terms of CPU cycles.

    I suppose my confusion is, how do we set up the "timing" of these oversamples? If all SOC's are triggered by the same source, will they not all just have the exact same value on their input, or is this going to be seperated by however long it takes to execute each line of code in the CPU?

    The EPWM1 SOCA signal will enqueue all SOCs with TRIGSEL=5 for conversion. By default, the SOC queue is processed serially based on round robin priority, where each SOC will employ the ADC for (ACQPS + Conversion + Result Store) amount of time.

    For oversampling over a longer timescale, the adc_ex13_burst_mode_oversampling example may be more fitting.

    You can find an explanation of the SOC priority scheme, burst mode, and ADC timing diagrams, in the TRM.

    Is it true that the maximum that you can oversample a signal is 8 equal to the amount of SOCs on the ADC? For example, there are 8 SOCs on the ADC, so the maximum number of oversamples is 8? This seems quite low, allowing only 1 bit of extra resolution if I remember correctly the equation.

    Each ADC has 16 SOCs, not 8.

    There is nothing that prevents you from accumulating even more samples in the variable using a different triggering scheme.

    1 bit of extra resolution is a doubling of precision.... I would consider that to be significant.

  • Hi TLee,

    Thanks for your help this far, very helpful, making a lot of sense. Does this seem appropriate method of doing this?

    interrupt void MainCPU_ISR(void)
    {
    // Read sensor data across the ADCs
    // Please double check these are correct!
    // Read all in CPU1 to make easier to distribute between cores
    inputVolts_CLA1 = (float) AdcbResultRegs.ADCRESULT5; // Read the ADC value placed into ADC register
    inputVolts_CPU2 = inputVolts_CLA1; // Place a copy into CLA shared RAM in form

    // Perform oversampling in the ADC's to achieve higher resolution
    // Bitwise shift by 3 divides the result by 8
    // Bitwise shift by 4 divides by 16, 2 by 4.
    uint16_t voltageAccumulate;
    uint16_t currentAccumulate;


    voltageAccumulate = ((AdcaResultRegs.ADCRESULT1)
    +(AdcaResultRegs.ADCRESULT2)
    +(AdcaResultRegs.ADCRESULT3)
    +(AdcaResultRegs.ADCRESULT4)
    +(AdcaResultRegs.ADCRESULT5)
    +(AdcaResultRegs.ADCRESULT6)
    +(AdcaResultRegs.ADCRESULT7)
    +(AdcaResultRegs.ADCRESULT8)) >> 3; // Compute the average reading of over-sampled Cathode output voltage SOCs and cast to float

    currentAccumulate = ((AdccResultRegs.ADCRESULT5)
    +(AdccResultRegs.ADCRESULT6)
    +(AdccResultRegs.ADCRESULT7)
    +(AdccResultRegs.ADCRESULT8)) >> 2;

    flyVolts = (float)voltageAccumulate;
    flyCurr = (float)currentAccumulate;

    // Return from interrupt
    AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // Clear ADC INT1 flag
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge PIE group 1 to enable further interrupts
    }

    I accumulate the values in 16-bit format, logical bit shift to divide and THEN cast to float. You said a single float divide but I am not sure if that is what was meant. 

    Let me know?
    Joel

  • I accumulate the values in 16-bit format

    Good.

    logical bit shift to divide and THEN cast to float

    Not good. The bit-shift is the equivalent of an integer divide that will truncate the precision that you wanted to capture in floating point.

    Instead of a bit-shift, use a floating point divide: flyVolts = voltageAccumulate / 8.0

    This is a standard C concept.

  • voltageAccumulate = ((AdcaResultRegs.ADCRESULT1)
    +(AdcaResultRegs.ADCRESULT2)
    +(AdcaResultRegs.ADCRESULT3)
    +(AdcaResultRegs.ADCRESULT4)
    +(AdcaResultRegs.ADCRESULT5)
    +(AdcaResultRegs.ADCRESULT6)
    +(AdcaResultRegs.ADCRESULT7)
    +(AdcaResultRegs.ADCRESULT8)); // Compute the average reading of over-sampled Cathode output voltage SOCs and cast to float

    currentAccumulate = ((AdccResultRegs.ADCRESULT5)
    +(AdccResultRegs.ADCRESULT6)
    +(AdccResultRegs.ADCRESULT7)
    +(AdccResultRegs.ADCRESULT8));

    // Do a singular floating point divide
    flyVolts = (voltageAccumulate/8.0);
    flyCurr = (currentAccumulate/8.0);

    Better? I would assume that this would still have the same issues as the bit shift because this is still dividing by eight. For example if the final result is 8000, and we do >>3, the result would be 1000. If we do 8000/8.0, the result would be 1000.00. 

    Sorry If i Have misunderstood.

    Best regards,
    Joel

  • Yes, better.