Spikes again

28 Nov 2011

Hi,

Ran into the famous spikes issues of the ADC converter and found some effects i would like to share. My application needs 2 channel ADC samples very 1ms so not that fast (but that does not seem to be the problem). In all cases I measured with noting attached to the ADC pins (so i get a 'nice'50Hz humm signal).

1) When I do conversions with the mbed ADC api triggered by either RIT or Ticker i have spikes (0-3 every 100 samples). ADC conversion clock unknown.

2) When I use the Timer to setup DMA triggered 2 ADC conversions at an interrupt I have the same effect as 1). The ADC is running with a 8MHz conversion clock.

BUT when I use the Timer1 mat1:0 pin to trigger a conversion and in the ADC interrupt routine do a 2nd immediate one I do not have spikes at all (currently I completed 30.000 x 2 x 100 samples without a single spike). The ADC is running with a 12MHz conversion clock.

It seems like problems start when at least the ADC is running continuously and ADC samples are drawn from the flow of conversions performed. This however does not explain the DMA code although both conversions run continuously after each other.

Only strange effect on the Timer1 method is that timing is not as precise as one would expect. Every 2*10 conversions I seem to loose/slip a uSec (so 1uSec slip every 10mSec).

Any comments on this? And does anyone know what the default ADC conversion clock is of the mBed libs?

wvd_vegt

28 Nov 2011

Just curious. Are these ADC spikes seen on any other LPC1768 boards, or is it just the mbed?

29 Nov 2011

Hi All

I compared code and added two (we'll could be one) lines that seem to improve (to soon to say it cures) the occurences of spikes:

//Deleted code...
#define SAMPLE_RATE     150000
ADC adc(SAMPLE_RATE,1);

As i do not use the adc object it's at leasyt curious. But where i had about an average of a spike per 100 samples i'm now running a loop of 1000 (already at 200) of 2*100 samples, sofar without a single spike.

I define a spike as a value that is 128 larger (or smaller) than it's neighbors. With open ADC inputs it gives some false positives as I measure quite some humm.

So does anyone have an idea what the adc constructor does (and my code not)?

For those interested, the raw code from my project:

1) The definitions and interrupt part

//------------------------------------------------------------------------------
//----ALL ADC Methods
//------------------------------------------------------------------------------

#define XTAL_FREQ       12000000

#define MAX_SAMPLES     100

//Deleted code...
#define SAMPLE_RATE     150000

ADC adc(SAMPLE_RATE,1);

//Current Skip Rate Index.
static int adc_rate = 0;

//Start with every 10th point, end with every point.
static int adc_rates[2] = {1, 1};

//Stop with first skip rate at sample 5. -1 is forever.
static int adc_skippoint[2] = {-1, -1};

//Always start with a sample.
static int adc_skip = 1;

unsigned long Samples0[MAX_SAMPLES];
unsigned long Samples1[MAX_SAMPLES];
unsigned long Timing01[MAX_SAMPLES];

Timer adc_stamper;          //Timestamping Samples.
Timer match_timing;         //Total Sampling Time.

//------------------------------------------------------------------------------
//----CID_TIMER (MAT1.0 Based ADC Triggering)
//------------------------------------------------------------------------------

volatile uint32_t timer1hits =     0;       // timer1 stops when timer1hits==imer1loop
uint32_t match               =   500;       // 0.5ms (2Mhz/1000)
uint32_t prescaler           =  96-1;       // 96Mhz/96 = 1Mhz

//See http://mbed.org/forum/mbed/topic/1965/?page=1#comment-10043

//Manually start a conversion (and cause an interrupt).
//* CLKDIV was 1UL (24/2) now (24/4)
#define CLKDIV 1UL
#define START_CONVERSION_NOW(ch) \
    LPC_ADC->ADCR=(0x1<<24)|(1UL<<21)|(CLKDIV<<8)|ch;\
    NVIC_EnableIRQ(ADC_IRQn);\
    LPC_ADC->ADINTEN = 0x3

//Trigger ADC on falling edge of MAT1:0 (and have it cause an interrupt).
#define START_CONVERSION_TIMED(ch) \
    LPC_ADC->ADCR=(0x6<<24)|(1UL<<21)|(CLKDIV<<8)|ch;\
    NVIC_EnableIRQ(ADC_IRQn);\
    LPC_ADC->ADINTEN = 0x3;\
    LPC_TIM1->TCR=0;\
    LPC_TIM1->TCR=1

extern "C" void Adc_IRQHandler(void) __irq {
    //LPC_TIM1->MR0 = match-1;
    LPC_TIM1->IR = 0x1UL;                   //* Re-eanbled

    Timing01[timer1hits]=adc_stamper.read_us();

    //TODO Toggle Channel Mask (01 to 01 in ADCR).
    //     Take twice the number of samples at half the sampling time
    //     So 2000 samples (1000+1000) at 0.5ms -> 1000 pairs in 1 sec.

    switch (LPC_ADC->ADSTAT & 0xFF) {
        case 0x01:
            Samples0[timer1hits] = LPC_ADC->ADDR0;// adc[0];
            START_CONVERSION_NOW(0x02);
            break;

        case 0x02:
            Samples1[timer1hits] = LPC_ADC->ADDR1;// adc[1];
            START_CONVERSION_TIMED(0x01);

            //Skip Samples to lower rates. Change adc_rate to change sample rate.
            adc_skip--;
            if (adc_skip==0) {
                if (adc_skippoint[adc_rate]!=-1 && timer1hits==adc_skippoint[adc_rate]) {
                    adc_rate++;
                }

                //Reload adc_skip
                adc_skip = adc_rates[adc_rate];

                timer1hits++;
            }

            if (timer1hits==MAX_SAMPLES) {
                match_timing.stop();

                NVIC_DisableIRQ(ADC_IRQn);

                LPC_TIM1->TCR = 0x00;        // Disable Timer
            }

            break;
    }
}

2) The setup, measurement and report part

           //*******************************
            //NOTE: Timer1 Driven ADC.
            //
            //      Works but way to fast!!
            //
            //      Did not work previously because connected
            //      to the wrong IRQ (should be the adc one).
            //
            //      Skips 1 usec every 2*10 samples.
            //*******************************

        case CID_TIMER: {
            // Enable the ISR vector
            NVIC_SetVector(ADC_IRQn,  (uint32_t)&Adc_IRQHandler);

            // Set PCLK_TIMER1
            //                    PCLK_TIMER1 = CCLK/1 96M/1 = 96MHz
            LPC_SC->PCLKSEL0 &= ~(3UL << 4);   // Clear bits
            LPC_SC->PCLKSEL0 |=  (1UL << 4);   // Set bit

            LPC_SC->PCONP |= 1 << 2;      // Power on Timer 1

            LPC_TIM1->TCR  = 0x2UL;       // Reset and set to timer mode

            //NOTE match-2 is to much (seems like a clock is off somehwere).

            LPC_TIM1->CTCR = 0x0UL;       // Connect to prescaler
            LPC_TIM1->PR   = prescaler;   // Prescale -> 1Mhz
            LPC_TIM1->MR0  = match-1;     // Match count for 5mS (we toggle so end up with half)
            LPC_TIM1->MCR  = 2;           // Reset TCR to zero on match

            //See http://mbed.org/forum/mbed/topic/1965/?page=1#comment-10043
            LPC_TIM1->EMR  = (3UL<<4)|1;  // Make MAT1.0 toggle (see START_CONVERSION_TIMED MACRO).

            // veg - ADC Max Clock = 13MHz. One conversion takes 65cycles so 200Khz Sampling frequency!
            //       For nice exact values for a single conversion we need the ADC Clock to be a 65 fold (So overclock).

            // Power up the ADC and set PCLK
            LPC_SC->PCLKSEL0 &= ~(3UL << 16);           // PCLK = CCLK/4 96M/4 = 24MHz

            LPC_SC->PCONP    |=  (1UL << 12);           // Power on ADC
            LPC_ADC->ADCR    |=  (1UL << 21) | (CLKDIV<<8);    // * Enable ADC & Set Divider Sample Rate 24Mhz / CLKDIV+1

            // Set the pin functions to ADC
            LPC_PINCON->PINSEL1 &= ~(3UL << 14);        // P0.23, Mbed p15. (AD0)
            LPC_PINCON->PINSEL1 |=  (1UL << 14);
            LPC_PINCON->PINSEL1 &= ~(3UL << 16);        // P0.24, Mbed p16. (AD1)
            LPC_PINCON->PINSEL1 |=  (1UL << 16);

            memset(Samples0, 0, sizeof(Samples0));
            memset(Samples1, 0, sizeof(Samples1));

            // Enable the ADC, 12MHz,  ADC0.0,  ADC0.1 -> Macro/Defines!
            // LPC_ADC->ADCR  = (1UL << 21) | (1UL << 8) | (3UL << 0);

            //Reset Test Variable.
            timer1hits   = 0;

            //Reset Skip Rate.
            adc_rate     = 0;
            adc_skip     = 1;             // We start with a sample!

            adc_stamper.reset();
            adc_stamper.start();

            match_timing.reset();
            match_timing.start();

            START_CONVERSION_TIMED(0x01);

            //Pause until timer stops.
            while (LPC_TIM1->TCR==1) {
                wait(0.001);
            }

            //LPC_ADC->ADCR &= ~(7UL << 24);          // Clear ADC Start bits -> Macro/Defines!

            int elapsed = match_timing.read_us();

            match_timing.stop();

            adc_stamper.stop();

            //Show Results.

            //[ADC_1]
            //0000=0xC00028C0       ;Done + Overrun
            cmdb.printsection("ADC_1");
            for (int i=0; i < MAX_SAMPLES; i++) {
                cmdb.printf("%04d=%04u ; 0x%08X\r\n", i, (Samples0[i]>>4) & 0xFFF, Samples0[i]);
            }

            //[ADC_2]
            //0000=0x00004B40       ;!Done (or already cleared)
            cmdb.printsection("ADC_2");
            for (int i=0; i < MAX_SAMPLES; i++) {
                cmdb.printf("%04d=%04u ; 0x%08X\r\n", i, (Samples1[i]>>4) & 0xFFF, Samples1[i]);
            }

            cmdb.printsection("TIMING_1");
            for (int i=0; i < MAX_SAMPLES; i++) {
                cmdb.printf("%04d=%08.3f\r\n", i, 0.001*Timing01[i]);
            }

            cmdb.printsection("Totals");
            cmdb.println("Method=Timer");
            cmdb.printf("Elapsed=%f ; ms\r\n", 0.001*elapsed);
            cmdb.printf("Hits=%d ; timer hits\r\n", timer1hits);
            cmdb.printf("Sampletime=%0.3f ; us\r\n", 1.0*elapsed/timer1hits);

            LPC_ADC->ADCR    |=  (1UL << 21);           // * Disable ADC
            LPC_SC->PCONP    |=  (1UL << 12);           // * Power off ADC

            match_timing.reset();
        }
        break;

wvd_vegt

30 Nov 2011

Hi,

For those interested in testing I publised a test program at:

http://mbed.org/users/wvd_vegt/programs/Spiker/m1g2jp

As it is I have no spikes of 128 cnts or more unless I disable the line:

ADC adc(SAMPLE_RATE,1);

It does an endless 2*1000 samples conversion at 1ms sample time and flashes led 2 after each conversion (and writes a . to the console). When it delects a spike it flahes led1 and writes the data to the console. The spike criterium can be adjusted.

wvd_vegt

30 Nov 2011

Hi wim,

Great post; I've raised it as a ticket and we'll reproduce/digest/take a look in detail tomorrow.

Keep it up!

Simon

01 Dec 2011

Hi All,

Just a quick note to say that we're reopening the investigation into this. Emilio is helping with some test software and I'll be trying a few different hardware scenarios.

I'll keep you all updated - probably with the test results on a cookbook page.

Cheers, Chris

02 Dec 2011

HI All,

I've spent the last day or so doing some experiments with an mbed, to try and characterize the ADC noise that has been regularly reported, and see what the top tips are for reducing it.

A link to my notebook page has just posted in the cookbook, here it is (save you looking in the cookbook! :

All feedback is welcome!

Cheers, Chris

02 Dec 2011

Hi everyone,

Perhaps this is a dumb question, but have you tried compiling your code against the beta mbed library? I was having major problems with erroneous readings with voltage measurement, and the beta libraries seem to have completely eliminated the problem that I was experiencing, which seems extremely similar to the problem you're reporting. Apparently the release library contains a bug in the read() routine that the wizards at mbed have fixed and published in the beta library. I'm thrilled that they make the beta library available to everyone!

See my thread here: http://mbed.org/forum/electronics/topic/2783/ and a different thread here: http://mbed.org/forum/mbed/post/14563/

Hope this helps.

Cheers, Chris

02 Dec 2011

Hi Chris,

The beta library was pushed live the other day, and they are now one and the same.

The new library does have some new functionality; it runs the ADC faster, takes 3 samples and returns the median. This is great for spike rejection, but it's not the entire picture.

For example, all the testing I have been doing today was with the current live library, with this median filtering. It is better, but not perfect (as you'll see from my first test case), but the other tips to reduce ADC noise do work a treat.

Cheers, (the other)Chris

02 Dec 2011

Hi Chris

I compiled against v28 it seems (at least the compiler tells me). There is an updated v29.

The strange part was that without the:

ADC adc(SAMPLE_RATE,1);

Line I had quite some spikes, with it not a single (and it repeated). I was lucky to have a printout of the working code so end up with the only line that could make the difference.

I did 100.000 times 2*250 samples without a single large spike (and nothing connected to the ADC inputs). Without the line I had at least 5+ spikes in each 2*100 samples I tried there. So it's significantly enough to me to look into. Other measuring methods where not affected and all showed spikes.

What I would like is that the actual cause is found of the problem (i'm favoring a init order problem at the moment). Perhaps it's already solved in the beta libs, but I do not like beta's for production code (so have to wait).

wvd_vegt

02 Dec 2011

Hi Chris & Wim (and everyone else),

I don't think the beta library and the release library are the same. I show the release library as "Revision 29" and the beta library shows as "Revision 37". I have done millions of reads with the beta library without any noticeable spikes or bad reads. Would be really interesting for you to try the current beta library to see if there's any improvement.

I'm with you on not trusting beta code for production projects either, except in certain circumstances.

Any chance you can give it a try and let me know?

Cheers, Chris

04 Dec 2011

Hi,

Doing reads with an 'underwater' median filter that suppresses the kind of 1 sample spikes I'm seeing/talking about isn't what I want (but if the filter works correctly it's no wonder you're not seeing spikes).

I was measuring by directly reading the adc registers, so no filters or fixups whatsoever!

wvd_vegt

04 Dec 2011

Hi Wvdv,

I'm new to this, of course, so please forgive my question. Seems to me that going this route might be analogous to reading directly from a hard disk's sectors and bypassing the drive's error correcting mechanisms...do-able but not sure what the benefit(s) might be. What is the benefit of doing the reads directly as you're doing as opposed to using the read() from the library?

Cheers, Chris

04 Dec 2011

Hi,

No, error correcting is different, it reconstructs the original data.

The median filter does alter the signal in a none reversible way.

If I have raw data I can for instance alter later my filter algorithm at display time (for instance let he user pick a filter). With altered data you throw away information. A Median or other digital filter is not bad, it's the time at which it's applied (before it can be stored safely).

wvd_vegt

05 Dec 2011

Hi Chris (&all),

Just to reconfirm, the "Revision 29" of the library is what was previously in beta - it is now live and production quality.

The beta and live repository are different SVN trees. The beta one was at Revision 37 when we decided it was time to push it live, at which point it is released into the live repository at Revision 29.

The Revision 29 has the median filtering, the same as the old beta library.

Hope that clarifies!

Thanks, Chris

05 Dec 2011

Hi All,

I have just published a new version of the ADC performance test that does an explicit check for a full scale spikes. It spots them quite happily, and (more to the point) the outlined techniques still prevents them!

Thanks, Chris

07 Dec 2011

I see a significant improvement in reading stability with the V29 Library. Thanks to everyone who worked on this...

--

25 Jan 2013

Hi all,

Where can I found the V29 Library. It doesn't appear with the URL and when I search V29 only appear I2CR library.

25 Jan 2013

They mean revision 29 of the mbed library. We are meanwhile alot further in the revisions, and they still should have the fix from 29.