Speed tests of Arduino Uno R3's pins.

Good evening,

Tonight I ran some tests on the Arduino Uno's input/output pins.

I hooked a single Arduino pin to an oscilloscope and measured the frequency at which the Arduino could toggle the pin from high to low. I used the following code:

    for (int i = 0; i < 10; i++)
    {
      digitalWrite(7, HIGH);
      float test = 13/.0777;
      test = test * 1.030232;
      for (long int t = 0; t < 10000000; t++) 
      {
        int x = 3;
        float y = x / 2.3;
      }
      
      digitalWrite(7, LOW);
    }

The frequency came out to be about 120KHz. As a comparison, I re-ran the code without loop in between the digitalWrite calls.

Without any thing else happening, I found that the Arduino was able to toggle the pins at the same frequency (120KHz).

To test the reading rate, I setup a 50% duty cycle pulse train at varying frequencies and then attempted to detect the rising edge using the following code:

    unsigned long w = 500;
    _t = millis();
    while ((millis()-t) < w)
    {
      if (PIND & (1<<7)) state = HIGH;
      else state = LOW;
      if (state == HIGH && _state == LOW)
      {
        n++;
      }
      _state = state;
    }
    Serial.print("Time during sensing: ");
    Serial.println(millis()-_t);
    Serial.print("Number detected: ");
    Serial.println(n);

Now, as I vary the frequency of the pulse train I compared the number of pulses that should have been sent to the arduino along with the number the arduino reportedly detected. Up to 40KHz the numbers were usually the same (within 1 or 2) but past 40KHz, the Arduino couldn't keep up. At 50KHz the Arduino only reported detecting about 24000 pulses during a half second detection cycle. That is a pretty hefty loss if you ask me.

Now, my question is this: Does any one know how, electrically and physically, the Arduino detects when the digital pins are high or low? I still need to do some research on these things and compare the Arduino to other microcontrollers/microprocessors out there, but overall I found the results interesting.

I hope this information helps the community,

Regards,

Adam

Related Note: It seems to me like the compiler is optimizing out that loop if it doesn't change the speed of the test to include it.

Try declaring the variables you use in the loop with the volatile keyword, which tells the compiler that when you tell it to write or read the variable, you know what you are doing even if it looks like a useless loop. That should make your test more accurate.

As for how the arduino detects pin voltages, I don't actually know. Probably just goes straight into a logic gate or something.

You can read in the datasheet the voltages at which a high or low is detected.

The Arduino uses a timer with interrupt for the millis() function. That means an interrupt is running. During that interrupt, some pulses could be missed.
You can run code on the ATmega chips without Arduino and without that interrupt. Have a look at http://www.avrfreaks.net/ that is about ATmega programming without Arduino.

Most ATmega microcontrollers can select a pin as timer input. So you can count pulses without missing something. Even with Arduino code. There are frequency counters that can count up to 8MHz.
Here is an example, but there are many more:
http://interface.khm.de/index.php/lab/experiments/arduino-frequency-counter-library/

aatyler:
Without any thing else happening, I found that the Arduino was able to toggle the pins at the same frequency (120KHz).

All that proves is that digitalWrite() is a very slow function

(and that the compiler wasn't fooled by your attempt to use floating point math to slow it down).

Try this instead:

byte pin = 1<<5;
DDRD |= pin;
while (true) {
 PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;
 PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;
 PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;
 PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;  PIND = pin;
}

aatyler:
To test the reading rate, I setup a 50% duty cycle pulse train at varying frequencies and then attempted to detect the rising edge using the following code:

...

At 50KHz the Arduino only reported detecting about 24000 pulses during a half second detection cycle. That is a pretty hefty loss if you ask me.

All that proves is that detecting high-frequency pin changes is pointless in software.

The Arduino's chip has hardware to detect pin changes if you need it to. The timers can also be driven by external pins, eg. Timer 1 is a 16-bit counter that can use Pin 7 as an input, ie. it will count the number of changes on the pin in hardware.

How fast? The datasheet says: "Each half period of the external clock applied must be longer than one system clock cycle to
ensure correct sampling. The external clock must be guaranteed to have less than half the system clock frequency."

So on a 16MHz Arduino you can count events up to 8MHz.

aatyler:
Does any one know how, electrically and physically, the Arduino detects when the digital pins are high or low?

The people who wrote the datasheet.

(and they included that information there).

Thank you every one for your replies.

I will try changing the code inside of the loop by adding the Volatile keyword. I will also look into the hardware timers as a better way of detecting pulses. Once I get a chance to get back to the lab at school at re-run the tests with the changes I'll post again.

FUNGUS:

I tried something similar, meaning I wrote directly to the port registers and yes it was very fast but the pulse shape was terrible. I think I found it somewhere near 2 GHz ouputs, but the pulse were more like a distorted sine wave. Of course I can add delays and such to make it nicer. I didn't have time to pursue that in the lab the other day. I will look at directly writing to the registers while making the speed more reasonable so as to make the pulse shape more ideal. Also, thank you for the tips on the timer, I didn't know that and good point about the datasheet for the ATmega.

Regards all,

Adam

The internal electronics of the Atmel microcontrollers for an output pin is actually very good.
It is not as good as some fpga chips, but it is very good for a normal logic signals.
If you see terrible pulse shapes, perhaps you have a capacitive load ?
Or do you have an Arduino clone from Ebay with minimal pcb copper and bad components for decoupling ?

If I were designing a microcontroller I would not want the voltage to rise instantly when I turned a pin high. I would probably aim for a distorted sine shape because a fast pulse on will emit more RF which is a really bad thing in professional design where the aim is to not cause any RF interference. I think the word for that is slew rate limiting. I'm not sure if the atmegas do this but if they do a sine wave is just what they would probably go for.

I think I found it somewhere near 2 GHz ouputs

The max you can get from an Arduino using SW is 4MHz. You can get 8MHz using one of the timers.

I find your input numbers inexplicable. I would think the Arduino should do significantly better than 40kHz. Shucks, there are SW Serial implementations that handle a bitstream faster than 40kHz... Are you sure you're not off by a factor of 10 or something?