1

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);
}
4
  • Do you have a code used for these measures?
    – KIIV
    Commented Jul 16, 2016 at 20:30
  • Sorry, meant to post this the first time. 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); }
    – hiitsali
    Commented Jul 17, 2016 at 4:59
  • You might consult the data sheets for the two MCUs particularly the number of peripheral clocks to perform a conversion in a given mode and see if what you are measuring seems consistent. Commented Aug 16, 2016 at 23:28
  • @hiitsali, thank you for mentioning this. I suppose there is no solution for this yet ? I did some tests with the Arduino functions and the Arduino M0 is 3 to 7 times faster. The pinMode() takes 2µs, that is only 2 times faster. The analogRead() takes 422µs which is 4 times slower. I don't want to use interrupts for the ADC, so I have to keep in mind that it is very slow when using the average with many samples.
    – Jot
    Commented Jan 30, 2017 at 14:42

1 Answer 1

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.

3
  • 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 time
    – hiitsali
    Commented 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
    Commented 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. Commented Oct 15, 2016 at 17:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.