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.

Problem with ADC Timer period

Hello,


Sometime ago I got some help with the ADC part of my project, and it was all working fine, however now I noticed I am getting more points then expected for a given period of acquisition.

I am using a TM4C123 launchpad board and this is how i set up the frequency of acquisition of ADC:

uint32_t ui32convrate = 7000;
uint32_t ui32period = SysCtlClockGet()/ui32convrate;

Clock is running at 20MHz, and that is what I get when I print the value of SysCtlClockGet() value.

So I was expecting a 7K samples per second conversion rate, and since I average every 4 samples, I was counting on 1750 samples/second per channel. However, i am getting more than that.

When I printed the ui32period value I get 2285 instead of 2857 (value returned by the calculator of 20000000/7000).

If i got this right, ui32period is the number of revolutions of the system clock to make one revolution of the virtual ADC clock, which with this value gives 1/20M * 2285 = 0.000114 acquisiton period, which is equivalent to 8752Hz and not 7000.

There's probably something about the ADC setup that I am not quite getting, but I'd like some help understanding this so I can figure out why acquisiton is over the 1750 samples/second i wanted. If it has to do with the clock setup or maybe some other part of my code.

I attached my code.

//
//  PS3chan.c
//  
//
//  Created by Ricardo Cabrita on 25/01/16.
//
//

// Includes ------------------------------------------------------------------------------------------
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>

#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_i2c.h"

#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/i2c.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "driverlib/adc.h"
#include "driverlib/timer.h"

#include "utils/uartstdio.h"
#include "utils/ringbuf.h"



// Defines -------------------------------------------------------------------------------------------
#define LED_RED GPIO_PIN_1
#define LED_BLUE GPIO_PIN_2
#define LED_GREEN GPIO_PIN_3
//#define ch1 0001000000000000

// Variables -----------------------------------------------------------------------------------------
uint32_t ui32Seq0Value[4] = {0,0,0,0},ui32Seq1Value[4] = {0,0,0,0}, ui32Seq2Value[4] = {0,0,0,0};
uint32_t averagedADC1,averagedADC2,averagedADC3;
uint32_t ui32Baud = 1000000;

//Ring Buffer Variables
tRingBufObject adcBuff;
uint32_t buffSize = 20000;
uint8_t ui8buff[20000];

//Tracker Variables
float wAVG1 = 0, wAVG2 = 0, wAVG3 = 0;
uint32_t time_stamps = 1;

// Functions -----------------------------------------------------------------------------------------
void ADCinit(uint32_t ui32period){
    //Initialize ADC
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE); //Pin for ADC input
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); //ADC peripheral
    //Enable Timer 0
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    
    //Pins used for ADC channels (3)
    GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_1); //set GPIO_E3 for channel 1
    //GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_2); //set GPIO_E2 for channel 2
    //GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_1); //set GPIO_E1 for channel 3
    
    ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0); //sequencer 0
    ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_TIMER, 1); //sequencer 1
    ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_TIMER, 2); //sequencer 2
    
    //sequencer 0
    ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH0);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH0);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_CH0 | ADC_CTL_END);
    
    //sequencer 1
    ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_CH1);
    ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_CH1);
    ADCSequenceStepConfigure(ADC0_BASE, 1, 2, ADC_CTL_CH1);
    ADCSequenceStepConfigure(ADC0_BASE, 1, 3, ADC_CTL_CH1 | ADC_CTL_END);
    
    //sequencer 2
    ADCSequenceStepConfigure(ADC0_BASE, 2, 0, ADC_CTL_CH2);
    ADCSequenceStepConfigure(ADC0_BASE, 2, 1, ADC_CTL_CH2);
    ADCSequenceStepConfigure(ADC0_BASE, 2, 2, ADC_CTL_CH2);
    ADCSequenceStepConfigure(ADC0_BASE, 2, 3, ADC_CTL_CH2 | ADC_CTL_IE | ADC_CTL_END);
    
    //
    //Initialize Timer 0 to trigger an ADC conversion
    //
    TimerConfigure(TIMER0_BASE,TIMER_CFG_PERIODIC);
    TimerLoadSet(TIMER0_BASE,TIMER_A,ui32period-1); // sampling rate
    TimerEnable(TIMER0_BASE, TIMER_A);
    TimerControlTrigger(TIMER0_BASE,TIMER_A,true);
    
    
    ADCSequenceEnable(ADC0_BASE,0);
    ADCSequenceEnable(ADC0_BASE, 1);
    ADCSequenceEnable(ADC0_BASE, 2);
    //ADCIntEnable(ADC0_BASE,0);
    ADCIntEnable(ADC0_BASE,2);
    //IntEnable(INT_ADC0SS1);
    
}
void ConfigureUART1(void){
    
    // Enable the peripherals used by UART
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
    
    // Set GPIO A0 and A1 as UART pins.
    GPIOPinConfigure(GPIO_PB0_U1RX);
    GPIOPinConfigure(GPIO_PB1_U1TX);
    GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
    // Configure UART clock using UART utils
    UARTClockSourceSet(UART1_BASE, UART_CLOCK_PIOSC);
    UARTStdioConfig(1, ui32Baud, 16000000);
}

void ADC0IntHandler(void){
    
    //short a,b;
    uint8_t putme[24];
    uint32_t avgSeq0, avgSeq1, avgSeq2;
    
    //Clear ADC interrupt  here if interrupt is Light Cycle and small nr of instructions
    //ADCIntClear(ADC0_BASE,1);
    
    //get averaged data from the ADC
    ADCSequenceDataGet(ADC0_BASE,0,ui32Seq0Value);
    ADCSequenceDataGet(ADC0_BASE,1,ui32Seq1Value);
    ADCSequenceDataGet(ADC0_BASE,2,ui32Seq2Value);
    
    avgSeq0 = (ui32Seq0Value[0]+ui32Seq0Value[1]+ui32Seq0Value[2]+ui32Seq0Value[3])/4;
    avgSeq1 = (ui32Seq1Value[0]+ui32Seq1Value[1]+ui32Seq1Value[2]+ui32Seq1Value[3])/4;
    avgSeq2 = (ui32Seq2Value[0]+ui32Seq2Value[1]+ui32Seq2Value[2]+ui32Seq2Value[3])/4;
    
    //write to RingBuffer (all values without averaging)
    /*putme[0] = ui32Seq0Value[0] & 0xff; //Chan0  (0)
     putme[1] = ui32Seq0Value[0] >> 8;
     putme[2] = ui32Seq1Value[0] & 0xff; //Chan1 (0)
     putme[3] = ui32Seq1Value[0] >> 8;
     putme[4] = ui32Seq2Value[0] & 0xff; //Chan2 (0)
     putme[5] = ui32Seq2Value[0] >> 8;
     putme[6] = ui32Seq0Value[1] & 0xff; //Chan0 (1)
     putme[7] = ui32Seq0Value[1] >> 8;
     putme[8] = ui32Seq1Value[1] & 0xff;//Chan1 (1)
     putme[9] = ui32Seq1Value[1] >> 8;
     putme[10] = ui32Seq2Value[1] & 0xff;//Chan2 (1)
     putme[11] = ui32Seq2Value[1] >> 8;
     putme[12] = ui32Seq0Value[2] & 0xff;//Chan0 (2)
     putme[13] = ui32Seq0Value[2] >> 8;
     putme[14] = ui32Seq1Value[2] & 0xff;//Chan1 (2)
     putme[15] = ui32Seq1Value[2] >> 8;
     putme[16] = ui32Seq2Value[2] & 0xff;//Chan2 (2)
     putme[17] = ui32Seq2Value[2] >> 8;
     putme[18] = ui32Seq0Value[3] & 0xff;//Chan0 (3)
     putme[19] = ui32Seq0Value[3] >> 8;
     putme[20] = ui32Seq1Value[3] & 0xff;//Chan1 (3)
     putme[21] = ui32Seq1Value[3] >> 8;
     putme[22] = ui32Seq2Value[3] & 0xff;//Chan2 (3)
     putme[23] = ui32Seq2Value[3] >> 8;*/
    
    //send averaged values
    putme[0] = avgSeq0 & 0xff; //Chan0  (0)
    putme[1] = avgSeq0 >> 8;
    putme[2] = avgSeq1 & 0xff; //Chan1 (0)
    putme[3] = avgSeq1 >> 8;
    putme[4] = avgSeq2 & 0xff; //Chan2 (0)
    putme[5] = avgSeq2 >> 8;
    
    //Debug
    //UARTprintf("chan0:%d | %d | %d | %d \n",ui32Seq0Value[0],ui32Seq0Value[1],ui32Seq0Value[2],ui32Seq0Value[3]);
    //UARTprintf("chan1:%d | %d | %d | %d \n",ui32Seq1Value[0],ui32Seq1Value[1],ui32Seq1Value[2],ui32Seq1Value[3]);
    //UARTprintf("chan2:%d | %d | %d | %d \n",ui32Seq2Value[0],ui32Seq2Value[1],ui32Seq2Value[2],ui32Seq2Value[3]);
    
    if(RingBufFull(&adcBuff)){ //light RED LED if buffer is full (means lost data)
        GPIOPinWrite(GPIO_PORTF_BASE, LED_RED|LED_GREEN|LED_BLUE, LED_RED);
    }
    
    RingBufWrite(&adcBuff,putme,6);
    
    //otherwise clear here (might loose some data points)
    //ADCIntClear(ADC0_BASE,0);
    ADCIntClear(ADC0_BASE,2);
}



void printADC(){
    uint8_t readme[6];
    uint16_t chan1, chan2, chan3;
    uint16_t bufload;
    uint32_t send1,send2;
    uint32_t start = 122, checksum; // 0x00FF start byte
    bufload = RingBufUsed(&adcBuff);
    
    if(bufload>6){
        RingBufRead(&adcBuff,readme,6);
        
        chan1 = readme[1];
        chan1 = (chan1 << 8)+readme[0];
        chan2 = readme[3];
        chan2 = (chan2 << 8)+readme[2];
        chan3 = readme[5];
        chan3 = (chan3 << 8)+readme[4];
        
        //UARTprintf("chan1:%d | chan2:%d | chan3:%d\n",chan1,chan2,chan3); //for putty
        //check sum
        checksum = chan1 ^ chan2 ^ chan3;
        
        //prepare stream to send, 7 bytes in send1 and send2
        // start byte + chan1 + chan2 + chan3 + checksum:
        send1 = chan2 << 20; //put chan2 in the enf of send1
        send1 = send1 + (chan1<<8);//add chan1 and leave room for start byte
        send1 = send1 + start; //add start byte to stream
        send2 = checksum << 12; //put checksum and leave space for chan3
        send2 = send2 + chan3; //add chan3
        
        UARTCharPut(UART1_BASE,send1); //start byte
        UARTCharPut(UART1_BASE,send1 >> 8); //least sig byte of chan1
        UARTCharPut(UART1_BASE,send1 >> 16); //4 most sig bits of chan1 and least sig 4 bits of chan2
        UARTCharPut(UART1_BASE,send1 >> 24); //most sig byte of chan2
        UARTCharPut(UART1_BASE,send2); // least sig byte of chan3
        UARTCharPut(UART1_BASE,send2 >> 8); //4 most sig bits of chan3 and least sig 4 bits of checksum
        UARTCharPut(UART1_BASE,send2 >> 16); //most sig byte of checksum
        
    }
    
}

// Main ----------------------------------------------------------------------------------------------
int main(void){
    
    uint32_t ui32convrate = 7000;
    uint32_t ui32period = SysCtlClockGet()/ui32convrate;
    
    // Enable lazy stacking
    FPULazyStackingEnable();
    
    //Initialize circular buffer
    RingBufInit(&adcBuff,ui8buff,buffSize);
    
    // Set the system clock to run at 20Mhz off PLL with external crystal as reference.
    SysCtlClockSet(SYSCTL_SYSDIV_10 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);
    
    // Initialize the UART and write status.
    ConfigureUART1();
    UARTprintf("ADC CRASH OVERRIDE!! Using clock %d and period %d\n",  SysCtlClockGet(),ui32period);
    
    // Enable LEDs
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, LED_RED|LED_BLUE|LED_GREEN);
    GPIOPinWrite(GPIO_PORTF_BASE, LED_RED|LED_GREEN|LED_BLUE, LED_BLUE);
    
    //Initialize ADC
    ADCinit(ui32period);
    //UARTprintf("ADC Sequence Initiated!\n");
    
    
    ADCIntClear(ADC0_BASE,2);
    ADCIntEnable(ADC0_BASE,2);
    IntEnable(INT_ADC0SS2);
    IntMasterEnable();
    // Create print variables
    
    while(1){
        printADC();
    }
    
    
}

thanks everyone,

Ricardo Cabrita

  • Your ADC runs @ 16MHz - thus 16MHz/7000 = (your) 2285.   (on the nose!)

    Even thought your system clock far exceeds 16MHz - that's the rate the designers chose for your device - and that 16MHz is consistently routed to the ADC - independent of your system clock.    Note that you must maintain system clock @ 16MHz (or above) to satisfy the ADC.

    Far back in your MCU manual you may read "all about" the ADC specifications - 16MHz is clearly listed...

  • Thank you for the reply.

    Sorry I'm a bit confused, so even though SysCtlClockGet() returns 20MHz, ADC is still running at 16MHz ? And still, if I'm doing SysCtlClockGet()/7000 I just find it strange it returns 2285 when printing SysCtlClockGet() returns 20MHz.

    Not that I am doubting what you are saying, I am just confused.

    But if you are right, and if the ADC conversion is running at 7KHz, I am still getting more data points than I should, so there must be something else wrong with the code...

    thanks once again,
    Ricardo
  • My friend - I've provided the, "What, where" in description of the ADC running at (only) 16MHz - have I not?   The 2285 - as mentioned earlier - results from 16,000,000/7,000.

    The ADC does not (always) run @ system clock speed!   (this appears to be your expectation)  ADC runs @ system clock - if an only if - the system clock is 16MHz.   (one hopes that is now clear)   MCU manual is thick - but it does describe.

    Now your initial post raises grave concern - you write, "I was counting on 1750K samples/second per channel.   However, i am getting more than that!"   Is not 1750K equal to 1,750,000 samples?   Yet - your MCU is spec'ed @ a "peak" conversion rate of 1 sample/µS - nearly half of your listing.   And - if that 1µS sample rate can be maintained (I doubt that) it is across ALL channels - not "per channel."

    Faster & more capable ADCs are available from this vendor.   You may have to adjust your expectations a bit downward - so that they better match MCU - ADC's "reality."

    Lastly - you note that you're getting "more data points than you should" - yet I cannot find any particular data you've presented - in justification of that claim...

  • The clock matter is clear now, thank you.

    And I am so sorry, I meant 1750 samples/second not 1750K. Conversion frequency is 7k, since I'm averaging 4 samples on each interrupt, it should give 7000/4 = 1750.
    However, I am, it seems, getting more data points than expected. Meaning, when I do a signal measurement for say x seconds, I get n sample points. When dividing n by 1750 I get more seconds then the actual duration (x seconds) of the measurement. This is what got me questioning the timer period in the first place.

    I'll edit the question to 1750 instead of 1750K, sorry once gain.

    Ricardo
  • Review of your code shows that you've employed 3 different ADC Sequences - while you can do that I'm unsure if  "all three" of those ADC sequences can be fully & properly launched by a single, Timer trigger.   (firm/I use many ARM MCUs from several vendors - I've been away from this API long enough to cloud my recall.)

    What I can offer is the suggestion that you (temporarily) slow your timer - yielding fewer conversions - and this slower rate may better identify: "if, when and where" your code deviates from the desired.   It should also prove useful for you to "toggle" a GPIO Output upon each entry into the ADC handler - and observe that toggle via a scope or Led.   As you're concerned about the conversion rate - which is "Timer triggered" - it's worthwhile to test/verify the triggering Timer.

    You may also test for all 3 Sequencers being triggered by inserting break points w/in each - and observing that code location being reached.

    Final tip - we employ very stable reference voltages when test/verifying our ADCs.   By selecting references which are well separated in voltage level - it becomes easy to confirm that each ADC pin is properly configured and that the trigger has performed to your desire...

  • I think i have solved the problem, at least it seems to be working as exptected.
    I didn't understand that the 7K frequency of the Timer was for triggering the ADC interruption, thats why I was expecting 1750 points per second and instead I was getting roughly 7K.
    When I tried a 1Hz frequency, and I got 1 point per second despite averaging every 4 samples, i finally understood that this was the frequency of interruption.

    Thank you very much for taking your time to look into my problem. It seems it was after all a simple matter, I should have seen this sooner.
    I will verify your answear about the ADC clock for clearing up that issue for me, I understand now that the ADC clock is one thing, and the timer configuration for interruptions and whatnot is another.

    kind regards,
    Ricardo
  • Thank you - and you are NOT alone in missing that "ADC operation" at - and only at - 16MHz clock rate.

    By following the suggestion to, "Run & Test @ far slower rate" you further (unlocked) the secrets of, Timer triggered - ADC operation.

    Your care & courtesy is appreciated - the "KISS" method we've (together) employed - most always - results in user success!