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.

Weird ADC Problem

Hello,

I am experiencing a weird issue with my ADC where every 8 or so samples, it "doubles up" on a sample, meaning that it skips the value that it should be at (assuming a sinusoid) and it instead places a point at the next value. The image below shows what I'm talking about. 

My code works the following way. I have a PWM with period of 80 clock cycles (at 80 MHz, so every 1 us) that triggers SOCA at count 0. SOC0 and SOC1 are set to trigger on the same PWM, with the RRPointer set at 15 so that SOC0 is always first. SOC0 triggers, then SOC1 does, and EOC1 is set to trigger ADCINT1, which I use to save the data into a buffer (which is where I am getting my data points). Does anybody know what might be wrong with this? I've attached 3 pieces of my code below: ADC initialization, PWM initialization, and Interrupt code. The background loop just waits for the buffer to fill up before exiting, there are no actions inside the background loop.

  --------ADC CODE-------------

EALLOW;
AdcRegs.ADCCTL1.bit.ADCBGPWD = 1; // Power up band gap
AdcRegs.ADCCTL1.bit.ADCREFPWD = 1; // Power up reference
AdcRegs.ADCCTL1.bit.ADCPWDN = 1; // Power up rest of ADC
AdcRegs.ADCCTL1.bit.ADCENABLE = 1; // set timing to 13cycles as required by ADC module

for(i=0; i<10000; i++){} // wait 60000 cycles = 1ms (each iteration is 12 cycles)

AdcRegs.ADCCTL2.bit.CLKDIV2EN = 1;
AdcRegs.ADCCTL2.bit.CLKDIV4EN = 0;
AdcRegs.ADCCTL2.bit.ADCNONOVERLAP = 1; // Overlap is not allowed

AdcRegs.ADCSAMPLEMODE.bit.SIMULEN0 = 0;
AdcRegs.ADCINTSOCSEL1.bit.SOC0 = 0; // No ADCINT triggers SOC0. TRIGSEL field determines trigger.
AdcRegs.ADCINTSOCSEL1.bit.SOC1 = 0;
AdcRegs.SOCPRICTL.bit.RRPOINTER = 0xF;
AdcRegs.SOCPRICTL.bit.SOCPRIORITY = 0; // All SOCs handled in round-robin mode

// Configure ADC

AdcRegs.ADCSOC0CTL.bit.CHSEL = 1; //set SOC0 channel select to ADCINA1
AdcRegs.ADCSOC0CTL.bit.TRIGSEL = 9; //set SOC0 start trigger on EPWM3A, due to round-robin SOC0 converts first then SOC1
AdcRegs.ADCSOC0CTL.bit.ACQPS = 6; //set SOC0 S/H Window to 8 ADC Clock Cycles, (8 ACQPS plus 1)

AdcRegs.ADCSOC1CTL.bit.CHSEL = 1;
AdcRegs.ADCSOC1CTL.bit.TRIGSEL = 9; //set SOC0 start trigger on EPWM3A, due to round-robin SOC0 converts first then SOC1
AdcRegs.ADCSOC1CTL.bit.ACQPS = 6; //set SOC0 S/H Window to 8 ADC Clock Cycles, (8 ACQPS plus 1)

AdcRegs.INTSEL1N2.bit.INT1E = 1; // Enable ADCINT1
AdcRegs.INTSEL1N2.bit.INT1SEL = 1; // EOC1 triggers ADCINT1

EDIS;

------------------PWM CODE------------------------------

EPwm3Regs.TBCTL.bit.CTRMODE = 0x3;
EPwm3Regs.TBPRD = period3; // Set timer period, PWM frequency = 1 / period
EPwm3Regs.TBPHS.all = 0; // Time-Base Phase Register
EPwm3Regs.TBCTL.bit.PRDLD = TB_IMMEDIATE; // Set Immediate load
EPwm3Regs.TBCTL.bit.PHSEN = 1;
EPwm3Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;
EPwm3Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1;
EPwm3Regs.TBCTL.bit.CLKDIV = TB_DIV1;

// Setup shadow register load on ZERO
EPwm3Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm3Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO; // load on CTR=Zero

// Configure start of conversion event for ADC
EPwm3Regs.ETSEL.bit.SOCAEN = 1; // Enable SOC on A group
EPwm3Regs.ETSEL.bit.SOCASEL = 1; // Select SOC when the timer is 0
EPwm3Regs.ETPS.bit.SOCAPRD = 1; // Generate pulse on 1st event

----------------------INTERRUPT CODE----------------------------------

interrupt void ADCINT1_ISR(void) // PIE1.1 @ 0x000D40 ADCINT1
{
//--- Manage the ADC registers
TemporaryTime = EPwm1Regs.TBCTR;
TemporaryADC = AdcResult.ADCRESULT1 - NegOffset1; // Collect data into temporary variables

*ADC_Start_Ptr++ = TemporaryADC;
if (TemporaryADC > ADC_Max_Ref)
{
ADC_Max_Ref = TemporaryADC;
ADC_Time_Ref = TemporaryTime;
ADC_Index_Ref = AdcBufTimer;
}
AdcBufTimer++;

AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;
PieCtrlRegs.PIEACK.all = 0xFFFF; // Must acknowledge the PIE group-- This controls repeat-ability
asm(" CLRC INTM, DBGM");
}

Thanks so much for your help,

Matthew

  • Hi Matthew,

    To be precise, can you tell what amount of deviation are you able to observe from the mean value?

    Regards,

    Gautam

  • The signal is varying around a mean value of about 500 on the ADC. On the right, larger side, its fluctuating from about 0 to 1000 and on the left, lower side, maybe 350 to 650. Hope this helps! 

    Matthew

  • Matthew Krenik said:
    instead places a point at the next value.

    Believe this statement much benefits from added description/clarity.  ("places a point" - is an imaginative way of describing an ADC's data collection - but by itself - conveys little...)

    Years since our group used MSP - but this behavior w/in this vendor's ARM MCUs usually results from an "incomplete emptying" of ADC's FIFO data store - or possibly some defect in the ADC "fetch & store" mechanism. 

    When such issues arise - we often find "slowing the process" (both MCU speed & conversion rate) a great aid. 

    In addition - we find a linear ramp signal far superior to sine.  (i.e. provides an "even (and known) "step" in voltage level - making any ADC or transfer defect more notable!)  Our ramp starts @ 0V - drives linearly to V-ADC_Max - then resets to 0V.  We have full control of the ramp's frequency and slope - which enables its "tailoring" to your ADC's capability.

    Note that should the ramp's rise rate be steep - your ADC's input R & C may impact your measurements...

  • Unfortunately I do not get a choice with which signal to sample-- I am looking at an ultrasound 40 kHz receiver and I need to determine it accurately enough to correlate to. Thus the high sampling rate. I will try testing this code with a signal generator, but I don't know if there will be any difference. 

    Besides the C2000 should be more than capable of sampling at 1 MHz. And even if not, I reduced the PWM rate by half (thus cutting my sampling rate in half) and I still get this behavior where the ADC still "incompletely empties" after a sample and hold.

  • Hi Matt,

    Can you post the raw data for this - the exact conversion results you are getting?  I am curious as to whether these results are exactly the same, or just similar, and at what code this is occurring.  

  • 0336.ReceiveData.xlsx

    Sure! I've attached the data (hopefully you can see it). 

    A few things about it:

    - 40 kHz ultrasound receiver (at 1 MHz, that means 25 samples per period)

    - Fed into a 2-stage op amp with a digital potentiometer for variable gain

    - ADC input is biased to about 1.65V

    - ADC digital result is subtracted by offset value of 1700 (this value is variably controlled from a calibration step)

    - Then, result is saved in a buffer array with size of 2000 short int

    - This data represents array values 600 - 999.

  • I think I figured out the error, but its contingent on the assumption that my understanding of the ADC Round Robin mode is correct. Can one of you guys verify this line of thinking?

    I had a different ADC problem occur earlier this summer and I was advised to create two SOCs, to throw away the first one and to keep the second one (as specified in the silicon errata). I made EPWM trigger SOC0 and SOC1. And EOC1 triggered the interrupt. The RRPointer, however, was set to 0, meaning that the first SOC to actually execute was SOC1 (is this correct?). So when I made this change to add in the second SOC, it didn't actually do anything because my first SOC was still SOC1 (the value that I was sampling). When I ran the code like this, everything ran smoothly, no errors. 

    Then, when I changed the RRPointer to 0xF, I made SOC0 the first to execute (actually fitting what the silicon errata specifies). But this additional SOC conversion time puts my interrupt period over 1 us, which makes the interrupt too long for the speed of the PWM, which consequently creates errors. 

  • Hi Matt,

    I haven't looked at your data yet, but your above theory seems reasonable.  If this is the case, the good news is that there is an easy workaround.

    To get around the first sample issue, you actually have 2 options: throw away the first sample or enable non-overlap mode.  Since you only need one sample, you can actually use non-overlap with no performance penalty.  Can you try this?

    (note, see this other thread where I discuss when you want to use non-overlap and when you want to throw a sample away)

    http://e2e.ti.com/support/microcontrollers/c2000/f/902/p/283143/987409.aspx#987409