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
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