根据官网说明, analogRead( ) 大约要 100us:
http://arduino.cc/en/Reference/analogRead
也就是说, 一秒最多只能读取大约一万次(10K), 更正确的说, 理论上 sampling rate 是 9600 Hz, 接近 10KHz, 但这是因为 Arduino 的 ADC 之 Prescaler 被设为 128;
所以, 假设 Arduino 的频率(Clock)是 16MHz, 则 ADC clock = 16MHz / 128 = 125KHz;
一次 ADC 转换要踢它13下, 就是要 13 clock (tick), 于是变成 125KHz/13 = 9600Hz
请注意, 这是因为在 analogRead( ) 内必须等 ADC 取样完成才会返回,
关于 analogRead( ) 的源代码与解说可以参考这:
http://garretlab.web.fc2.com/en/arduino/inside/arduino/wiring_analog.c/analogRead.html
不过 9600Hz 的sampling rate 只是理论值, 因为你的程序本身也要花时间, 调用函数进出需要时间, for Loop 本身也要时间, 把 sample(采样) 到的值加总到 total 也要时间 !
所以, 如果你用一个 for Loop 全力一直做 analogRead(A0),
那一秒钟也只能读取大约 8925 次, 达不到理论值 9600次 !!
不相信, 用你的 Arduino 测试一下:
//
// test A0 sampling rate -- by tsaiwn@cs.nctu.edu.tw
const int pin = A0;
const int n = 1000; // sample 采样 1000 次
void setup() {
Serial.begin(9600);
for(int i=0; i< 543; i++) analogRead(pin); // 热身 :-)
Serial.println(String("Sample ") + n + " times, pin=" + pin);
Serial.flush( );
delay(568);
}
void loop( ) { //
unsigned long begt, runt, total;
total = 0; // clear before sampling
begt = micros();
for(int i=0; i< n; i++) {
total += analogRead(pin);
}
runt = micros() - begt; // elapsed time
Serial.println(String("Average=") + total/n);
Serial.print(String("Time per sample: ")+runt/1.0/n +"us");
Serial.println(String(", Frequency: ")+1000000.0/runt*n +" Hz");
delay(5566);
}// loop(
你看, 以上程序已经简短到无法再简短了,
但是经过实际测试, 结果 sampling rate 只有不到 9KHz,(我测试结果大约 8925 次)
这是因为除了 analogRead( )等待 ADC 转换的延迟外, 程序中 for Loop 与 tatal += 也都要一些时间 !
有兴趣的可以看看 analogRead( ) 这函数的源代码:
int analogRead(uint8_t pin) {
extern unsigned char analog_reference;
uint8_t low, high;
const unsigned char bitADSC = (1 << ADSC);
if (pin >= 14) pin -= 14; // allow for channel or pin numbers
// 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).
ADMUX = (analog_reference << 6) | (pin & 0x07);
// start the conversion
ADCSRA |= (1 << ADSC); // sbi(ADCSRA, ADSC);
// ADSC is cleared when the conversion finishes
while (bit_is_set(ADCSRA, ADSC)); // while(ADCSRA & bitADSC) ;
// 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.
low = ADCL;
high = ADCH;
// combine the two bytes
return (high << 8) | low;
}
看到了吧, analogRead( ) 里面有个如下的 while Loop:
while (bit_is_set(ADCSRA, ADSC));
这句意思是要一直等到 ADC 转换完成把 ADSC 这 bit 清除为 0;
然后才可以读取 ADCL 与 ADCH, 合成总共 10 bit, 代表 0 ~ 1023 的取样值 !
刚刚说过, 理论值 9.6KHz 是因为 ADC 的 Prescaler设为128;
不过, Prescaler是可以改的!
只要把 ADC 的 Prescaler 改小, 就可以加快 ADC 转换速度与 analogRead( )速度!
例如, 把 ADC 的 Prescaler 改为 16,
则理论的 Sample Rate 可达 16MHz / 16 / 13 = 76.8KHz
不过, 经过实测只能达到大约 58KHz
若 ADC 的 Prescaler 改为 8,
则理论的 Sample Rate 可达 153KHz, 但实测只有大约93.5KHz
可是一般不建议把 Prescaler 设在 16以下, 否则ADC转换不太准 !!
以下是测试用 Prescaler 16, 采样频率 大约 58KHz;
但在以下程序中,
我已经帮忙写了数个可以把 ADC 的 Prescaler 设为各种值的函数:
//
// speed up sampling rate -- by tsaiwn@cs.nctu.edu.tw
const int pin = A0;
const int n = 1000; // sample 采样 1000 次
void setup() {
Serial.begin(9600);
//setP64( ); // Prescaler = 64
//setP32( ); // Prescaler = 32
//setP8( ); // Prescaler = 8
setP16( ); // Prescaler = 16
//setP128( ); // Prescaler = 128 = default
for(int i=0; i< 543; i++) analogRead(A0); // 热身 :-)
Serial.println(String("Sample ") + n + " times, pin=" + pin);
Serial.flush( );
delay(568);
}
void loop( ) { //
long begt, runt, total;
total = 0; // clear before sampling
begt = micros();
for(int i=0; i< n; i++) {
total += analogRead(pin);
}
runt = micros() - begt; // elapsed time
Serial.println(String("Average=") + total/n);
Serial.print(String("Time per sample: ")+runt/1.0/n +"us");
Serial.println(String(", Frequency: ")+1000000.0/runt*n +" Hz");
delay(5566);
}// loop(
void setP16( ) {
Serial.println("ADC Prescaler = 16"); // 100
ADCSRA |= (1 << ADPS2); // 1
ADCSRA &= ~(1 << ADPS1); // 0
ADCSRA &= ~(1 << ADPS0); // 0
}
void setP8( ) { // prescaler = 8 ; 不建议设为 16 以下!
Serial.println("ADC Prescaler = 8"); // 011
ADCSRA &= ~(1 << ADPS2); // 0
ADCSRA |= (1 << ADPS1); // 1
ADCSRA |= (1 << ADPS0); // 1
}
void setP4( ) { // prescaler = 4 ; 不建议设为 16 以下!
Serial.println("ADC Prescaler = 4"); // 010
ADCSRA &= ~(1 << ADPS2); // 0
ADCSRA |= (1 << ADPS1); // 1
ADCSRA &= ~(1 << ADPS0); // 0
}
void setP32( ) {
Serial.println("ADC Prescaler = 32"); // 101
ADCSRA |= (1 << ADPS2); // 1
ADCSRA &= ~(1 << ADPS1); // 0
ADCSRA |= (1 << ADPS0); // 1
}
void setP64( ) { // prescaler = 64 ; // 110
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
const unsigned char PS_64 = (1 << ADPS2) | (1 << ADPS1);
Serial.println("ADC Prescaler = 64"); // 110
ADCSRA &= ~PS_128; // clear that 3 bits
ADCSRA |= PS_64; // set as 110
}
// Reference http://www.microsmart.co.za/tech ... vanced-arduino-adc/
void setP128( ) { // 默认就是这样
Serial.println("ADC Prescaler = 128"); // 111
ADCSRA |= (1 << ADPS2); // 1
ADCSRA |= (1 << ADPS1); // 1
ADCSRA |= (1 << ADPS0); // 1
} // setP128
请注意, 虽然可以把 ADC 的 Prescaler 设为很小,
但是当 Prescaler 小于 16, 用 analogRead( )读到的值将很不准确!
这有人做过实验, 如果 Prescaler 在 32 或以上大致还没问题, 参考:
http://www.gammon.com.au/forum/?id=12779
补充:
如果你还要更高的采样率Sampling Rate, 那必须使用 ADC 转换完成的中断处理,
也就是要自己写 ISR(ADC_vect) 中断程序, 不要使用 analogRead( ),
自己直接从 ADC 转换完成后的 ADCL 与 ADCH 读取答案!
以下是个简单范例, 只有读取采样 10 bits 的左边 8 bit, 所以答案是 0 到 255:
kittenblock中小学创客名师推荐的图形化编程软件
// 以下范例是只读取 10 bits 中的左边 8 bits
// 所以答案是 0…255, 如有必要, 你可以把该答案 map 到(0, 1023)
volatile unsigned long cnt;
volatile unsigned long total;
const unsigned long every = 50000;
void setup( ) {
Serial.begin(9600);
delay(123);
initADC( );
}
unsigned long n, tot;
void loop( ) {
if(cnt % every == 0) {
cli( );
n = cnt;
tot = total;
sei( );
Serial.print("n="); Serial.print(n);
Serial.print(", average=");
Serial.println(tot*1.0/n);
}
} // loop(
void initADC( ) {
cli( );
ADMUX |= (1 << REFS0); //set reference voltage
ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
/// 以上是设定 10 bit 的左边 8 bit 改放在 ADCH
ADCSRA &= ~(1 << ADPS1); // bitClear(ADPS1, ADPS1); // 101
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
ADCSRA |= (1 << ADATE); //enabble auto trigger
ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
ADCSRA |= (1 << ADEN); //enable ADC ;; bitSet(ADCSRA, ADEN);
cnt = 0;
ADCSRA |= (1 << ADSC); //start ADC measurements
sei( );
} // initADC(
volatile int adcHigh;
ISR(ADC_vect) {//when new ADC value ready
adcHigh = ADCH; //update the new value from A0 (between 0 and 255)
++cnt;
}
其他参考文件:
http://www.atmel.com/dyn/resources/prod_documents/DOC2559.PDF
http://forum.arduino.cc/index.php/topic,6549.0.html
http://www.microsmart.co.za/technical/2014/03/01/advanced-arduino-adc/
http://meettechniek.info/embedded/arduino-analog.html
http://www.instructables.com/id/Arduino-Audio-Input/step6/Sampling-rate-of-40kHz/
https://bennthomsen.wordpress.com/arduino/peripherals/continuous-adc-capture/
再补充一下, 透过 ADC 转换器,
我们还可以读取 Arduino 内部的温度传感器,
然后把温度传送到 PC 计算机上!
不过, 因为每个 Arduino 板子实际上会略有差异,
所以以下的程序你必须实际测量后调整里面 tempBias 的值:
//从 Serial monitor 印出当前温度
const int tempBias = 320; // 要实际测量后调整这
void setup ( ) {
Serial.begin (9600);
ADCSRA = bit (ADEN); // turn ADC on
ADCSRA |= bit (ADPS0) | bit (ADPS1) | bit (ADPS2); // Prescaler of 128
ADMUX = bit (REFS0) | bit (REFS1) | 0x08; // temperature sensor
delay (20); // let it stabilize
bitSet (ADCSRA, ADSC); // start a conversion
while (bit_is_set(ADCSRA, ADSC)) ;; // 等待转换完成
int value = ADC;
Serial.print ("Temperature = ");
Serial.println (value - tempBias);
} // setup(
void loop () {
;
}