[some exploration]
The analogRead() waits time in its call until the measurement is ready.
I've split analogRead into 3 calls that allows you to send a measurement while a next analogRead is in progress.
you should convert the raw measurement to float on the receiving side (probably a PC that is fast enough to handle that)
Don't know if it is fast enough for you - this makes 10K integer samples in 3.64 seconds [UNO 16Mhz]
//
// FILE: asyncAnalogRead.pde
// AUTHOR: Rob Tillaart
// DATE: 09-jun-2012
//
// PUPROSE: experimental async analogRead
//
unsigned long start = 0;
unsigned int counter = 0;
void setup()
{
Serial.begin(115200);
start = millis();
counter++;
AR_Start(0); // start the analogRead
}
void loop()
{
while (counter < 10000)
{
while(!AR_Ready()); // wait until ready
int val = AR_Value(); // read its value
AR_Start(0); // start a new reading
counter++;
Serial.println(val);
}
Serial.println(millis() - start);
while(1);
}
//
// This is the code of analogRead split in 3 separate functions
// not nice but it works
//
#include "wiring_private.h"
#include "pins_arduino.h"
void AR_Start(uint8_t pin)
{
#if defined(ADCSRB) && defined(MUX5)
// the MUX5 bit of ADCSRB selects whether we're reading from channels
// 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#endif
// set the analog reference (high two bits of ADMUX) and select the
// channel (low 4 bits). this also sets ADLAR (left-adjust result)
// to 0 (the default).
#if defined(ADMUX)
ADMUX = (DEFAULT << 6) | (pin & 0x07);
#endif
// without a delay, we seem to read from the wrong channel
//delay(1);
sbi(ADCSRA, ADSC);
}
boolean AR_Ready()
{
// ADSC is cleared when the conversion finishes
return bit_is_set(ADCSRA, ADSC)==0;
}
int AR_Value()
{
// we have to read ADCL first; doing so locks both ADCL
// and ADCH until ADCH is read. reading ADCL second would
// cause the results of each conversion to be discarded,
// as ADCL and ADCH would be locked when it completed.
int low = ADCL;
int high = ADCH;
// combine the two bytes
return (high << 8) | low;
}
The real performance eater is the communication.
If your receiving program can handle 500000 baud, you set Serial.begin(500000); // or 230400 345600 works quite well too
The Arduino can send it at that speed, but be aware the IDE monitor cannot handle it
I measured ~1.75 seconds for 10K reads == 5710 samples per second
For reference
unsigned long start = 0;
unsigned int counter = 0;
void setup()
{
Serial.begin(500000);
start = millis();
}
void loop()
{
while (counter < 10000)
{
Serial.println(analogRead(0));
counter++;
}
Serial.println(millis() - start);
while(1);
}
The normal analogRead() make 10K samples in 2.8 seconds == 3570 samples/sec
A next optimization would be sending the raw samples in binary format
unsigned long start = 0;
unsigned int counter = 0;
void setup()
{
Serial.begin(500000);
start = millis();
}
void loop()
{
while (counter < 10000)
{
int x = analogRead(0);
Serial.write(x >> 8);
Serial.write(x & 255);
counter++;
}
Serial.println();
Serial.println(millis() - start);
while(1);
}
10K samples in 1.44 second == 6944 samples per second
doing this with the async way ... it makes 10K samples in 1.119 second = 8936 samples per second (now we are getting somewhere)
//
// FILE: asyncAnalogRead.pde
// AUTHOR: Rob Tillaart
// DATE: 09-jun-2012
//
// PUPROSE: experimental async analogRead
//
unsigned long start = 0;
unsigned int counter = 0;
void setup()
{
Serial.begin(500000);
start = millis();
counter++;
AR_Start(0); // start the analogRead
}
void loop()
{
while (counter < 10000)
{
while(!AR_Ready()); // wait until ready
int val = AR_Value(); // read its value
AR_Start(0); // start a new reading
counter++;
Serial.write(val >> 8);
Serial.write(val & 255);
}
Serial.println();
Serial.println(millis() - start);
while(1);
}
//
// This is the code of analogRead split in 3 separate functions
// not nice but it works
//
#include "wiring_private.h"
#include "pins_arduino.h"
void AR_Start(uint8_t pin)
{
#if defined(ADCSRB) && defined(MUX5)
// the MUX5 bit of ADCSRB selects whether we're reading from channels
// 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#endif
// set the analog reference (high two bits of ADMUX) and select the
// channel (low 4 bits). this also sets ADLAR (left-adjust result)
// to 0 (the default).
#if defined(ADMUX)
ADMUX = (DEFAULT << 6) | (pin & 0x07);
#endif
// without a delay, we seem to read from the wrong channel
//delay(1);
sbi(ADCSRA, ADSC);
}
boolean AR_Ready()
{
// ADSC is cleared when the conversion finishes
return bit_is_set(ADCSRA, ADSC)==0;
}
int AR_Value()
{
// we have to read ADCL first; doing so locks both ADCL
// and ADCH until ADCH is read. reading ADCL second would
// cause the results of each conversion to be discarded,
// as ADCL and ADCH would be locked when it completed.
int low = ADCL;
int high = ADCH;
// combine the two bytes
return (high << 8) | low;
}
If eight bit is enough one can reduce the communication with 50% (and improving it too as a missing byte will not screw the signal!)
A quick test did not show improvements.
but you can tweak the AD conversion itself, ==> probably loosing accuracy and precision in the analogRead().
unsigned long start = 0;
unsigned int counter = 0;
void setup()
{
Serial.begin(500000);
// prescale clock to 16
bitClear(ADCSRA,ADPS0);
bitClear(ADCSRA,ADPS1);
bitSet(ADCSRA,ADPS2);
start = millis();
}
void loop()
{
while (counter < 10000)
{
int x = analogRead(0);
Serial.write(x >> 8);
Serial.write(x & 255);
counter++;
}
Serial.println();
Serial.println(millis() - start);
Serial.println(millis() - start);
while(1);
}
10K samples in 0.522 seconds .... almost 20K samples per second (I expect missing bytes, all disclaimers apply!!)
Note this is about the maximum # samples you can send over 500Kbaud!
The ultimate test, tweak the prescaler clock, do it async, and only 8 bit .... ==> terminal app could not handle it anymore.
unsigned long start = 0;
unsigned int counter = 0;
void setup()
{
Serial.begin(500000);
// prescale clock to 16
bitClear(ADCSRA,ADPS0);
bitClear(ADCSRA,ADPS1);
bitSet(ADCSRA,ADPS2);
start = millis();
counter++;
AR_Start(0); // start the analogRead
}
void loop()
{
while (counter < 10000)
{
while(!AR_Ready()); // wait until ready
int val = AR_Value(); // read its value
AR_Start(0); // start a new reading
counter++;
Serial.write(val);
//Serial.write(val & 255);
}
Serial.println();
Serial.println(millis() - start);
while(1);
}
None of the code above is tested (extensively), but it shows there is room to improve the speed of your sampling application.
updated a few texts for readablility