Arduino provides an convenient way to read analog input this using the analogRead() function. Without going into much details the analogRead() function takes 100 microseconds leading to a theoretical sampling rate of 9600 Hz. You can read more about this topic here.
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++) { analogRead(A0); } 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?
Speedup the analogRead() function
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() { sbi(ADCSRA, ADPS2); cbi(ADCSRA, ADPS1); cbi(ADCSRA, ADPS0); ...
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.
Prescale | ADPS2 ADPS1 ADPS0 | Clock freq (MHz) | Sampling rate (KHz) |
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:
- Do not waste time waiting for the next sample allowing to execute additional logic in the loop function.
- Improve accuracy of sampling reducing jitter.
int numSamples=0; long t, t0; void setup() { Serial.begin(115200); ADCSRA = 0; // clear ADCSRA register ADCSRB = 0; // clear ADCSRB register ADMUX |= (0 & 0x07); // set A0 analog input pin ADMUX |= (1 << REFS0); // set reference voltage ADMUX |= (1 << ADLAR); // left align ADC value to 8 bits from ADCH register // sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles] // for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles //ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // 32 prescaler for 38.5 KHz ADCSRA |= (1 << ADPS2); // 16 prescaler for 76.9 KHz //ADCSRA |= (1 << ADPS1) | (1 << ADPS0); // 8 prescaler for 153.8 KHz ADCSRA |= (1 << ADATE); // enable auto trigger ADCSRA |= (1 << ADIE); // enable interrupts when measurement complete ADCSRA |= (1 << ADEN); // enable ADC ADCSRA |= (1 << ADSC); // start ADC measurements } ISR(ADC_vect) { byte x = ADCH; // read 8 bit value from ADC 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; } }
If you want to learn more on ADC Free Running mode and tweaking ADC register you can look at the following pages.
AVR Guide - Analog Inputs
Instructables - Girino - Fast Arduino Oscilloscope
Instructables - Arduino Audio Input
Arduino Forum - Faster Analog Read
How are you transferring the samples to your computer ?
ReplyDeleteI'm using serial for that and that's seriously affecting the sample rate.
You can set a higher bitrate for the serial port.
DeleteTry - Serial.begin(115200);
I have changed the example.
Hi how can i transfer live data from arduino to matlab in PC and make live plot
DeleteThanks for your prompt reply. I made the following changes to your code to achieve the desired result.
ReplyDelete(1)
ADMUX |= (1 << REFS0); // set reference voltage to internal
ADMUX |= (1 << REFS1);
(2)
ISR(ADC_vect)
{
byte x = ADCH; // read 8 bit value from ADC
numSamples++;
}
The result of this is that I'm getting constant value(255) through the serial even though different voltages is being applied through the serial. What I'm thinking is that maybe the ADC is putting out samples to fast for the Serial to handle or maybe there is some other problem.
My main objective is to get the samples to my laptop as soon as they are put into the register by ADC. If there is any alternative way to do that please suggest. If not, please suggest changes in the code to fix the serial problem.
Thanks!
Edit:
Delete(2)
ISR(ADC_vect)
{
byte x = ADCH; // read 8 bit value from ADC
Serial.println(x);
numSamples++;
}
The result of this is that I'm getting constant value(255) through the serial even though different voltages is being applied to the ANALOG A0 pin
I have found the problem.
DeleteReplace
ADMUX |= (A0 & 0x07);
to
ADMUX |= (0 & 0x07);
I have also updated my OScope project (http://yaab-arduino.blogspot.it/p/oscope.html)
DeleteCan you try to download the new version and tell me what do you think?
https://drive.google.com/file/d/0B6PxDhwK5tSJaTFka254WUk1NjA/view?usp=sharing
Thank you for your help. It worked! I used your code to sample data for the myograph me and my team created as a part of course project. I appreciate your help.
DeleteSure! I'll give it a try.
How could we adapt the code for A0 and A1
ReplyDeleteHello Bruno, Very interesting and useful article! Complimenti!
ReplyDeleteDo you think that it's possible to sample the data at 8khz and save all on the sd card?
Thanks
Stefano
I don't have an SD card reader so I cannot try.
ReplyDeleteI think it should be possible but I don't think you can sample 'exactly' at 8KHz.
You can use the free running mode to sample at 9.6Khz and write to SD when a new sample is available.
How to change ADMUX |= (0 & 0x07); if i want to read from all 6 analog pins?,thank you
ReplyDeleteThis comment has been removed by the author.
DeleteDid you find the answer?
Deletese the last part of https://sites.google.com/site/qeewiki/books/avr-guide/analog-input
DeleteWhy is is x local?
ReplyDeleteAnd shouldn't numsamples be declared volatile?
hi bruno, how can u acces the analog variable?
ReplyDeleteHow I use the analog data?
ReplyDeleteHow I use this in an arduino due?
ReplyDeleteCan I use this for nodeMCU (development board based on esp8266)?
ReplyDeleteYou made this type of fascinating piece to peruse, giving each subject illumination for people to pick up learning. A debt of gratitude is in order for imparting the such data to us to peruse thisbeta afp browser
ReplyDeleteThis is seriously nice stuff, but if I want to check a different input pin to A0 then what do I say? Your "ADMUX |= (0 & 0x07); // set A0 analog input pin" line seems to set A0, but how could I set A1, A2, A3, A4 or A5?
ReplyDeleteThank you
ReplyDeleteBest Category of classy and glamorous Female Models Lahore Escorts A stunning Girls who make your time more pleasurable We are famous for our Reasonable Rate and High Class Service Escorts Services in Lahore are well- educated and very well- mannered you will feel very comfortable with her Call us now and get Best Price for your fantasy night.
hi sir i could not understand what is the usage of the void loop() function for taking value from arduino and transfer it to the pc
ReplyDeleteThis is the best post about ADC that I have ever read. Congratulations
ReplyDeleteHi, Thanks for this useful post. How can I read 10bit instead of 8bit even it is less fast. For me, it can be little bit more slow but I need V/1023 resolution.
ReplyDelete
ReplyDeleteHi, Amazing your article you know I'm too lazy to sign up an account just for comment your article. it's really good and helping dude. thanks!
Jitter Speed Test
Thank you for sharing, you can also find more information about everything in life like health food, sports ... here. Have a nice day
ReplyDeleteGamesbx 2
yorg 3
Bullet force
chompers
AV Club
BBC Food
BBC Sport
bleacher repor
Cinema Blend
Delicious