I'm programming an Arduino Mini that has some internal states that I want to be able to make the user aware of, so I'm setting it up to blink its state on the LED, in the form of "long blink, n short blinks" where n is the ordinal of the state.
Every time loop is called, I look at the state and the value returned by millis(), and calculate whether the LED should be on or off at that moment, then pass that on to the LED.
The question is: How expensive is it to call digitalWrite() each time I calculate the state, versus keeping the current state in a global variable and only calling digitalWrite() when it changes?
On a PC this is a no-brainer, because any I/O is going to have to go through half a dozen layers of APIs, kernel calls, etc., but it occurred to me that digitalWrite might be compiling down to a single 1-clock-cycle instruction, and I'd actually be making it less efficient (both in time and code) by trying to cache it.
No, it's not a single clock cycle by any means. Neither is it very slow. I think it's a few microseconds.
Some of my user indicators are in functions now. So I can change the type of indicator by changing only the code in the function. So I can have it on a dedicated pin, or as a graphic or chosen segment on a display, for example. By having it in the function, I don't have to ever change the code. But of course, I don't call the function every time through the loop. You could call it inefficient, but until the processor actually reaches its full workload, I like to think of it as working to pay for its upkeep.
Jon_White:
Every time loop is called, I look at the state and the value returned by millis(), and calculate whether the LED should be on or off at that moment, then pass that on to the LED.
The question is: How expensive is it to call digitalWrite() each time I calculate the state, versus keeping the current state in a global variable and only calling digitalWrite() when it changes?
As you have not posted your code it is hard to give a useful response.
If the existing program works, why try to fix it ?
...faster? In other words, will I get a performance benefit in exchange for the extra bytes of flash memory the latter uses?
Robin2:
If the existing program works, why try to fix it ?
At the point where I have 10,000 lines of code, I don't want the device straining at its seams because I made an incorrect assumption about performance on day one. I don't know what kind of resources digitalWrite() consumes. Maybe it's just a one-instruction write to a register, or maybe it has to do some kind of critical section thing to make sure it doesn't collide with interrupt handlers, etc. Maybe calling it thousands of times when I don't need to is going to reduce my battery life or make my serial connection jittery. Or maybe it's totally benign and the hardware takes care of caching the state. Who knows?
Checking the value of ledOn is going to be much faster than calling digitalWrite() unnecessarily.
But, is speed REALLY an issue?
I don't know what kind of resources digitalWrite() consumes.
Look at the source code, or write a test case. Record the time. Turn the pin on and then off 10000 times. Record the time. Compute and print the difference. Use micros(), not millis().
Maybe it's just a one-instruction write to a register
Way more than that.
or maybe it has to do some kind of critical section thing to make sure it doesn't collide with interrupt handlers, etc.
No.
Look at the source code. See for yourself.
Who knows?
Lots of us. You can, too. No secret handshake needed (although if you want to palm off some money, I'm in).
Now that I can see your code your question makes more sense to me.
digitalWrite() is a slow function. You have the source code for it inyour Arduino IDE.
The best way to assess which solution is fastest is to time it. Get the code to repeat for 1000 or 10000 times and see how long it takes. Record the value of micros() before and after the N repeats.
I think I read somewhere that the modulus operator is slow. Maybe a simple if(millis() - prevMillis >= interval) would be faster.
For the highest speed use port manipulation - but then you lose portability between Arduino boards.
@Jon_White, your consideration of performance is well placed when working with microcontrollers. However, your first concern when writing code should always be "Is it correct?"
How does your code behave close to the point when millis wraps? Is that behaviour correct?
How expensive is it to call digitalWrite() each time I calculate the state
Moderate expensive. Possibly more so on other arduino-compatible platforms (Galileo I comes to mind, with it's IO ports on the other end of an I2C io expander.) Worth caching.
Huzzah! Thank you for that. Something I can't help but notice is that, at least with the fast example, it's probably going to compile down to something like:
; I have not learned atmega328p assembly code yet so don't yell at me.
IN a, 123
OR a, 16
OUT 123, a
If an interrupt were to fire between the IN and OUT instructions, and the interrupt handler tried to modify the same register (which presumably represents multiple output pins), its changes would be lost as soon as the OUT instruction ran.
That means the real implementation in digitalWrite() has to be disabling interrupts while the state of the register is still in flight, and considering my device's main job is to do fast, interrupt-driven serial input, I think that's probably as good an argument as any for not calling digitalWrite() thousands of times per second.
I think the given "blink once each half second" example is so absolutely dominated by the "is it time to do something",
that the differences in the execution time for the port manipulations are hardly measurable by timing the different routines, at least the test has to run for a very long time.
One execution per 500 ms for the different parts and 50000 time checks in all of them. If you change the behaviour to "blink each 512 ms" just test the 0x200 bit of millis(), this should give you a huge speed improvement in overall execution, regardless of how you handle the port bit.
The logic analyzer approach is much better for measuring the port manipulations. IMHO
The "how can a port bit flipped in the most efficient way" question was discussed here in depth many times.
Jon_White:
That means the real implementation in digitalWrite() has to be disabling interrupts while the state of the register is still in flight, and considering my device's main job is to do fast, interrupt-driven serial input, I think that's probably as good an argument as any for not calling digitalWrite() thousands of times per second.
You can always look at the code which is part of the download:
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;
}
Yes, it disables interrupts briefly. Is that a problem? When a timer interrupt fires that also disables other interrupts briefly.