into the third parameter:
// bad function
void adder(uint8_t a1, uint8_t a2,
uint8_t results)
{
results = a1 + a2;
}
Let's call it with:
adderTest()
{
uint8_t add1 = 1;
uint8_t add2 = 1;
uint8_t results = 0;
adder(add1,add2,results);
if(results == 2) getRewarded();
else getBoinked();
}
If you think 1 + 1 = 2 in this example, then prepare to
get boinked. In the adder function, 'results' = 2, but this
doesn't change anything in the parameter list in the
adder Test() function that called the adder() function.
Returns
Ouch! Boinking hurts, so let's make adder work right.
We change the return type from void to uint8_t:
// good function
uint8_t adder(uint8_t a1, uint8_t a2)
{
uint8_t results;
results = a1 + a2;
return (results);
}
Now you will get rewarded. (And we have a useless
function. If we want to add 1 and 1, we just use the
+ operator to add them, but you get the point.)
Automatic And External Variables
Another way to do the adder() thing would be to use
an external variable (global). These are variables defined
outside any function, usually in a header or before main(),
and are available for any function to use. We contrast
external variable to automatic variable, such as the uint8_t
results declared in the above adder() function. This
variable is created 'automatically' every time the function
is called and disappears when the function is exited.
Using external variables, we could write:
// define the function
void adder(uint8_t, uint8_t);
// create an external variable
uint8_t results = 0;
SMILEY’S WORKSHOP
int main()
{
unsigned char add1 = 1;
unsigned char add2 = 1;
adder(add1,add2);
if(results == 2) getrewarded();
else getboinked();
}
// This works because 'results' is external
void adder(unsigned char a1, unsigned char a2)
{
results = a1 + a2;
}
This would work fine, unless an interrupt triggered
right after we set results in adder() and changed external
variable 'results' to 3. Then, when the interrupt finishes
and we look at results in main() we get boinked again.
Be very careful using external variables. You never
know where they've been or what kind of nasty stuff
they might track in. And, unlike automatic variables,
they permanently occupy memory. Carefully used
however (as in our wearable clock), they are just fine.
Variable names have scope, meaning the sections of
code where they are recognized that determine how they
can be used. External variables can be used by anything in
the module, but variables defined within a function can
only be used by that function.
Volatile
We can add the qualifier 'volatile' to a variable name
to tell the compiler to leave it alone when it is optimizing
the code (it’s complicated and compiler-dependent).
Usually, we only need to do this when the variable will be
changed by an interrupt as in our clock code. Newbies
using interrupts often forget about volatile and have
mysterious difficulties to finding bugs as a result.
Due to space limitations, we will put some additional
C syntax and compiler topics into a downloadable
supplement listed at the end of this article.
Computer Time Keeping
Computers are made of electric circuits that rely on
one sequence of events completing before the next
sequence of events can be looked at (sequential state
machines). Changes in voltages and currents take time
to stabilize so for each change in the state of the
microcontroller, it must wait a bit of time to let things
settle down before allowing states to change again. The
quickest that the computer circuits can settle determines
the fastest speed the computer can run. The datasheets
provide this value, for instance, some AVRs have a
maximum frequency of 20 MHz, meaning that they will
run reliably at that speed. They might run just fine at
25 MHz, but Atmel won't guarantee it and it is kind of
February 2009 75