I've been trying to figure out why the analogRead() function takes about four times as long on an Arduino Zero than on an Uno, when the Zero has a faster clock than the Uno. Arduinos website says that it should take around 100 microseconds, and it does on the Uno, but it takes a little over 400 microseconds on the Zero. To time it, I've been setting a digital pin high immediately before the function call and low immediately after and measuring the period on a digital osscilloscope. I'm just trying to test analogRead, so I'm not doing anything else in this code. I've tried putting the ADC in freerun mode, and it trims ~15 us off of the ~415 us it currently takes. I've also used the on-chip debugger to look at the disassembly, and the line that takes too long is always the line waiting for the conversion to complete. I was also wondering if other ARM-based Arduinos (like the Zero) take over 100 us. Thanks!
int sensorPin = A1;
int ledPin = 11;
int sensorValue = 0;
void setup() {
analogReadResolution(12);
pinMode(ledPin, OUTPUT);
}
void loop() {
digitalWrite(ledPin,HIGH);
sensorValue = analogRead(sensorPin);
digitalWrite(ledPin,LOW);
}
1 Answer 1
Arduino is universal platform, lot of wrappers to hide real hardware and it is slow. For example digital write is about 100times slower than direct access to hardware.
For example for AVR Mega328p based Arduino analogRead looks like (without macros):
int analogRead(uint8_t pin)
{
uint8_t low, high;
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
sbi(ADCSRA, ADSC);
// ADSC is cleared when the conversion finishes
while (bit_is_set(ADCSRA, ADSC));
// 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;
}
There are no extra function calls, no much extra actions and ADC is enabled in the startup code (as most of hw).
On the other side there is ARM based arduino with SAM3X8E:
uint32_t analogRead(uint32_t ulPin)
{
uint32_t ulValue = 0;
uint32_t ulChannel;
if (ulPin < A0)
ulPin += A0;
ulChannel = g_APinDescription[ulPin].ulADCChannelNumber ;
static uint32_t latestSelectedChannel = -1;
switch ( g_APinDescription[ulPin].ulAnalogChannel )
{
// Handling ADC 12 bits channels
case ADC0 :
case ADC1 :
case ADC2 :
case ADC3 :
case ADC4 :
case ADC5 :
case ADC6 :
case ADC7 :
case ADC8 :
case ADC9 :
case ADC10 :
case ADC11 :
// Enable the corresponding channel
if (adc_get_channel_status(ADC, ulChannel) != 1) {
adc_enable_channel( ADC, ulChannel );
if ( latestSelectedChannel != (uint32_t)-1 && ulChannel != latestSelectedChannel)
adc_disable_channel( ADC, latestSelectedChannel );
latestSelectedChannel = ulChannel;
g_pinStatus[ulPin] = (g_pinStatus[ulPin] & 0xF0) | PIN_STATUS_ANALOG;
}
// Start the ADC
adc_start( ADC );
// Wait for end of conversion
while ((adc_get_status(ADC) & ADC_ISR_DRDY) != ADC_ISR_DRDY)
;
// Read the value
ulValue = adc_get_latest_value(ADC);
ulValue = mapResolution(ulValue, ADC_RESOLUTION, _readResolution);
break;
// Compiler could yell because we don't handle DAC pins
default :
ulValue=0;
break;
}
return ulValue;
}
As you can see there is little bit more actions to do single measure using HAL (Hardware Abstraction Level) so you have another level of abstraction here.
If you want faster actions, you can access hw directly, or use HAL and skip unnecessary steps. It would be much faster then.
-
Thanks, I've actually tried this already. It shaves off some time, but it's still slower than the time cited by the datasheet (100 microseconds) on the Zero. On the Uno this helps get it under that, but I don't understand why the baseline time of the Zero is so much longer. I've spent quite a while looking at the C files and the disassembly. On the zero, in the disassembly, it's always the line that's waiting for the flag indicating that the conversion is done that takes a long timehiitsali– hiitsali2016年07月17日 13:03:11 +00:00Commented Jul 17, 2016 at 13:03
-
@hiitsali abviously the waiting for the result would take more time as the setting up (if done efficiently). Is the problem that you'll have to wait, or that it simply takes too long? You can try setting the ADC to do less samples and/or lower accuracy, that may improve speed. Otherwise, you can implement an interrupt when ADC is complete, so you're not waiting for the ADC (it's not faster, but more efficient).aaa– aaa2016年08月16日 13:52:56 +00:00Commented Aug 16, 2016 at 13:52
-
Code abstraction is not the issue for something already relatively slow like that. Rather the issue would be the actual time required for a conversion with the configured ADC clock and mode.Chris Stratton– Chris Stratton2016年10月15日 17:32:59 +00:00Commented Oct 15, 2016 at 17:32
int sensorPin = A1; int ledPin = 11; int sensorValue = 0; void setup() { analogReadResolution(12); pinMode(ledPin, OUTPUT); } void loop() { digitalWrite(ledPin,HIGH); sensorValue = analogRead(sensorPin); digitalWrite(ledPin,LOW); }