Sampling from ADC

Hey folks,

I'm working on a ultrasonic range finder project with my new Due, and I had a quick question re: sampling from the ADC.

Basically, I want to be able to sample a signal for a period of time (40-50 ms) at about 100,000 khz. I've wrote the code below that allows me to change the sample num of samples and the sampling frequency; it uses a timer to handle the sampling. However, I wondered, should I be just running a loop in a function to collect the samples instead of using the timer? Would that result in a consistent sample frequency (sampling at ADC speed)?

Code is below:

//timer library
#include <ARMtimer.h>
#undef HID_ENABLED

int result = 0;

int sampleCount = 0;
int sampleFrequency = 60000;
// unsigned long sampleTime = 1000; //in millis
const int numSamples = 20000;
int resultArray[numSamples];
unsigned long programtime = 0;

//boolean readready = FALSE;


//setup adc function

void prepareADC(){

  //adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST); 
  adc_init(ADC, 42000000, 21000000, 3); 
// adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST);
  adc_configure_timing(ADC, 0, ADC_SETTLING_TIME_3, 1); 
  //Port 0 = ADC_Channel_7
  adc_enable_channel(ADC, ADC_CHANNEL_7); 
  
  ADC->ADC_MR |=0x80; // free running

//adc_set_comparison_channel(ADC, ADC_CHANNEL_7);

//adc_set_comparison_mode(ADC, ADC_EMR_CMPMODE_IN);

//adc_set_comparison_window(ADC, 0, 3300); 

  adc_enable_interrupt(ADC, ADC_IER_DRDY); 
   
//adc_configure_trigger(ADC, ADC_TRIG_SW, 0); 

  NVIC_EnableIRQ(ADC_IRQn);
  adc_start(ADC);
}


void ADC_Handler(void)  {
       //Check the ADC conversion status
       if ((adc_get_status(ADC) & ADC_ISR_DRDY) == ADC_ISR_DRDY)
       {
       //Get latest digital data value from ADC and can be used by application
           result = adc_get_latest_value(ADC); 
     //     Serial.println("test"); 
       }
   }

volatile void receivedata()
{

  //this should happen sample frequency 
if (sampleCount < numSamples){
      //put analog value into array
   resultArray[sampleCount] = result;
 //  Serial.println(resultArray[sampleCount]);
   // Serial.println(sampleCount);
   // Serial.println(numSamples);
 // Serial.println(result);
  }  
  
 sampleCount = sampleCount + 1;
// Serial.println(sampleCount);
    
if (sampleCount == numSamples){
 sampleCount = 0; 
//  Serial.println(sampleCount);
}
    
}


//function to send array to serial

void sendData(){
  for (int j=0; j<= numSamples; j++){
  Serial.println(resultArray[j]);
  } 
}


void establishContact() {
  while (Serial.available() <= 0) {
    Serial.print('A');   // send a capital A
    delay(300);
  }
}


void setup() {

   //setup serial
   Serial.begin(115200);
 
   
   prepareADC();
   
 // Start timer. Parameters are:

  // TC1 : timer counter. Can be TC0, TC1 or TC2
  // 0   : channel. Can be 0, 1 or 2
  // TC3_IRQn: irq number. See table.
  // 2  : frequency (in Hz)
  // myTimedPrint : function pointer to the function you want executed

  //read 100 times per second 
  
  // Paramters table:
  // TC0, 0, TC0_IRQn
  // TC0, 1, TC1_IRQn
  // TC0, 2, TC2_IRQn
  // TC1, 0, TC3_IRQn
  // TC1, 1, TC4_IRQn
  // TC1, 2, TC5_IRQn
  // TC2, 0, TC6_IRQn
  // TC2, 1, TC7_IRQn
  // TC2, 2, TC8_IRQn
  

  // put your setup code here, to run once:
  
  //setup timer to trigger 10hz
  //setup adc to capture on trigger
  //on trigger add value to array
   programtime = millis();
}

void loop() {
  
//   programtime = millis();
   
  // Serial.println(programtime);
 
//  if (programtime > sampleTime+1){
//    stopTimer(TC3_IRQn);
//  }
  
  
if (Serial.available() > 0) {
    // read the serial buffer;
    // you don't care about the value of
    // the incoming byte, just that one was
    // sent:
    int inByte = Serial.read();
    
    startTimer(TC1, 0, TC3_IRQn, sampleFrequency, receivedata);
//   delay(100);
//for (int i = 0; i<= numSamples-1; i++){
//  Serial.print(resultArray[i]);
//    Serial.write((uint8_t *)resultArray[numSamples], numSamples);
  sendData();
   // Serial.println("\n");
    stopTimer(TC3_IRQn);
    sampleCount = 0; 


    }


 
 
// check to see whether there is a byte available
// to read in the serial buffer:
 // if (Serial.available() > 0) {
    // read the serial buffer;
    // you don't care about the value of
    // the incoming byte, just that one was
    // sent:
//    int inByte = Serial.read();
//    }
// if (sampleCount == numSamples){
//    sampleCount = 0;
//    Serial.println('new sample');
    //start another sample
//  }
}

You should explain where to get the library you use...

#include <ARMtimer.h>

Without using a timer to schedule conversions you are at the mercy of other interrupt handlers in the system - this
may or may not be significant in your particular case. You can either just run conversions back-to-back, poll the time
to schedule them, or schedule from a timer interrupt (which is what your code seems to be doing),

Using a timer to schedule allows the rest of your code to proceed with something else at the same time, again this
could be useful - depends on the rest of the sketch...

Mark,

The Timer library can be found at: GitHub - ivanseidel/DueTimer: ⏳ Timer Library fully implemented for Arduino DUE

I guess what I'm wondering is what is the 'correct' way to do this. I've noticed lots of bad programming practices when it comes to Arduino, and I'm trying to set this up in the best way possible. Or is there no 'best-practice' when it comes to this stuff?

Cheers,
Mike

Well there are other concerns that affect the decision. For instance you might need to handle other
stuff in the same time period, so offloading to timer interrupts is good.

Or you might find that the ADC performs better (in the analog domain) with the processor sleeping
so a combination of timer polling to start conversions and interrupts to wake up again might be needed.

How accurate should your sample timebase be? You can answer that, I can only guess... You might
find other interrupts in the system to be an issue and want to turn them off during this time?

Everything is going to be a compromise in a microcontrol, resources are finite...

Mark,

Thanks for your answer. I appreciate there are lots of issues to consider but you've pointed out that at least I'm not completely on the wrong track :slight_smile:

Ideally I want the timebase to be fairly consistent as my place is to pull the data into Matlab for digital filtering, echo detection and display.

I wished there was a good book I could read that goes beyond the 'blinking LED'. :slight_smile:

Thanks again,
Mike