Go Down

Topic: What happens before the loop function? (Read 604 times) previous topic - next topic

Buffalo_Chip

Hi! I just got my Arduino today and am having some serious questions about the programming. I am just learning, so if I use a phrase in this post like "declare a variable," I may be saying it wrong. Please correct me.

I am basically trying to get to the point where I can write my own programs. How do I figure out what variables need to be declared prior to the setup function and within the setup function? And what types of variables should I not mess with until I get into Loop?

There are 2 example sketches that do the exact same thing, but the code before loop() is entirely different between them.

The examples are in Basics-->Fade and Analog-->Fading

The Fade sketch has
Code: [Select]
pinMode(led, OUTPUT);
In the setup function, however, the Fading sketch has nothing in the Setup, but it still works. Why is that?

And here is the code from Fade prior to the Setup function:
Code: [Select]
int led = 9;           // the pin that the LED is attached to
int brightness = 0;    // how bright the LED is
int fadeAmount = 5;    // how many points to fade the LED by


So what is the difference between variables that show up in the Setup function, and those that are in the code even before Setup? How do I know where to put them? Why aren't brightness and fadeAmount in setup() in the example? Why are they before setup()?

Why is there so little code prior to the Loop function in the Fading sketch?

I realize this is a lot of questions but if anyone can answer 1 or 2 of them, I can probably figure it out from there.

Thank you!!

Arrch

Google "c++ scope".

In a nutshell, where a variable is declared dictates where it can be used.

eddiea6987

if you take away the code in the setup part  of the fade sketch it will still work ,
try getting a book for beginners in arduino it will really help you understand some very basic things like this
I could print the Arduino logo on a box of cereal and sell it as "Arduin-O's"

cr0sh


In the setup function, however, the Fading sketch has nothing in the Setup, but it still works. Why is that?


Basically, because the Fading sketch is written correctly, while the other one is incorrect - see the documentation on AnalogWrite():

http://arduino.cc/en/Reference/AnalogWrite

Note where it reads "You do not need to call pinMode() to set the pin as an output before calling analogWrite()."


So what is the difference between variables that show up in the Setup function, and those that are in the code even before Setup?


As already noted, what is confusing you is called "variable scoping"; variables declared inside a function may only be accessed within that function (iirc, this extends to certain block-level constructs as well - though I may be getting C++ and other languages with similar syntaxes confused). The scope of the variable is said to be "local" to that function. As soon as the function exits (ie, returns to the caller), that variable goes "out of scope", and it's value is lost.

Variables declared outside of the functions have what is known as "global scope". They can be initialized in the declaration, as well as read and changed within -any- other function. Note that such variables (globals) are usually considered a "bad thing" - but in the world of embedded programming (especially with small memory spaces), you basically have to live with them.

Why are they considered "bad"? Well, mainly because any function may change the value, and throw off what other functions may be expecting the value to be. There's also the issue of variable "name space" - if you aren't careful with your variable names, you might try to declare or use a variable locally in a function only to be met with weird bugs and such because that stomped on the global definition.

One way to combat this is to carefully name your variables - for instance, say you had a global integer variable for temperature - you could declare it like this (prior to "setup()"):

Code: [Select]
int temp = 0;

...but it would probably be better to declare it like this:

Code: [Select]
int gintTemperature = 0;

You could get fancier, but the goal here is to make it very readable, as well as to describe it's purpose - with such a declaration, you and others can easily tell that it's a global variable (the "g"), that its an integer type ("int"), and that its for some kind of temperature. Makes it a bit more difficult to stomp on, ultimately.

Most of the time, such global variables are used because it is easier to do it like this (from both a newbie and embedded point-of-view), than it is to use pointers in the function calls, or other means to transport variable values into and out of function calls (a function call can normally only return one value after it is called; and really, if you code things right, you shouldn't ever have to resort to tricks to update multiple values in a singular function call - that there says that your function needs refactoring).

Quite a bit of this can be alleviated by using object-oriented programming (because the Arduino's language is C++ after all) - create a class with public and private variables, and let your methods update the public members as needed - or keep everything private, and have a bunch of getter/setter methods, etc.


How do I know where to put them? Why aren't brightness and fadeAmount in setup() in the example? Why are they before setup()?


Variables that are meant to be global -must- be declared prior to setup(); the reason why is because the pre-processor that runs when you compile the code parses through the code and only deals with the globals declared there; if you tried to declare them between setup() and loop() - they would be skipped by the pre-processor. Really, it's was a decision made by the Arduino designers for the pre-processor, not really as a limitation to the language. That said, in most C/C++ programs, globals tend to be defined prior to any functions (especially main() - you may wonder why this function seems mysteriously absent - it gets inserted and built by the pre-processor, and actually "wraps" the setup() and loop() functions - all of that is mainly hidden from you).

brightness and fadeAmount aren't in setup() because then they wouldn't be global variables; if however, you had a need to initialize them in setup() - say you needed to read from the EEPROM or something - it could be done like:

Code: [Select]

int led = 9;           // the pin that the LED is attached to
int brightness;    // how bright the LED is
int fadeAmount;    // how many points to fade the LED by

// the setup routine runs once when you press reset:
void setup()  {
 brightness = 0; // set the brightness of the LED to nothing (off)
 fadeAmount = 5; // set the fade speed to 5 units
}


The variables are still all globals, but a couple of them are initialized in the setup() function, while the pin assignment is kept at the global declaration level.


Why is there so little code prior to the Loop function in the Fading sketch?


Because in this simple example, the author didn't need to declare anything in a global scope; all of the action happens in loop() and nowhere else. The only reason the pin number was set as a global was so that if you wanted to easily change the pin number to something else, all you have to do is find it at the top of the code, change it, and it would all continue to work. This is another reason for globals - they can act as easy-to-use setup parameters in your sketch; as long as you are religious about using them, and declare them early, and give them good meaningful names - or at least comments that say what they are - then you will have easier to maintain and debug code.

Note that while you can use and declare variables in such a manner, sometimes it may be better to use and define constants instead; constants are substituted in at compilation time wherever they are used (they aren't variables - that's why they are called "constants"); think of it as a search and replace operation that happens at compile time.

I also want to note that you should, when and wherever possible - to avoid a bunch of so-called "magic numbers" from cluttering up your code. These are values in the code that are not necessarily "self-evident"; instead, put them in a constant or a variable (named properly and commented about what the purpose of the number is), and then reference that constant/variable in your code. I can't tell you the number of times I have looked at code and this value or that value was being used in an obscure way, and I am left scratching my head wondering "why is this value read from this sensor being multiplied by this other numeric value - what is that value for?").


I realize this is a lot of questions but if anyone can answer 1 or 2 of them, I can probably figure it out from there.

Thank you!!


I hope this helps - ultimately, you should study up on C/C++ programming, and what certain terms and issues there are, as well as any good tuturials on arduino programming in general.
I will not respond to Arduino help PM's from random forum users; if you have such a question, start a new topic thread.

Buffalo_Chip


I hope this helps - ultimately, you should study up on C/C++ programming, and what certain terms and issues there are, as well as any good tuturials on arduino programming in general.


Cr0sh, this helps quite a bit actually! Thanks so much for taking the time to explain it. After a few of the other replies last night I did some searching and reading on variable scopes, classes and all that, but really it was over my head and I understood 5% of what I found. Your explanation was a lot easier for me to understand, so thank you.

Buffalo_Chip


Basically, because the Fading sketch is written correctly, while the other one is incorrect - see the documentation on AnalogWrite():

http://arduino.cc/en/Reference/AnalogWrite

Note where it reads "You do not need to call pinMode() to set the pin as an output before calling analogWrite()."


I looked at the analogWrite reference as you suggested. At the bottom of the reference page, there is an example sketch where they declare a pin used for analogWrite as an output in Setup(). So I have to guess that this following line is optional, since it's not required to use analogWrite:

Code: [Select]
pinMode(ledPin, OUTPUT);   // sets the pin as output

Here is the entire sketch:

Code: [Select]
int ledPin = 9;      // LED connected to digital pin 9

int analogPin = 3;   // potentiometer connected to analog pin 3

int val = 0;         // variable to store the read value



void setup()

{

  pinMode(ledPin, OUTPUT);   // sets the pin as output

}



void loop()

{

  val = analogRead(analogPin);   // read the input pin

  analogWrite(ledPin, val / 4);  // analogRead values go from 0 to 1023, analogWrite values from 0 to 255

}

retrolefty

Quote
So I have to guess that this following line is optional, since it's not required to use analogWrite:


Yes, but I would consider it a good practice to always define the mode and pins numbers of all the I/O pin your sketch is going to use.

Lefty

econjack

Actually, most programmers treat define and declare as meaning the same thing. They don't. See reply #4 at:

http://forum.arduino.cc/index.php?topic=177199.0

For a simple discussion of scope, see reply #16 at:

http://forum.arduino.cc/index.php?topic=180672.15

I hope these help.


Tom Carpenter

If it helps, this is the analogWrite() function:

Code: [Select]
void analogWrite(uint8_t pin, int val)
{
// We need to make sure the PWM output is enabled for those pins
// that support it, as we turn it off when digitally reading or
// writing with them.  Also, make sure the pin is in output mode
// for consistenty with Wiring, which doesn't require a pinMode
// call for the analog output pins.
pinMode(pin, OUTPUT);

//PWM stuff is then done here...


notice that there is a pinMode(pin,OUTPUT) statement called by the function. Another case of the Arduino core being horribly inefficient.
~Tom~

Go Up