Stellaris ADC Oversampling question

I am using a 9b92 CPU and Code Composer Studio.

I have a question about using hardware oversampling with an ADC sequence.   I am using an ADC sequence with 4 analog inputs and using a 4 step sequence to read each of the 4 inputs.  When running at 1MHz sample rate, it will take 4uS to collect the 4 ADC samples.  If I then turn on hardware oversampling of 32X, it should take 32*4 = 128uS to sample the 4 pins and average the data.  What I am not clear on is how exactly does the ADC oversample and average the data.  Does it:
1) Sample channel one 32 times, then sample channel two 32 times, then channels three 32 time and finally channel 4 32 time and then average and report the data?
Or does it
2)  sample channel one then two then three and then four and repeat (1->2->3->4)  thirty two times?

ROM_ADCHardwareOversampleConfigure(ADC0_BASE, 32);  //turn on 32x oversample 

Thanks,
Doug

  • Doug,

       The answer to your question is #1.  In your example the ADC will sample the first channel 32 times and store the average, then the second channel 32 times, then third channel 32 times then the 4th 32 times. 

    The ADC Module Block Diagram in figure 12-2 of the LM3S9b92 datasheet gives an accurate representation of where the hardware averager is located in the flow.

    Your understanding of time required per number of samples is correct.

    - Ken

  • In reply to Stellaris Ken:

    Hi there,

    I am currently using LM3S9D92@80 MHz, 12 bits resolution, TIMER3 as trigger@1 KHz, scanning AIN0 to AIN7 using sequencer SS0. If I enable the averager, the maximum oversampling factor I can apply without overrun is x8 (checked with usual "set at start and reset and end of conversion" output pin). Is it really the maximum oversampling factor I can get or should I expect to reach a higher one? Does the 12 bit resolution require a longer AD conversion time? Is it possible to know the averager processing time?

    best regards,

    Jacopo

  • In reply to Jacopo Costella:

    Jacopo,

    Could you go into more detail about the overrun you are seeing and how you know you're limited to 8x averaging? You should be able to average more than 8x since 8 channels*8 samples * 1 uS per sample =64 uS. 64 uS is well under the required 1 mS time set by your timer trigger.

    The 12 bit resolution can sample at 1 MHz. The averager processing time is fast enough to average each sample as it comes in, so it must be at least 1 MHz.

  • In reply to Stellaris John:

    This is the TIMER3 inizialization code:

    //
    // The Timer3 peripheral must be enabled for use.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER3);

    //
    // JJ Configure Timer3 as a 32-bit periodic timer.
    //
    ROM_TimerConfigure(TIMER3_BASE, TIMER_CFG_32_BIT_PER);

    //
    // Set the Timer3 load value to 1 ms.
    //
    Timer3Load = SysCtlClockGet() / 1000UL;
    ROM_TimerLoadSet(TIMER3_BASE, TIMER_A, Timer3Load);

    ROM_TimerControlTrigger(TIMER3_BASE, TIMER_A, true);
    //
    // Enable processor interrupts.
    //

    TimerIntRegister(TIMER3_BASE, TIMER_A, ClearTimer3);

    //
    // Configure the Timer3A interrupt for timer timeout.
    //
    ROM_TimerIntEnable(TIMER3_BASE, TIMER_TIMA_TIMEOUT);

    //
    // Enable the Timer3A interrupt on the processor (NVIC).
    //
    ROM_IntEnable(INT_TIMER3A);

    //
    // Enable Timer3.
    //
    ROM_TimerEnable(TIMER3_BASE, TIMER_A);

    *****************************

    and this is TIMER3 interrupt handler:

    //
    // Clear the timer interrupt flag.
    //
    TimerIntClear(TIMER3_BASE, TIMER_TIMA_TIMEOUT);

    SyncTimeMs++;

    ROM_GPIOPinWrite(GPIO_PORTJ_BASE, GPIO_PIN_0, GPIO_PIN_0);

    *******************************************************

    SyncTimeMs acts as a sample counter, PJ0 is the output test pin: it is set to 1 in the handler.

    Here is the ADC inizialization code:

    //
    // The ADC0 peripheral must be enabled for use.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    //
    // For this example ADC0 is used with AIN0 on port E7.
    // The actual port and pins used may be different on your part, consult
    // the data sheet for more information. GPIO port E needs to be enabled
    // so these pins can be used.
    // TODO: change this to whichever GPIO port you are using.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

    //
    // Select the analog ADC function for these pins.
    // Consult the data sheet to see which functions are allocated per pin.
    // TODO: change this to select the port/pin you are using.
    //
    ROM_GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_4);
    ROM_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_7 | GPIO_PIN_6 | GPIO_PIN_5 | GPIO_PIN_4);

    //
    // JJ Set resolution to 12 bits
    //

    ROM_ADCResolutionSet(ADC0_BASE, ADC_RES_12BIT);

    //
    // JJ oversample by 8
    //

    ROM_ADCHardwareOversampleConfigure(ADC0_BASE, 8);

    //
    // Enable sample sequence 0 with a timer signal trigger.
    // Each ADC module has 4 programmable sequences, sequence 0
    // to sequence 3. This example is arbitrarily using sequence 0.
    //
    ROM_ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0);

    ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0);
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH1);
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH2);
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_CH3);
    ROM_ADCSequenceStepConfigure(ADC1_BASE, 0, 4, ADC_CTL_CH4);
    ROM_ADCSequenceStepConfigure(ADC1_BASE, 0, 5, ADC_CTL_CH5);
    ROM_ADCSequenceStepConfigure(ADC1_BASE, 0, 6, ADC_CTL_CH6);
    ROM_ADCSequenceStepConfigure(ADC1_BASE, 0, 7, ADC_CTL_CH7 | ADC_CTL_IE | ADC_CTL_END);

    ROM_ADCSequenceEnable(ADC0_BASE, 0);

    ROM_ADCIntClear(ADC0_BASE, 0);
    }

    ***************************************************************

    this is the ADC reading code. I use a simple telnet connection to transmit counter and sample values:

    while(!ROM_ADCIntStatus(ADC0_BASE, 0, false)){ }

    ROM_ADCIntClear(ADC0_BASE, 0);

    // test pin reset
    ROM_GPIOPinWrite(GPIO_PORTJ_BASE, GPIO_PIN_0, 0);

    ROM_IntDisable(INT_TIMER3A);
    usprintf(outstr, "$%08X", SyncTimeMs);
    ROM_IntEnable(INT_TIMER3A);
    EnetWriteString(outstr);


    //
    // Read ADC0 Value
    //
    ROM_ADCSequenceDataGet(ADC0_BASE, 0, ulADC0_Value);

    for (i = 0; i < 8; i++)
    {
    usprintf(outstr, "%03X", ulADC0_Value[i]);
    EnetWriteString(outstr);

    }

    ****************************************

    At the beginning, I noticed that when oversampling by 16 or more, I missed some counter value, so I decided to use the test pin. 

    Am I doing something wrong? 

    regards,

    Jacopo

  • In reply to Jacopo Costella:

    Jacopo,

    See how long the ADC reading code is taking to execute. I have no idea how long your EnetWriteString function takes to run. It's possible if EnetWriteString takes close to 1/8 of a millisecond, then the additional 64 microseconds required by 16x oversampling would be enough for 2 timer interrupts to occur before the ADC reading code is finished. Then you would see the counter go up by 2 instead of 1.

    What happens when you move the Timer3 interrupt disable to after the ADC value print for loop?

  • In reply to Stellaris John:

    Oooops!

    at startup, the field MAXADC0SPD in RCGC0 register is set to 0x0 (minimum ADC speed). Setting it to 0x3 makes the ADC work at maximum speed.

    Jacopo