• 0~3.3V
• 12bit

// Potentiometer is connected to GPIO 34 (Analog ADC1_CH6)
const int potPin = 34;

// variable for storing the potentiometer value
int potValue = 0;

void setup() {
Serial.begin(115200);
delay(1000);
}

void loop() {
Serial.println(potValue);
delay(500);
}


The first part of the OScope project is to implement the Arduino sketch to read the input values from an analog pin. In this article will describe how to achieve a reliable sampling of analog signals up to 615 KHz using some advanced techniques.

The following piece of code takes 1000 samples using the analogRead() calculates some statistics.

void setup()
{
Serial.begin(115200);
pinMode(A0, INPUT);
}

void loop()
{
long t0, t;

t0 = micros();
for(int i=0; i<1000; i++) {
}
t = micros()-t0;  // calculate elapsed time

Serial.print("Time per sample: ");
Serial.println((float)t/1000);
Serial.print("Frequency: ");
Serial.println((float)1000*1000000/t);
Serial.println();
delay(2000);
}


This code gives 112us per sample for a 8928 Hz sampling rate.

So how can we increase sampling rate?

We now need a little more details. The ADC clock is 16 MHz divided by a 'prescale factor'. The prescale is set by default to 128 which leads to 16MHz/128 = 125 KHz ADC clock. Since a conversion takes 13 ADC clocks, the default sample rate is about 9600 Hz (125KHz/13). Adding few lines of code in the setup() function we can set an ADC prescale to 16 to have a clock of 1 MHz and a sample rate of 76.8KHz.

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

void setup()
{
...


The real frequency measured with the test program is 17us per sample for a 58.6 KHz sampling rate.

The following table shows prescale values with registers values and theoretical sample rates. Note that prescale values below 16 are not recommended because the ADC clock is rated.

2 0 0 1 8 615
4 0 1 0 4 307
8 0 1 1 2 153
16 1 0 0 1 76.8
32 1 0 1 0.5 38.4
64 1 1 0 0.25 19.2
128 1 1 1 0.125 9.6

### Interrupts

A better strategy is to avoid calling the analogRead() function and use the 'ADC Free Running mode'. This is a mode in which the ADC continuously converts the input and throws an interrupt at the end of each conversion. This approach has two major advantages:

1. Do not waste time waiting for the next sample allowing to execute additional logic in the loop function.
2. Improve accuracy of sampling reducing jitter.

In this new test program I set the prescale to 16 as the example above getting a 76.8 KHz sampling rate.

int numSamples=0;
long t, t0;

void setup()
{
Serial.begin(115200);

ADMUX |= (1 << REFS0);  // set reference voltage

// sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]
// for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles

}

{
numSamples++;
}

void loop()
{
if (numSamples>=1000)
{
t = micros()-t0;  // calculate elapsed time

Serial.print("Sampling frequency: ");
Serial.print((float)1000000/t);
Serial.println(" KHz");
delay(2000);

// restart
t0 = micros();
numSamples=0;
}
}