Channels ▼

Al Williams

Dr. Dobb's Bloggers

Marking Time

February 11, 2013

The Arduino code I showed last time had an odd statement in it:

PINB|= _BV(5);

This has the effect of toggling bit 5 of PINB (which happens to be the Arduino's digital output 13). I could have easily set the pin using a similar technique:

PORTB|=_BV(5);

Or reset it:

PORTB&=~_BV(5);

If you haven't seen this style of working with I/O ports before, you might have two questions. First, why not just use digitalWrite? Second, how would you know to use these mysterious lines to do the job?

The answer to the first question is simple. digitalWrite is a useful built-in library function that you often use when you want to work with an output bit. Here's a snippet of code slightly modified from the Arduino's Blink example:

void loop() {
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(25);               // wait for a second
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
  digitalWrite(led, HIGH);
  digitalWrite(led, LOW);
  digitalWrite(led, HIGH);
  digitalWrite(led, LOW);
 delay(1);
}

My intent was to produce a long pulse (25 milliseconds) followed by two pulses as fast as I could make them, and then wait 1 millisecond before doing it again. Most modern scopes have a trigger on pulse width mode, so the 25 millisecond pulse is a marker. How fast can the Arduino generate pulses like this? Here's what my scope read from a Duemilanove:

Each of the digital write commands took about 4us to do its job. That's pretty fast for human eyes, but you aren't always driving an LED. For the next test, I used the same code but replaced the last four digitalWrite commands with this:

PINB|=_BV(5);
PINB|=_BV(5);
PINB|=_BV(5);
PINB|=_BV(5);

Here's the result:

That's about 600ns. Don't miss that the units changed there. The previous example was at 4us or 4000ns! Using PINB was over six times faster than using digitalWrite!

Using PORTB instead of PINB gives roughly the same result. In all fairness, some of the issue is that digitalWrite does some sanity checks before it does the same line of code. For example, if a pin has PWM enabled, calling digitalWrite will disable the PWM before doing the work. You can find the code in hardware/arduino/cores/arduino/wiring_digital.c:

void digitalWrite(uint8_t pin, uint8_t val)
{
        uint8_t timer = digitalPinToTimer(pin);
        uint8_t bit = digitalPinToBitMask(pin);
        uint8_t port = digitalPinToPort(pin);
        volatile uint8_t *out;

        if (port == NOT_A_PIN) return;

        // If the pin that support PWM output, we need to turn it off
        // before doing a digital write.
        if (timer != NOT_ON_TIMER) turnOffPWM(timer);

        out = portOutputRegister(port);

        uint8_t oldSREG = SREG;
        cli();

        if (val == LOW) {
                *out &= ~bit;
        } else {
                *out |= bit;
        }

        SREG = oldSREG;
}

Some of the overhead is in the function call (although the calls to things like digitalPinToTimer are really macros). It is easy to see why the call takes more time. However, if you are dealing with a pin that never does PWM and doesn't need the other error checking and safeguards, this is a worthwhile optimization. You might not always need to have sub microsecond pulses, but, then again, you might need to speed up your program. You can also use the PIN and PORT registers to read and write multiple bits simultaneously, something you can't do with digitalWrite.

The second question is a little more complicated. How would you know to do this? In this particular case, of course, there is a lot of information about this on the Internet, but I was thinking of a more general-purpose answer. There are two ways you might find nuggets like this. The first is to read the source code. That’s one great thing about open source, it is — well — open. If you pull the source code to digitalWrite you can see where the real work is done and how much is extra and possible to optimize in some cases. You still need to understand how the Arduino's abstract resources (like pin 13) map to real AVR hardware (like Port B pin 5). You can find that information on the Arduino website For example, since the chip on my board is an Atmel ATMega 168, I need the mapping for that processor.

Speaking of the CPU, that's another great resource is to understand the actual CPU. The key to that is the datasheet. For example, section 14.2.2 mentions that writing to the PIN register of an output port toggles the value, the trick I used in the first sped-up example.

At over 400 pages, the data sheet can be a little intimidating. However, I often say that while you don't have to know how a car engine works to drive a car, you can bet the best drivers know exactly how an engine works (I'm sure I borrowed that line from someone, but I can't remember who). By the same token, the best programmers know details about the total environment they are using — the CPU, the libraries, and even the development tools.

I'm not suggesting that you never use digitalWrite. I'm simply saying that this is one of those abstractions you might break if you want to enhance performance. It isn't the only one — you can mine the datasheet and the source code to find many more.

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Dr. Dobb's TV