Pages: 1 [2] 3   Go Down
Author Topic: Not understanding toggle code  (Read 1708 times)
0 Members and 1 Guest are viewing this topic.
Dallas, TX USA
Offline Offline
Edison Member
*
Karma: 47
Posts: 2328
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Would you settle for
Code:
state = (HIGH + LOW) - state;
?

Interesting.....
I think that probably works.
Assuming state was initialized to LOW or HIGH, which I think
is an ok assumption for something like this.

Only reason I say "probably" is that I'm
not sure about if there is a overflow/underflow particularly
if state is an 8 bit value.

The if/else seems easier to understand for newbies.
I'm wondering how the compiler treats that vs
an if/else?


--- bill
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 238
Posts: 24321
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Interestingly, even if the API says only HIGH or LOW are allowed, the implementation of 1.0 only tests for LOW.
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Dallas, TX USA
Offline Offline
Edison Member
*
Karma: 47
Posts: 2328
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Interestingly, even if the API says only HIGH or LOW are allowed, the implementation of 1.0 only tests for LOW.

But technically, the digitalWrite() API never says that HIGH and LOW are the only allowed values,
(it's current C code declaration can't stop you from passing other values)
The API only makes a commitment as to what behavior to expect when the values are HIGH or LOW.
The behavior for other values are undefined in the API.

Garbage in, Garbage out right????

It is why I mentioned earlier that there are "working" code examples out there in the forum posts
and in libraries that are using 0 and non zero to set the output pin state.

They work because the current AVR arduino code in digitalWrite() only checks for LOW
and sets the output pin to a logic high level when the value passed is not LOW.

With todays AVR Arduino dititalWrite() code implementation,
you can get away with assuming HIGH is 1 and LOW is 0
and that zero sets a output pin to low and any other non zero value sets the output pin to a logical
high.

But I like to write and encourage others to write portable code that does
not depend on internal implementation details that are not part of the formal API
definition.

This is why I stress that using internal implementation details is dangerous as
some other implementation on some other processor
say ARM, pic32, etc... might not implement the code the same way or make the same assumptions
or the underlying code might change in the future.

The reality of things at this point, is that there is so much existing code out there
that didn't stick to the formal APIs that if you now were to change something like this,
some amount of the code will break, and that is why the core developers like Diligent/chipkit and Maple
have implemented their DigitalWrite() routines with the same HIGH/LOW values and assumptions.

Still, it's never a bad thing to try to encourage folks to stick to API definitions.

That said, there are sometimes legitimate uses to violate an API spec or take advantage
or internal details.
So I will admit there are times when you do want to intentionally violate the API spec.
Perhaps by taking advantage of knowing how the API works or the values of its defines
you can optimize the code to some higher needed level.
But usually when deciding to do this it is a path carefully taken knowing that in doing this,
it may create a portability issue in the future.

That would apply in this particular case if you were looking to squeeze a few cycles or
flash bytes out.

If you look at the AVR code generated, you can generate better/faster code
if you can take advantage of knowing that HIGH is 1 and LOW is 0.
For example;


Code:
state ^= HIGH;

and this

Code:
state ^= state;

Is more efficient than
Code:
if(state == HIGH)
  state == LOW;
else
  state == HIGH;

It will generate fewer AVR instructions which will be less/faster code.

(The subtract method gives a gcc warning as I assume
it is worried about overflows/underflows)

But doing things like this come with added risk that if ever the defines change,
the code may stop working as expected.
Or it may not generate better code on some other CPU architecture.

So, in general, that is why I try do discourage people from writing code that
takes advantage of internal API implementation details.
It just not good programming practice to ensure long term portability.


--- bill
Logged

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 58
Posts: 4002
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Is there a binary logic chip made that uses other than 0 for FALSE/LOW and 1 for TRUE/HIGH?
Memory, controller, processor, register?
Logged

Examples can be found in your IDE.

Offline Offline
Edison Member
*
Karma: 31
Posts: 1417
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
state ^= state;
This will set state to zero. I assume you meant this?:
Code:
state = !state;

and this:
Code:
if(state == HIGH)
  state == LOW;
else
  state == HIGH;
should be this:
Code:
if(state == HIGH)
  state = LOW;
else
  state = HIGH;

Pete
Logged

Where are the Nick Gammons of yesteryear?

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 508
Posts: 31384
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
that there are "working" code examples out there in the forum posts
and in libraries that are using 0 and non zero to set the output pin state.
Yes and in every other language I have programmed in Boolean variables use this convention.

Quote
and that is why the core developers like Diligent/chipkit and Maple
have implemented their DigitalWrite() routines with the same HIGH/LOW values and assumptions.

This convention has been in place long before the Arduino existed, it is the normal way of doing things.

Quote
But doing things like this come with added risk that if ever the defines change,
the code may stop working as expected.
Well the code will continue to work but it will only be when you recompile the code you will get trouble in the unlikely event that some idiot decides to overthrow decades of convention. Anyone trying to write portable code on an arduino is going to have to give up the pin orientated view of the outside world.
Remember we are not controlling a nuclear power plant but flashing an LED on and off.  

Code portability has its place but only when you know it is going to be portable. Too many bytes and CPU cycles are wasted trying to make things portable that will never ever move.
Logged

Dallas, TX USA
Offline Offline
Edison Member
*
Karma: 47
Posts: 2328
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Is there a binary logic chip made that uses other than 0 for FALSE/LOW and 1 for TRUE/HIGH?
Memory, controller, processor, register?


But we are not talking about chips.
We are talking about how to write software to use a software API function.
The s/w API says use these defines: "HIGH" and "LOW" to get things done.

Don't get hung up on this specific digitalWrite() function call and how HIGH and LOW
map to the output states of the pin.

BTW, there are some chips that use separate set and clear registers.
In that situation, 1 bits written to a clear register will actually clear bits not set them.
And actually there are examples where true was defined as 0 and false was 1 in software
from the 80's on at least one RISC processor - but lets not delve down there.

Again, it is the concept of properly using an API as it is defined vs stepping outside the defined API
to use it ways that it is not defined.

You guys are getting all hung up over this one simple function.
I'm trying to express a reason to follow an API which a much bigger concept.

To help illustrate, lets look at some other API function.
Maybe something like:

setpixel(int x, int y, int color);

Where color can be WHITE or BLACK.
Without looking could you be sure the values for BLACK or WHITE were? No....
If you were to take a sneak peak at what BLACK and WHITE are, maybe you could write some
sightly better code that uses their raw values rather than use the defines.
Or that took advantage of the library code assuming one color when the value was zero and
used the other other color for any non zero value.

But then I re-write the library to get ready for additional colors and I change the values for
BLACK and WHITE to other values. Those that use BLACK and WHITE vs their raw values will
be fine. Those that went around using using the defines or taking advantage of the library
working a particular way when say the color was non zero will get burned.

C++ could solve this by creating a function that only accepted the proper defines/enums
to ensure that people didn't bypass using the proper types on functions like digitalWrite(),
but today digitalWrite() is a C function using only native types and so there is no way to enforce
that the "val" argument is of the type vs some calculated numeric type made from assuming
the values of of the defines.

--- bill
Logged

UK
Offline Offline
Shannon Member
****
Karma: 183
Posts: 11154
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Is it conceivable that in any implementation of any language the HIGH will not be the logical inverse of LOW? These are hardware concepts and these nimby pimby software types had better get their head round that fact!

In general, when you code to any API you should avoid making any assumptions about the behaviour of that API that are not specified as part of the API. It may be that in this case you know (or assume) that HIGH and LOW correspond to a bit being set or unset in a hardware register. You may even know (or assume) which bit is set, and which bit state corresponds to the HIGH output. But it is poor practice to embed that assumption in your code. By encouraging people to do that, you're teaching them bad habits.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 58
Posts: 4002
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Like bloating code?
Logged

Examples can be found in your IDE.

0
Offline Offline
Faraday Member
**
Karma: 19
Posts: 3418
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

@berrybap: although I agree that one should not rely to much on implementation details I disagree with you reasoning.

1) Please notice that smilies indicate humor.

2) You write
Quote
(Udo: All the examples that use exclusive ORing, would break if LOW was not 0)
. This is not true. For which values of HIGH and LOW would this
Code:
digitalWrite(pin, state^= (HIGH^LOW));

break?

3) You seem to pick this up intentionally. IMHO teaching your "IF" style is no better. It promotes an imperative style and it does not consistently deal with undefined values. Notice: not your code example but the IF style.

4) There is not intrinsic API documentation for the Arduino. All documentation is outside Arduino.h. If you look into the header file you will immediately notice that the signatures are flawed:
Code:
void digitalWrite(uint8_t, uint8_t);
int digitalRead(uint8_t);

digitalRead delivers values of a different type than its counter part digitalWrite. At no place in the file is there any hint that the macros "HIGH" and "LOW" are to be used with these functions they are not even placed closely. So how would anyone correctly abstract from such an API?

Thus you refer to external "API specification". But which one? As it is this API requires developers to look up the code.

5) Your API example with PWM extensions is completely made up. Typically the method/function names should hint at their use. Extending functions that are prefixed with "digital" to allow for PWM seems not like a reasonable API extension to me. It actually changes the API semantics.

6) The "?" Operator is not a macro. It is an operator. IMHO the use of this operator should be more widely understood.
Logged

Check out my experiments http://blog.blinkenlight.net

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 58
Posts: 4002
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Is there a binary logic chip made that uses other than 0 for FALSE/LOW and 1 for TRUE/HIGH?
Memory, controller, processor, register?


But we are not talking about chips.
We are talking about how to write software to use a software API function.

Completely wrong. YOU are pissing up the API rope. And I know the difference and I specified it.

Quote
The s/w API says use these defines: "HIGH" and "LOW" to get things done.

The s/w API is written to run the chips, not conform to fairyland.

Quote
Don't get hung up on this specific digitalWrite() function call and how HIGH and LOW
map to the output states of the pin.

Or TTL, or CMOS, or .....

Quote
BTW, there are some chips that use separate set and clear registers.
In that situation, 1 bits written to a clear register will actually clear bits not set them.

if ( var != LOW ) then var = HIGH;
else var = LOW;

I guess THAT will work any better in your dragged up exception huh?
Certainly better than;

var = !var;

Yeah.... whoops! Didn't think of THAT didja?

As for the rest, when I want to get away from the metal I'll choose something other than C to do it. Perhaps Modula or Pascal would be abstract enough, or maybe LISP. For sure COBOL, DBASE, FoxPro, RPG or something along those lines. Something where the programmers are totally insulated from the realities of the hardware.

Make a C/C++ where people HAVE to play by your rules and see how fast it gets adopted as the real thing.
Logged

Examples can be found in your IDE.

Dallas, TX USA
Offline Offline
Edison Member
*
Karma: 47
Posts: 2328
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think many communication subtleties are lost in the written language.
I do appreciate the humor. And I think our thoughts and reasoning
are actually not very far off from each other.

@berrybap: although I agree that one should not rely to much on implementation details I disagree with you reasoning.

1) Please notice that smilies indicate humor.

2) You write
Quote
(Udo: All the examples that use exclusive ORing, would break if LOW was not 0)
. This is not true. For which values of HIGH and LOW would this
Code:
digitalWrite(pin, state^= (HIGH^LOW));

break?

After going back and looking closer, I admit I was very wrong on this point.
This method is quite clever and generates the least amount of code
of all the working portable examples.

Quote

3) You seem to pick this up intentionally. IMHO teaching your "IF" style is no better. It promotes an imperative style and it does not consistently deal with undefined values. Notice: not your code example but the IF style.

For myself, I don't prefer any method over any other, it is just that when trying to explain to person that is struggling
with understanding the basics such as an if/else construct, my suspicion is that it would be easier to help them
grasp an if/else methodology rather than introduce a new/additional language details such as bit twiddling or
ternary operators.

Now probably the real answer is to explain that if/else is one of many solutions and show other examples
that also work, but may use some syntax that they may be less familiar with.
And for completeness maybe even toss in an example that may also work but isn't fully portable.

Quote
4) There is not intrinsic API documentation for the Arduino. All documentation is outside Arduino.h. If you look into the header file you will immediately notice that the signatures are flawed:
Code:
void digitalWrite(uint8_t, uint8_t);
int digitalRead(uint8_t);

digitalRead delivers values of a different type than its counter part digitalWrite. At no place in the file is there any hint that the macros "HIGH" and "LOW" are to be used with these functions they are not even placed closely. So how would anyone correctly abstract from such an API?

Thus you refer to external "API specification". But which one? As it is this API requires developers to look up the code.

From the prototypes they would have to look at the code.
But that is generally the case with prototypes as they don't convey
information about the API itself only the parameters and return values.
This is why I like using doxygen for the documentation.
That way you can generate pretty documentation like HTML, and
yet still have the same documentation information in the actual code for those
that like to look at code.


From these pages, a developer could write working code.
http://arduino.cc/en/Reference/digitalWrite
http://arduino.cc/en/Reference/digitalRead

Which I'm guessing is what the majority of Arduino users are going by
rather than going in and actually looking at the underlying code.

And if all you have to go by is the reference pages, then you would want
to stick to using HIGH and LOW as the reference indicates.

However, it is not uncommon for API functions in C to be somewhat asymmetric in parameters vs return values.
While some call it sloppiness, it can be useful in some situations.
In some cases the return values from functions that return data are overloaded with status information.
In those cases, return values that land outside the range of the data are error/satatus codes.
Consider the stdlib functions getchar() and putchar().
The return value from getchar() is more than just a character it can also indicate an error.
Likewise the return from the read() function in the serial class uses this as well.


Quote
5) Your API example with PWM extensions is completely made up. Typically the method/function names should hint at their use. Extending functions that are prefixed with "digital" to allow for PWM seems not like a reasonable API extension to me. It actually changes the API semantics.

Of course it is made up. I even said as much.
However, I don't believe modifying digitalWrite() to support PWM would
really be changing the semantics as it still is creating a digital signal on a pin.
PWM creates a digital signal not an analog voltage.
It just isn't a signal that is a locked at a single level.
Yes PWM can be used to generate an analog voltage with some
additional components, but PWM itself, is still a digital signal.

analogWrite() is the call that is really messed up.
It does not create an analog level signal at all.

To me analogWrite() is what is misleading.
It hints at "analog" yet generates digital pulses.

The only reason that I bring up the digitalWrite() PWM example with HIGH and LOW
is that if you project Arduino out into the future to the point where you want to run it
on a micro controller that supports REAL analog outputs then the
existing digitalWrite()/analogWrite() API starts get a bit messy and confusing.

Should the day ever come where you need to support both PWM and real anlog output,
you will have to somehow resolve the difference between PWM output and real analog voltage output.
Having a call like analogWrite() complicates things in that it isn't really analog but digital PWM.

If, the digitalWrite() could encompass both steady state levels and PWM, then analogWrite() could
be used for real analog outputs.

And if you think about it, HIGH is no different than an PWM signal of 100% duty cycle
and LOW is no different than a PWM signal of 0% duty cycle.
So combining HIGH/LOW and PWM into a single API function
isn't that far fetched since they already essentially the same thing.

But in order to make a change like this requires everyone to use LOW/HIGH vs 0/1 or 0/non-zero
for digitalWrite().

Now does my craziness seem to make a bit more sense?

Quote
6) The "?" Operator is not a macro. It is an operator. IMHO the use of this operator should be more widely understood.
Totally agree.
I use it quite often.
Logged

Dallas, TX USA
Offline Offline
Edison Member
*
Karma: 47
Posts: 2328
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Is there a binary logic chip made that uses other than 0 for FALSE/LOW and 1 for TRUE/HIGH?
Memory, controller, processor, register?


But we are not talking about chips.
We are talking about how to write software to use a software API function.

Completely wrong. YOU are pissing up the API rope. And I know the difference and I specified it.

My methodology is that I focus on writing code that is portable that conforms to APIs whenever I can.
When circumstance won't allow it like it needs to be tighter to meet some kind of real-time
or space constraint need, I then go back and start making non-portable changes to optimize things.

Those non-portable changes can even extend to the point of completely bypassing an API and using assembler.


Quote
Quote
The s/w API says use these defines: "HIGH" and "LOW" to get things done.

The s/w API is written to run the chips, not conform to fairyland.

Exactly.
And suppose you need to support PWM *AND* an analog output?

How would you propose updating the API to support both PWM and analog output?
It could easily be handled by extending  digitalWrite() as I outlined above.
But then that requires users to strictly stick to the API of using HIGH and LOW for
steady state high and low level outputs.


Quote
Quote
BTW, there are some chips that use separate set and clear registers.
In that situation, 1 bits written to a clear register will actually clear bits not set them.

if ( var != LOW ) then var = HIGH;
else var = LOW;

I guess THAT will work any better in your dragged up exception huh?
Certainly better than;

var = !var;

Yeah.... whoops! Didn't think of THAT didja?
I have no idea what you meant by that.
Does the if/else example above even toggle the variable var between HIGH and LOW?

Also, I was not the one that proposed
var = !var;

That is one of the non portable examples I recommend against.


The only thing I'm saying is that when possible it is best to strictly follow
an API definition and use the specific parameters it specifies.
If you step outside of that, by taking advantage of the knowing the internal behavior
or the raw values of the abstracted parameters, you run the risk of your code either not working immediately
or potentially breaking at some point in the future.
I'm not sure why that is so controversial.

--- bill
Logged

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 58
Posts: 4002
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm not running 1.0 because it breaks code and I don't have to use any future new API that doesn't live by FALSE/LOW=0 and TRUE/HIGH=1. It's easier to change software than hardware IME.

Generally I have found that compatibility works sometimes and that efforts to preserve it too long result in BS like segmented architecture. Note how even through all the changes since around 1970 there are a few conventions that haven't changed as far as the mainstream?

Logged

Examples can be found in your IDE.

0
Offline Offline
Faraday Member
**
Karma: 19
Posts: 3418
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

@berrybap

Quote
However, I don't believe modifying digitalWrite() to support PWM would
really be changing the semantics as it still is creating a digital signal on a pin.
PWM creates a digital signal not an analog voltage.
It just isn't a signal that is a locked at a single level.
Yes PWM can be used to generate an analog voltage with some
additional components, but PWM itself, is still a digital signal.

However the issue is that this is not the only possible view of this API. Hence my conclusion is (for almost any API): you need to add a new API and phase out the old one gradually. Extending APIs in general is dangerous at least. I once participated in a comitee to discuss this kind of stuff and the final conclusion was that extension will be forbidden, only new interfaces are allowed. IMHO the other options have worse implications.

@GoForSmoke: segemented architecture? Have a look at this: http://www.astrodigital.org/space/stshorse.html.






Logged

Check out my experiments http://blog.blinkenlight.net

Pages: 1 [2] 3   Go Up
Jump to: