- A Single PORTx Call
- 2 thoughts on “ Benchmarking Arduino’s digitalWrite() with a Logic Analyzer ”
Recently I picked up a device called Logic from Saleae. It’s a 4-channel USB-based logic analyzer. While learning how the simple, but effective, UI works I ran some timing benchmarks on my Arduino Uno. The subject? digitalWrite(). I wanted to know how fastdigitalWrite() could turn on two (or more) pins.
Almost all Arduino users start out with the simple “blink” sketch. Turn pin 13 ON, delay, turn it OFF, and delay again. The heart of this version of “Hello World!” is the digitalWrite() function. Many Arduino users never even think about all of the stuff this single function call hides.
Using this simple program, I tried toggling Pin 8 and Pin 9, one call after another. For most Arduino users, this is the fastest way they know to change two pins. Some may even think this happens “at the same time”, or close enough. Does it?
The delay(waitTime) is so that we can clearly see the transitions on our Logic Analyzer’s waveform screen. Figure 1 shows the capture from the Logic.
Figure 1 digitalWrite() capture
Looks like everything is perfectly in sync doesn’t it? When we zoom in and turn on markers, we see a different story, shown in Figure 2. Same data as Figure 1, but the scale has changed.
Figure 2 Zoom-In on digitalWrite() capture
The difference between marker A1 and A2 shows Pin 9 turns on about 6us after Pin 8. At 16MHz, that’s nearly 100 clock cycles from one pin to the next. Fast for us humans, but rather slow for microcontrollers.
For most projects, this time between pin transitions is fine. However, what if you need those transitions to happen closer together? Well, one option would be to use the registers of the AVR chip and toggle them directly, instead of digitalWrite().
Behind the scenes, digitalWrite() is doing a bunch of things for you. It’s mapping the Pin Number you give it, to a physical pin on an Arduino board. Next it figures out the state of the pin, to make sure it knows what to do next. Then it adjusts the appropriate “register” for you. (A register is a series of transistors that keep track of very simple information, like if an I/O pin is input or output.) All of these things take time.
So let’s look what happens when we skip the hand-holding of digitalWrite(). Here’s how we go directly to the hardware’s registers ourselves.
Using the code, we change the state of pins 8 (PB0) and 9 (PB1). Note, this code only works on ATmega328-based boards like the Uno and Nano.
Figure 3 Two PORTB Calls
Ah-ha! Now our time between pins is down to just 83ns. Or… is it? This logic analyzer samples at 12MHz. Convert that to time between samples with 1 / 12MHz, and you get 83ns. Since our sample period is 83ns, and our measurement is 83ns, the data is suspect. We can’t accurately measure the time between pins with this logic analyzer! However, we do know the transistion occured within 1 sample period of the analyzer’s capture. That means register manipulation is at least 60 times faster than using digitalWrite().
But wait, there’s more!
A Single PORTx Call
This magic line of code turns on Pin 8.
The “|” character is a C-operator called “Bitwise-OR.” Bitwise functions perform an individual binary-OR on each bit of PORTB and the binary number.
To turn a bit ON, we Bitwise-OR that bit with 1 and all the other bits 0. To turn a bit OFF, we BitWise-AND that bit with 0 and all the other bits 1. Why? That’ll need another post.
On the ATmega328, PORTB controls pins 8-13. Why don’t we turn on bits 0 (Pin 8) and 1 (Pin 9) at the same time with a single line of code?
Now the code is simplified to this example.
Figure 4 0s with a single PORTB call
And now! The logic analyzer sees both bits changing at the SAME time.
When you switch to using direct port manipulation of an Arduino, you start to limit what boards the code will work on. Each ATmega chip has different PORTs that map to the Arduino board’s pins. One of the benefits of digitalWrite() is that it figures that mapping out for you. One of the downsides is that it takes, in microcontroller terms, much longer to operate than direct port manipulation.
I highly recommend the Logic from Saleae. For only about $110 it has decent specs, good hardware build, and simple to use software, it’s great for watching up to 4 pins of an Arduino project. You can buy the Logic from Jameco here.
Long comments, URLs, and code tend to get flagged for spam moderation. No need to resubmit.
ALL comments submitted with fake or throw-away services are deleted, regardless of content.
Don’t be a dweeb.
Leave a comment Cancel reply
2 thoughts on “ Benchmarking Arduino’s digitalWrite() with a Logic Analyzer ”
Thank you for the nice articles that you write!
As a newbie in electronics, it’s hard to find good information to aid learning for a couple of topics.
Oscilliscopes and logic analyzers are such a topic.
Most cover the usage and software but there is hardly focus on how to connect these devices, the do’s don’t and some background knowledge to learn and understand more.
Thanks for a very interesting and informative article on port calls vs. digitalWrite. This tutorial gives an alternate more in-depth understanding of subject matter not usually covered in typical tutorials.
Most Popular Posts
- MQTT Tutorial for Raspberry Pi, Arduino, and ESP8266
- millis() Tutorial: Arduino Multitasking
- P-Channel MOSFET Tutorial with only Positive Voltages
- The 4 best transistors to keep in your parts kit
- />MSGEQ7 Simple Spectrum Analyzer
- />Arduino: How do you reset millis() ?
- Low side vs. High side transistor switch
- 5 Myths Everyone Believes about Arduino (that aren’t true)
- Arduino: Independent On-Off Times with Millis()
Most Recent Posts
- />MC1377 Measurements on Mini Apple IIe Prototype video board
- Introducing Bit Preserve