ADC Overrun

26 Mar 2011

Hi,

I'm trying to get the following to work (sample 2 channels on 1Khz triggered by Timer1 Capture with DMA driven transfer ie based on Andy's code).

#include "mbed.h"
#include "MODDMA.h"

// How long between grabbing samples on all channels.
// Value is in microseconds (1000 fails probably on modserial output)
#define SAMPLE_PERIOD   1000

#define NUM_OF_SAMPLES  10

Serial pc(USBTX, USBRX);

DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);

//TODO Add three timing arrays,
// 1) timer0 int start,
// 2) after the dma config (skew)
// 3) inside the dma_callback.
uint32_t adBuffer[NUM_OF_SAMPLES * 2];

int t1[NUM_OF_SAMPLES];
int t2[NUM_OF_SAMPLES];
int t3[NUM_OF_SAMPLES];

bool dmaTransferComplete;
int  sample_index = 0;

MODDMA dma;
MODDMA_Config *conf;

Timer cap_timing;

void TC0_callback(void);
void ERR0_callback(void);

extern "C" void TIMER1_handler(void)
    __irq {
    if (sample_index<NUM_OF_SAMPLES) {
    t1[sample_index]=cap_timing.read_us();

        // Pre-prep a transfer
        dma.Setup(conf);
        dma.Enable(conf);

        LPC_ADC->ADCR |= (1UL << 16); // ADC burst mode
        LPC_ADC->ADINTEN = 0x100;     // Do all channels.

        t2[sample_index]=cap_timing.read_us();
    }

LPC_TIM1->IR = 1; // Clr timer1 irq.
}

int main() {

    memset(adBuffer, 0, sizeof(adBuffer));

    memset(t1, 0, sizeof(t1));
    memset(t2, 0, sizeof(t2));
    memset(t3, 0, sizeof(t3));

    sample_index=0;

    // Setup the serial port to print out results.
    pc.baud(115200);
    pc.printf("Starting up...\n");

    // Power up the ADC and set PCLK
    LPC_SC->PCONP    |= (1UL << 12);
    LPC_SC->PCLKSEL0 |= (3UL << 24);    // PCLK = CCLK/8 96M/8 = 12MHz
    NVIC_DisableIRQ(ADC_IRQn);

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

    LPC_ADC->ADINTEN = 0x100;

    //               Enable the ADC,
    //                             1MHz,
    //                                          ADC0.0,  ADC0.1
    LPC_ADC->ADCR = (1UL << 21) | (11UL << 8) | (3UL << 0); ///VEG: @ 4Mhz the slip starts. Now 1Mhz

    LPC_SC->PCONP    |= (1UL << 2); // TIM1 On
    LPC_SC->PCLKSEL0 |= (3UL << 4); // Timer0 CCLK/8 = 12MHz
    LPC_TIM1->PR      = 11;         // TC clocks at 1MHz.
    LPC_TIM1->MR0     = SAMPLE_PERIOD-1; //VEG: To be exact!
    LPC_TIM1->MCR     = 3;          // Reset TCR to zero on match and irq.

    NVIC_SetVector(TIMER1_IRQn, (uint32_t)TIMER1_handler);
    NVIC_EnableIRQ(TIMER1_IRQn);

    // Prepare the GPDMA system.
    conf = new MODDMA_Config;
    conf
    ->channelNum    ( MODDMA::Channel_0 )
    ->dstMemAddr    ( (uint32_t)adBuffer )
    ->transferSize  ( 2 )
    ->transferType  ( MODDMA::p2m )
    ->transferWidth ( MODDMA::word )
    ->srcConn       ( MODDMA::ADC )
    ->attach_tc     ( &TC0_callback )
    ->attach_err    ( &ERR0_callback )
    ; // end conf.

    // Prepare configuration.
    if (!dma.Setup( conf )) {
        error("Doh!");
    }

    pc.printf("Before %ld\r\n", conf->dstMemAddr());

    cap_timing.reset();
    cap_timing.start();

    // Begin.
    LPC_TIM1->TCR = 1;

    bool dumped = false;

    while (1) {
        if (!dumped && sample_index==NUM_OF_SAMPLES) {
            for (int j=0;j<NUM_OF_SAMPLES;j++) {
                pc.printf("ADC 0x%08lx  0x%08lx\r\n", adBuffer[j*2+0], adBuffer[j*2+1]);
                pc.printf("TIMING %d  %d  %d\r\n", t1[j], t2[j], t3[j]);
            }

            dumped=true;

            cap_timing.stop();
        }
    }
}

// Configuration callback on TC
void TC0_callback(void) {
    t3[sample_index]=cap_timing.read_us();

    MODDMA_Config *config = dma.getConfig();

    // Disbale burst mode and switch off the IRQ flag.
    LPC_ADC->ADCR &= ~(1UL << 16);
    LPC_ADC->ADINTEN = 0;

    // Finish the DMA cycle by shutting down the channel.
    dma.haltAndWaitChannelComplete( (MODDMA::CHANNELS)config->channelNum());
    dma.Disable( (MODDMA::CHANNELS)config->channelNum() );

    //Does not clear any flags
    //uint32_t dummy = LPC_ADC->ADDR0;

    //TODO Store TimeStamp or better Increment Destination Address.
    config->dstMemAddr(config->dstMemAddr()+2*sizeof(uint32_t));

    // Clear DMA IRQ flags.
    if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq();
    if (dma.irqType() == MODDMA::ErrIrq) dma.clearErrIrq();

    sample_index++;
}

// Configuration callback on Error
void ERR0_callback(void) {
    // Switch off burst conversions.
    LPC_ADC->ADCR |= ~(1UL << 16);
    LPC_ADC->ADINTEN = 0;
    error("Oh no! My Mbed EXPLODED! :( Only kidding, go find the problem");
}

The output is look like:

ADC 0x8000bef0  0x81000000
TIMING 1000  1010  1139
ADC 0xc000b4b0  0x81000000
TIMING 2000  2010  2139
ADC 0xc000b4d0  0x81000000
TIMING 3000  3010  3139
ADC 0xc000b720  0x81000000
TIMING 4000  4010  4139
ADC 0xc000b810  0x81000000
TIMING 5000  5010  5139
ADC 0xc000b8b0  0x81000000
TIMING 6000  6010  6139
ADC 0xc000b8d0  0x81000000
TIMING 7000  7010  7139
ADC 0xc000b950  0x81000000
TIMING 8000  8010  8139
ADC 0xc0000a00  0x81000000
TIMING 9000  9010  9139
ADC 0xc000b650  0x81000000
TIMING 10000  10010  10139

Strangely enough did I not see overrun flags in the original 3 channel code. Channel slipping starts at 3-4Mhz.

Is this overrrun flag caused by DMA to finished a started ADC conversion?

26 Mar 2011

Hi,

I examined the output of the 3 channel code a bit better and saw it gives overruns there too

ADC 0x8000bae0  0x81000030  0x820091b0
TIMING 10000  10010  10202
ADC 0x820091b0  0xc000f2f0  0x81000050
TIMING 20000  20010  20202
ADC 0x81000050  0x82008b90  0xc000b460
TIMING 30000  30010  30202
ADC 0xc000b460  0x81000030  0x820055e0
TIMING 40000  40010  40202
ADC 0x820055e0  0xc000f300  0x81000050
TIMING 50000  50010  50202
ADC 0x81000050  0x82005d80  0xc000b460
TIMING 60000  60010  60202
ADC 0xc000b460  0x81000050  0x820032d0
TIMING 70000  70010  70202
ADC 0x820032d0  0xc000f300  0x81000050
TIMING 80000  80010  80202
ADC 0x81000050  0x820043a0  0xc000b450
TIMING 90000  90010  90202
ADC 0xc000b450  0x81000050  0x82001f80
TIMING 100000  100010  100202

I re-read the user manual and saw i misread the table a bit. The 0x8... value is the overrun flag and 0xC... is both done and overrun. So i see overrun flags at each sample and at each 4th sample (probably the dma finishing the conversion). Still it puzzles me...

So is this harmless is there more to it?

26 Mar 2011

Hi

I found a 'solution'.

I added a read of the Global ADC Data Register to the TIMER1_handler interrupt handler. This read clears the overrun bit before the starting a new conversion so that subsequent reads are ok (with only the done bit set).

extern "C" void TIMER1_handler(void)
    __irq {
    if (sample_index<NUM_OF_SAMPLES) {
    t1[sample_index]=cap_timing.read_us();

    uint32_t dummy4 = LPC_ADC->ADGDR;
    ...
}