Saturday, February 20, 2016

How fast is an Arduino Nano pin?

I wonder how fast I can toggle an I/O pin of Arduino Nano (ATMEGA328 running at 16MHz)?

With this simple code:


void setup() {
  pinMode(12, OUTPUT);
}

void loop() {
  digitalWrite(12, LOW);
  digitalWrite(12,HIGH);
}

I monitored pin D12 with my oscilloscope and found that it takes 5uS for each level. Thus the code gives 100kHz square wave as shown in the figure below. Note that oscilloscope setting was 5 uS/DIV.



Now I modified the above code such that it directly writes to PORTB (pin D12 is mapped to bit B4 of PORTB). This avoids using digitalWrite() function. The modified version of the code is shown below:

void setup() {
  pinMode(12, OUTPUT);
}

void loop() {
  PORTB = B00000000;
  PORTB = B00010000;
}

Result: (0.5 uS/DIV)



The frequency of the waveform is around 1.11 MHz which is about 10 times faster than using digitalWrite(). However the waveform does not look symmetry. Duration of the HIGH is around 0.8uS while that of the LOW is around 0.1uS.

I then swap the 2 line of code:

From
  PORTB = B00000000;
  PORTB = B00010000;

to
  PORTB = B00010000;
  PORTB = B00000000;

The result was: (again 0.5uS/DIV)



Obviously, the duration of the HIGH and the LOW are swapped. I think the reason is the Arduino loop takes around 0.7uS.


Conclusions:

  • Write directly to port is much faster than using digitalWrite().
  • Arduino loop() takes about 0.7 uS to execute.





4 comments:

  1. I love these kinds of experiments where you benchmark hardware to get a sense of what it can and can't do.

    Maybe it is just that arduino was a bit immature when I started coding on AVR's but I ended up not using the arduino SDK and always writing complete firmwares on GCC and flashing them using avrdude. I suspect this would solve your asymmetry issue since you would be writing a main() function with a single loop and it wouldn't have whatever overhead arduino is introducing in it's calling of the loop function.

    Do you have an AVR GCC toolchain to try this out? If I remember correctly on Ubuntu it is something as simpler as:
    sudo apt-get install avr-libc avr-gcc-c++ avrdude

    Then you're ready to roll.

    ReplyDelete
  2. Thank you very much for your suggestions. I thought of doing it in C code too but didn't have the compiler at the moment. Actually I've never code the AVR in C. I know that you have been using AVR's for very long time. I've just realized the AVR's are very cheap and powerful processors. I started using the AVR's when I learned Arduino. Arduino is very easy which is excellent for beginners. It took me a while to implement 1-wire interface for DS18B20 Temp. sensor on DSPic. But it takes 1 minute to do it with Arduino. I have never used Arduino for any time-critical applications until recently when I try to drive 2 stepper motors synchronously. I want to pulse to motors as fast as I can. I use timer interrupt to control pulse duration.

    ReplyDelete
    Replies
    1. If you don't mind me copying your idea for a post, I might try a similar post on my blog demonstrating how to do this in pure avr-C.

      I agree that Arduino is good for beginners but I think that most people get so comfortable with it they never realize that it is still quite easy to write code without the Arduino setup.

      The downside on Arduino, of course, is that you lose some low level control of the device.

      I also agree that Arduino can make some (but not all) of the hardware support easier with its libraries. But on one or two occasions, I found it quite easy (a matter of minutes) to port one or two functions for supporting I2C, etc to your own program. If I'm not mistaken, Arduino code is compiled using wrappers around the same AVR-GCC that I use making porting its libraries an almost trivial task.

      If you do this don't forget to check the copyright terms to make sure you aren't in violation. :-)

      Delete
    2. No, I don't mind. I'd like to start coding AVRs in C too. I have a bunch of AVR boards now.

      Delete