Go Down

Topic: How exactly slow is digitalRead()? (Read 14473 times) previous topic - next topic

SukkoPera

Jul 23, 2015, 12:27 pm Last Edit: Jul 23, 2015, 12:28 pm by SukkoPera
OK, we all know that Arduino sacrifices speed for simplicity and portability, when it comes to reading and writing pin states. I know I can circumvent this with direct port manipulation, but I want to know if I can avoid this to maintain portability to different microcontrollers.

So what I would like to know is exactly how slow digitalRead() is. I can't measure this myself, I tried to look up numbers on the web and I came up with different numbers. I know that it can a different time according to whether the pin has "recently" been used as PWM or not, so let's suppose a simple case where I set a pin as INPUT and just call digitalRead() on it from time to time. Some sources I found state that it takes ~1 us, while others say ~4 us.

Who is right? A figure in the us range will be enough for me.

Make your Sega MegaDrive/Genesis region-free with Arduino! https://goo.gl/X7zBcq

Guida rapida a ESP8266: https://goo.gl/kzh62E

mart256

Depends on the crystal frequency. And how many instructions it takes in assembly to read a pin. You could check on avr forums.

SukkoPera

Let's take a standard Arduino Uno, so clocked at 16 MHz and using the Arduino libraries.

BTW, I have just noticed the sticked I/O Benchmarks post, it says for the Uno:

Quote
Digital Pin Read  Takes About 4.78 Microseconds.
So it seems I have my answer: almost 5 us! Sorry I didn't notice that before :smiley-confuse:.
Make your Sega MegaDrive/Genesis region-free with Arduino! https://goo.gl/X7zBcq

Guida rapida a ESP8266: https://goo.gl/kzh62E

jboyton

#3
Jul 23, 2015, 07:06 pm Last Edit: Jul 23, 2015, 07:08 pm by jboyton
An alternative is to obtain the port and bit mask with some macros. I think these macros are supported in the various cores. It's slightly slower than direct port access because of the architecture of the AVR chips, but it's a lot faster than digitalRead.

    #define MY_PIN    8

    // do this once at setup
    uint8_t myPin_mask = digitalPinToBitMask(MY_PIN);
    volatile uint8_t *myPin_port = portInputRegister(digitalPinToPort(MY_PIN));

    // read the pin
    uint8_t pinValue = (*myPin_port & myPin_mask) != 0;


There's no doubt a nicer way to dress this up.

DigitalWrite is also slow. You can do the same thing except use portOutputRegister() instead.

robtillaart

if you know the pin number at compile time you can optimize a lot.
if the pin number is not known ...

check - http://forum.arduino.cc/index.php?topic=46896.0 -
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Peter_n

#5
Jul 24, 2015, 05:27 am Last Edit: Jul 24, 2015, 05:30 am by Peter_n
A single I/O instruction is 0.125us. The ATmega chip is optimized for single I/O bit instructions.
A digitalRead() is about 3.6us. That is without the time for the iteration/loop. The 4.78us is with the iteration/loop included.

Using the digitalPinToPort() and so, will increase the speed a lot. It is not as fast as 0.125us, because a few variables have to be read from memory.
The digital...Fast functions are more or less portable. For fully portability you are stuck with that 3.6us.

SukkoPera

Thanks a lot, Peter, that's what I needed to know. One more question though: all those timings are going to double when running at 8 MHz, right?
Make your Sega MegaDrive/Genesis region-free with Arduino! https://goo.gl/X7zBcq

Guida rapida a ESP8266: https://goo.gl/kzh62E

Paul__B

Thanks a lot, Peter, that's what I needed to know. One more question though: all those timings are going to double when running at 8 MHz, right?
Naturally.

The complication is of course, that digitalRead() includes the option of using a variable to select the port.  That includes the overhead of performing a bit shift according to the variable value (essentially a FOR loop) on the mask bit which is used to select the bit.  Then it must perform a zero test in order to determine which value - TRUE or FALSE - to return.

All this is easy but not simple, whereas if you know at compile time, exactly which bit you want and are happy to cope with a zero/ non-zero but probably not specified result, it can be programmed in assembler at lightning (sort of) speed.  The digitalRead() function is almost like using an interpreter.

SukkoPera

#8
Jul 24, 2015, 09:42 am Last Edit: Jul 24, 2015, 09:45 am by SukkoPera
Thanks again. Yes, I understand why it is slow and the various ways readings can be sped up, but before going that way I wanted to make sure it was absolutely necessary.

Indeed, it is. I have a signal that I must sample on a 12 us clock. Of course I use an interrupt: it seems it takes about 3 us to enter the handler and 2 more to leave it. If running at 8 MHz, like I plan, that doubles and doesn't leave much time for the actual job :(.
Make your Sega MegaDrive/Genesis region-free with Arduino! https://goo.gl/X7zBcq

Guida rapida a ESP8266: https://goo.gl/kzh62E

Riva

#9
Jul 24, 2015, 09:59 am Last Edit: Jul 24, 2015, 10:00 am by Riva
Indeed, it is. I have a signal that I must sample on a 12 us clock. Of course I use an interrupt: it seems it takes about 3 us to enter the handler and 2 more to leave it. If running at 8 MHz, like I plan, that doubles and doesn't leave much time for the actual job :(.
The benchmark sticky you quoted is not perfect but I figured your normally going to want to act on result from reading so the for-next loop overhead sort of makes up for this.

To sample at that speed I would consider a faster MCU/clock speed as you won't have much time to do anything else. If you supply more details of what you need/want then maybe we can suggest options.
Don't PM me for help as I will ignore it.

Peter_n

Super extra awesome interrupt tutorial : http://www.gammon.com.au/interrupts
There is a section: "How long does it take to execute an ISR?"
With 8MHz, you have to double the numbers  :(

jboyton

A single I/O instruction is 0.125us.
Isn't it just one cycle (62.5ns at 16MHz) for reading/writing one of the GPIO ports?

Peter_n

Try it !
Perhaps the other cycle is loading the constant for a byte to read/write to the port.

jboyton

Okay. I tried it. It took one cycle to read PINB.

In the context of the rest of a sketch of course there will be overhead. You can't just read a value into a register and call it a day. But if you do it differently, for example by using a pointer to the port instead of an immediate value, it will take more cycles.

Peter_n

Thanks for testing that  8)
Two two cycles is probably not the constant for the bit, but storing the value into a variable.

Go Up