If I want to turn something on with digitalWrite() there are two ways I can code this. I'm wondering if one is preferable to the other.
The first method is to execute the digitalWrite() command in every loop of the sketch, like this:
good question... In my opinion it is more efficient not to keep calling digitalWrite, just because of the overhead it introduces, but you hardly ever need to optimise your code to that level. On the other hand, setting a pin to the same value over and over won't cause the pin to "wear out" or anything like that. It should also not produce any glitches on the output, so it doesn't really matter if you keep setting it over and over (but it does feel wasteful, doesn't it?)
I would love to find out if someone has concrete evidence to show that it is not a good idea...
Since the title of this thread actually has "Best practice..." in it,
I would highly discourage the above construct.
While the above works today, the digitalWrite() api is defined to use value constants HIGH or LOW not a boolean value.
The behavior of using any other value parameter is currently undefined.
Using parameters that are outside a defined API can create potential long term issues should the underlying defines ever
be changed.
As far as "Best Practices" goes, the best thing is to always use an API the way it is defined
rather than try to take advantage of some internal known behavior of the underlying code.
So in this case, even though using a boolean vs HIGH/LOW would work, it definitely would not fall in
the "best practices" category as far as coding goes.
Yes theoretically any API or system defined constant can change in a future release.
And in this example if the values of LOW/HIGH were ever changed,
all those that "cheated" and didn't use the constants LOW and HIGH would
potentially get burned but those that followed the API as it is documented, would not have an issue.
That is why I said that as far as "best practices" goes (which is what we are talking about right?),
always use an API the way it is documented.
Whether it is this digitalWrite() function or any other function, it is always best to use an API the way it is defined
rather than try to take advantage of known internal behavior of a current underlying implementation of the API code.
That way the code using the API function is insulated from any changes to the underlying values of the
documented defines or the underlying implementation.
This same thing rings true for any API in any system not just Arduino.
Consider another real-world example: The function pinMode().
I wouldn't want to stray from using the mode defines like INPUT, OUTPUT, etc...
so I would also highly encourage the use of the mode defines rather than using
naked constants, booleans, or 0/non-zero values when using that function as well.
Prior to Arduino 1.0.1 the pinMode() mode parameter was defined to be INPUT or OUTPUT
but the pre 1.0.1 code in wiring_digital.c only checked for INPUT which is defined as 0.
This meant that a mode of 0 meant "input" and any non-zero value meant "output" for
a pin. So in pre 1.0.1 if somebody passed in any non zero value, the pin became an output.
Starting in 1.0.1 there are now 3 modes, INPUT,OUTPUT, INPUT_PULLUP.
So now not all non zero values set the pin to be an output.
Change happens, and those that chose to use an API differently than it is defined run
the risk that their code may break in the future, even if the API itself hasn't changed.
Ever since C began, and probably before that, 0 has meant off and 1 has meant on. It's not technically documented but then again (as many people have pointed out) lots of things in C aren't documented on the reference page -- it doesn't mean it's not standardized.
No "good api" should use defines. Enums are the only way to make sure things are done right and there is no excuse for not using them -- the interface doesn't change a bit.
WizenedEE:
No "good api" should use defines. Enums are the only way to make sure things are done right and there is no excuse for not using them -- the interface doesn't change a bit.
Totally agree. And that is another good example of why using the documented parameters of an API is better than
trying to take advantage of internal behaviors by using your own undocumented parameters.
Lets say the arduino team updated the core code to have more strict type checking of the value parameter in
digitalWrite() by changing its type and re-declaring LOW & HIGH to be something other than simple #defines of naked 0 & 1 constants.
(for example: keep the value of LOW 0 and the value of HIGH 1 but change them to be a specific type)
Those that didn't use LOW/HIGH would now get burned as their code would no longer compile.
Those that use LOW/HIGH would see no issue.
Yet the API nor the values of LOW and HIGH "didn't change bit", right?
Back to the OP: in the grand scheme of things, it isn't going to make a blind bit of difference whichever way you do it. One of my sketches has two things it turns on and off, one is done one way and one is done the other, its what took my fancy at the time. Ok, writing the pin each time might use an extra couple of electrons, but you won't be able to measure it......
pluggy:
Back to the OP: in the grand scheme of things, it isn't going to make a blind bit of difference whichever way you do it. One of my sketches has two things it turns on and off, one is done one way and one is done the other, its what took my fancy at the time. Ok, writing the pin each time might use an extra couple of electrons, but you won't be able to measure it......
While it won't make much difference from a power perspective, it can make a big difference from a timing perspective.
From a timing perspective, digitalWrite() is very time consuming for what it does.
The way the Arduino core code has implemented this, it requires multiple
table lookups that are from tables that are stored in progmem. This causes it to take upwards of 5us or so.
Whereas a check on variable in SRAM takes just a few cycles (few hundred ns at most) so the code to check a variable and not
call digitalWrite() can execute 10-20+ times faster than just blindly calling digitalWrite() each time.
For simple things that don't have critical timing needs then it won't matter but it is something to keep in mind.
bperrybap:
While it won't make much difference from a power perspective, it can make a big difference from a timing perspective.
From a timing perspective, digitalWrite() is very time consuming for what it does.
I was thinking about doing a little test for that, now I don't have to. What about digitalRead() an analogRead(), do they take up as much time as digitalWrite()?
ScottG:
I was thinking about doing a little test for that, now I don't have to. What about digitalRead() an analogRead(), do they take up as much time as digitalWrite()?
The Arduino libraries are included with the IDE (location changes depending on operating system.) You could look at the code for all three and judge for yourself.
digitalRead (in wiring_digital.c) maps the number to the appropriate port bit, turns of PWM if necessary, then returns the value.
analogRead (in wiring_analog.c) does a bunch of things but the important aspect to realize is it takes about 100us for the conversion to complete.