got some questions about a code

Hello everybody,

Let me introduce myself, My name is Miguel and I'm a newbie at programming.
I do have some electronic skills but programming is a whole new thing for me.
So I have been busy the last couple of days with Arduino and I noticed something.
Like the next piece of code I have some questions about it, I actually found it on the forum here:

#define button 8
#define Q 13
#define Q_ 3


boolean stat;

void setup(){
  
  pinMode(Q, OUTPUT); 
  pinMode(Q_, OUTPUT);
  pinMode(button, INPUT);
}

void loop(){
  if (digitalRead(button) == true) {
    stat = ~stat;
    digitalWrite(Q, stat);
    digitalWrite(Q_, ~stat);
  }
  
  while(digitalRead(button) == true);;
  delay(100);
}

First question,
Sometime I see the use of use ''define'', like in this code to make certain pins have a name. Can I always do that or is there some trick to it how or when to use it.
The Arduino explanation says that you save memory on the chip when you do that, but doesn't it also slow down your program

Second question,
What happens over here

stat = ~stat;
    digitalWrite(Q, stat);
    digitalWrite(Q_, ~stat);

What does ''stat'' and ''~stat'' mean?
Can't really find what it is supposed to do?

Maybe it is simple but it would be really helpful for my knowledge on the code and how to read it!

Bit invert "stat" and assign the value to "stat"

As a matter of style it’s generally preferred to use a const definition rather than a #define, but both ways work.

You can use any name that isn’t a reserved ‘C’/C++ word and doesn’t clash with any other existing definitions in the AVR runtime framework or the Arduino runtime framework or any libraries you’re using or your code. It’s recommended that the names you choose are meaningful and not easily confused with each other - Q and Q_ fails both criteria. The name stat is also pretty opaque out of context - some comments could have made that ore obvious.

Snake035:
First question,
Sometime I see the use of use ''define'', like in this code to make certain pins have a name. Can I always do that or is there some trick to it how or when to use it.

Of course you can always do that. I would always recommend it when you use fixed pin numbers; that is pin numbers pre-determined at the start of your program.

The Arduino explanation says that you save memory on the chip when you do that, but doesn't it also slow down your program

No. They do not use extra memory at all. You see, it is a simple compile trick. The compiler simply replaces every constant by a value defined by #define. It does not matter whether you say "10" or C" [when defined by #define C 10]. The program itself does not change at all.

You see, it is a simple compile trick.

No, its not the compiler it's the c pre-processor which deals with #define etc . The pre-processor is a stand alone program and is sometimes used to expand text just for fun.

Mark

ArtificialUser:
(...)

The Arduino explanation says that you save memory on the chip when you do that, but doesn't it also slow down your program

No. They do not use extra memory at all. You see, it is a simple compile trick. The compiler simply replaces every constant by a value defined by #define. It does not matter whether you say "10" or C" [when defined by #define C 10]. The program itself does not change at all.

I think that he talks about to use #define instead of a const int. In that case I don't have 100% sure that you don't save memory.

PeterH:
As a matter of style it's generally preferred to use a const definition rather than a #define, but both ways work.

You can use any name that isn't a reserved 'C'/C++ word and doesn't clash with any other existing definitions in the AVR runtime framework or the Arduino runtime framework or any libraries you're using or your code. It's recommended that the names you choose are meaningful and not easily confused with each other - Q and Q_ fails both criteria. The name stat is also pretty opaque out of context - some comments could have made that ore obvious.

I think the program is a flip-flop simulation. In that case, I see the criteria of the names. "stat" is the internal status of the flip-flop, "Q" is the output "Q_" is not Q or Q "bar" (i.e., the inverse of the output).

luisilva:
I think the program is a flip-flop simulation. In that case, I see the criteria of the names. "stat" is the internal status of the flip-flop, "Q" is the output "Q_" is not Q or Q "bar" (i.e., the inverse of the output).

Maybe so - it's mediocre code either way.

Which memory? Remember we have both program memory and SRAM they are not interchangeable! In some cases a define may save memory of both types. But it's just not worth taking any time over. The compiler does so much optimisation it's best just to ignore the issue until you run into problems.

Mark

The program that you have it's the same that follow:

boolean stat;

void setup(){
  
  pinMode(13, OUTPUT); 
  pinMode(3, OUTPUT);
  pinMode(8, INPUT);
}

void loop(){
  if (digitalRead(8) == true) {
    stat = ~stat;
    digitalWrite(13, stat);
    digitalWrite(3, ~stat);
  }
  
  while(digitalRead(8) == true);;
  delay(100);
}

I delete the defines and I replace the name by the value that is defined.

Now, lets say that for some reason we need to change the pin where the button is placed. What we need to do? We need to find all the places where this pin is read and defined (pinMode). This is a very simple program and we only use the button pin in 2 places. If we have a program that we use this button pin in 15 places, for example, this task will be more difficult. This is one of the reasons why we use the define. Using the define we only change the pin number in one place (even if we are using that pin number in 15 places, because in the 15 places we have the name and we only have one place where we really have the pin number).

The question about the stat...
stat is a variable. A variable "holds" some value that we need in our program. In this case, stat, is a boolean variable, that means that only can hold 2 values: true or false. As we don't initialize the variable (that is, we don't say what is the initial value of the variable) the initial value will be "false" (more exactly "zero" meaning "false").
Like AWOL said, the line stat = ~stat; will invert the value of stat (is what means the ~stat) and hold the value again in stat. So, stat will begin with "false", and after this line was processed it will hold the value "true" (the inverse of false); the next time that this line will be processed it will hold "false" again, and so on.
This can be done in other way, like:

   if (stat == false ) {
      stat = true;
   }
   else if (stat == true) {
      stat = false;
   }

but as you may see its much more easy to do in the way that is in your example.

luisilva:
So, stat will begin with "false", and after this line was processed it will hold the value "true" (the inverse of false); the next time that this line will be processed it will hold "false" again, and so on.

That's not quite accurate. The ~ (tilde) operator is the BITWISE NOT operator. If you apply the operator to zero, you get a value with all bits set (0xFF). This is neither true, false, HIGH, LOW or any of the other values that you might reasonably expect digitalWrite() to handle. Luckily the people who implemented digitalWrite() coded it in such a way that it treats any non-zero valiue as HIGH. It's very poor practice to rely on this sort of behaviour though - the argument to digitalWrite() should be an unsigned 8-bit integer containing the value HIGH (1) or LOW (0). Since these coincide with the language defined values true and false, it would be more sensible to use a logical NOT operator rather than a bitwise operator to toggle the values.

PeterH, I don't know what I can say about your last reply...
Why you say that 0xFF is not the value of true?!
If you read my reply I put false and true inside quotes.
I think I give to Miguel the answer the that he need to read right now. Nevermind what is "true" or "false" or "HIGH" and "LOW"... he neither ask about it! And the most important, I think that talk about it is confusing to someone that is starting (because if he don't know what is stat = ~stat; he is starting in programming, or at least starting programming in C language).

To answering my question, think about what will do this piece of code:

   while (0xff);

EDIT: I like this part:

Luckily the people who implemented digitalWrite() coded it in such a way that it treats any non-zero valiue as HIGH.

:slight_smile: :slight_smile: :slight_smile: :slight_smile: :slight_smile: :slight_smile:

EDIT2: I like this part too:

Since these coincide with the language defined values true and false, (...)

What language? C language or "your C language"?

EDIT3: Even your first reply is very pour. When you say:

(...) Q and Q_ fails both criteria. The name stat is also pretty opaque out of context - some comments could have made that ore obvious.

Like I said before, I think you are wrong! In the context that the code was wrote I think they are the best! (it could be "output" and "not_output", but, one more time, in the context, I think that maybe "Q" and "Q_" are better than the others)

luisilva:
Why you say that 0xFF is not the value of true?!

Because the value of true is 1, not 0xFF. This is defined by the language standard.

The value is being passed to digitalWrite(), which is documented as accepting uint8_t values HIGH and LOW. AT a pinch it's OK passing in true and false since we know that HIGH and LOW coincide with these and it is convenient to be able to use boolean logic on the values. What is much less OK is passing in arbitrary int values which have been derived by bit-munging. It's very poor practice and only works at all because of the diligence of the people who implemented digitalWrite().

Yes, the original poster is presumably a novice programmer and that is why I am pointing out that the code he's looking at is poorly written and does things that are not recommended - this is not a good example to be followed.

luisilva:
To answering my question, think about what will do this piece of code:

   while (0xff);

The language defines that operators taking boolean values will interpret any non-zero value as true. This does not mean that you can pass a non-zero value to any function that expects to receive values 0 and 1, and assume that it will treat them the same. The fact that digitalWrite() does is just due to the care that was taken writing it and is not something that coders should be relying on. It happens to work, but is poor practice. When you're using an API you should use the types and values that the API declares.

Because the value of true is 1, not 0xFF. This is defined by the language standard.

Can you please point me here is this?

I suggest that instead of randomly trying stuff, you get a good C/C++ textbook, and read it.

The problem over what "true" is defined as, is a good reason for encouraging people to write

if ( stat )
{

}

instead of the silly

if ( stat == true )
{

}

Some computer languages require the second formulation, C/C++ does not. And if "true" has the specific value of 1, and stat is not 1, then you can get potentially spurious behaviour.

If stat is a boolean variable, the correct way to invert its state is using !stat , not ~stat

Since a flip-flop is a shift register component it would be an interesting and educational exercise to expand that snippet to an n length shift register...

Doc

The fact that digitalWrite() does is just due to the care that was taken writing it

That's a very odd assertion. How do you know that the person or persons who implemented digitalWrite( ) were careless ? If they had been "paying more attention", as you imply that they should have been, what sort of run-time type checking do you propose that they should have implemented for the Arduino platform, if they had been taking more "care" with what they were doing ??

PeterH:
(...)
Because the value of true is 1, not 0xFF. This is defined by the language standard.
(...)

This is not from the standard, but anyway:

Defining (...) true and false (Boolean Constants)

There are two constants used to represent truth and falsity in the Arduino language: true, and false.
false
false is the easier of the two to define. false is defined as 0 (zero).

true
true is often said to be defined as 1, which is correct, but true has a wider definition. Any integer which is non-zero is true, in a Boolean sense. So -1, 2 and -200 are all defined as true, too, in a Boolean sense.

You can find the original text here.

luisilva:

Because the value of true is 1, not 0xFF. This is defined by the language standard.

Can you please point me here is this?

No, beacause it is wrong, only ‘false’ has an explicit definition ( 0 ). I can however point you to what the standard does say:

4.12 Boolean conversions [conv.bool]
1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a
prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false;
any other value is converted to true. A prvalue of type std::nullptr_t can be converted to a prvalue of
type bool; the resulting value is false.

And finally what is a bool really:

3.9.1 Fundamental types
7 Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively
called integral types.48 A synonym for integral type is integer type. The representations of integral types
shall de?ne values by use of a pure binary numeration system.49 [Example: this International Standard permits 2’s complement, 1’s complement and signed magnitude representations for integral types. — end
example ]