Tuesday, November 25, 2014

Variables, Counting Numbers, and the For Loop

At the end of the last lesson, we talked about using shadow registers to manipulate a port register and avoid manipulating individual bits of the port register because of Read/Modify/Write issues.  You also may have noticed I defined a variable, but I have only briefly talked about variables in an earlier lesson.  We are going to expand further variables in this lesson, and on shadow registers in the next lesson. This lesson will also introduce the "for" loop.

VARIABLES

A variable in C is basically a name which we assign to a memory location, which holds a number we are working with.  The compiler selects the next available memory location for us automatically.  In math, we use variables to represent a number which may change, such as T for time, or to represent a number which is a constant, such as the letter G for the gravitational constant. When we are writing a C program, we use variables as a placeholder for numbers to help make the code more manageable. Most C programs use many variables.

In our project, we have been looking at how to turn on and off an LED.  In our lesson about generating time delays, we looked at making the LED flash on and off for as long as we hold down our button.  In a new project, we will output the count of a variable to our LED so that we can visually see the variable in use. For this simple task, we are going to need two variables, but first, let's look at the types of variables, and the rules for naming them.

VARIABLE TYPES

Figure 1.

In one of our first lessons, A Crash Course Introduction to C, we saw the same table as in figure 1.  This table shows the different Variable Types, how much memory space they require, and what their decimal range is (what size number they can hold).  You will notice that unsigned Data Types refer to numbers who's values are only positive, while signed Data Types can be either positive or negative numbers.  Negative numbers in C would be used for things such as negative voltage values, temperatures, in calculator type programs, or even to determine what direction a motor is turning.  I have only ever used positive numbers in my C programs, so we will stick with those here as I don't want to give false information on a subject I'm not familiar enough with.

The size of the variable types refers to how much memory space they require. A bit requires one bit of memory space, where as a Character requires 8 bits, or 1 byte.  Decimal range tells us how far that Data Type can count.  We can tell one bit to be either on (1) or off (0).  We can tell a byte to be any combination of ones and zeros across 8 bits (00110100).  As it happens, one byte can be 256 combinations, thus we can use 1 byte to count from 0 to 255 (256 combinations if you include 0).  The mathematical formula for determining this range is: 2 possible states (0 or 1) across 8 bits, or 2^8 (two to the power of 8) = 256.  2^16 (for two bytes) = 65536.

If we want a variable which can count to 5, we would need to use one byte of memory space, since this is the smallest type which can count to 5.  Thus, we will select the "unsigned char" data type.  We could select something larger than this to accomplish the same thing, as using 2 bytes also can count to 5, but we would be wasting memory space.

A note about variable overflow.  An overflow is when a value exceeds the allowed number span. For instance, using the unsigned char data type which can range from 0 to 255, if we asked the program to do the calculation 255 + 1 the result would equal 0, and not 256 because we would have exceeded the range of the unsigned char type and the variable has overflowed.  If our program used the variable to continue counting up from 0 and not stop, the unsigned char data type would continue to count from 0 to 255 over and over again.  If you wish to avoid this overflow, you would need to choose a different variable data type, that can cover the range you require.

For our example program, we will utilize the unsigned char variable type.  All we have to do now is tell the compiler what we want.  Let's see how to define a variable type (Figure 2).

Figure 2.

You can see this code snippet begins a new program.  After the usual comments, and inclusion of the #include and configuration word at the top of the program, I have dedicated a space to defining variables. Notice the continued use of comments to break up the program into sections, which continues to help keep our program neat and easy to read.  Line 13 shows the statement "unsigned char count = 1;".  Remember that all statements end with a semicolon.  I have used the C Keyword "unsigned char" to define this variable as a character data type with no sign (as in no negative sign).

After the C Keyword comes our variable name, which in this case is "count". A name for a variable is called a "Variable Identifier", and you can name your variables just about anything, as long as it's not a C keyword such as "main" or "char".  A list of reserved C keywords is shown in figure 3. A C Variable Identifier is a sequence of letters and digits (no spaces), including the underscore which is considered a letter. The identifier must start with a letter and not a number, and although you can start with an underscore, you should avoid doing so, as such identifiers are reserved for the compiler's use and should not be defined by your programs.  Two more rules about variable names: They are case-sensitive, so "count" is different from "Count", and your variable name cannot exceed 255 characters.

Figure 3.
After naming the variable I have given it a value, in this case a value of 1.  Not all variables require assigning a value but it is appropriate in this example as you will soon see.  You may have noticed that I defined this section as "Define Global Variables".  Within a program you can have Global Variables and Local Variables.

Global variables are known throughout the entire program, and can be called again and again by any function within the program.  They take up a permanent residence in the program memory and are defined outside any function, including the main function.

A local variable however, is specific to just one function in the program and is created by the function itself.  Local variables are a great way to keep memory space down, as a local variable is created and stored in the RAM for only as long as the function needs it. The memory location is then freed for other uses.  As stated, a local variable can only be called within the function that created it, and would not be recognized anywhere else in the program.  If the same function is called again in the program, that local variable would be created again, used, and then discarded.  We will see a local variable used in our program below.

The For Loop

As we know, a loop in a C program is executed repeatedly so long as a condition is true, such as the while(1) loop which is always true.  A for loop is a function which repeats a specified number of times, and evaluates an expression on each loop to test whether the condition is still true.  If we had a task for instance, that we wanted to repeat ten times in a row, we could just type the same statements ten times in a row which would get the job done, but that would be ridiculous.  Alternatively, we would use a for loop.

The basic structure of a for loop is:   for(variable initialization; evaluate condition of true/false; update variable).  Notice that the for loop contains three statements within the parenthesis, and each statement is separated by a semicolon.  If I wanted to flash our LED 10 times, I could type the following instructions 10 times:

GP0 = 1;
GP0 = 0;

This would be impractical though, rather we will use a for loop to accomplish the same result.  Let's assume we have a variable named "flashes" that we defined in our global variables section.  The for loop would initialize that variable, use a conditional statement to see if we have reached 10 iterations of the loop, and if not, update the variable to account for the 1 iteration of the loop the function is currently doing.  Here's how that would look:

for(flashes = 0; flashes < 10; flashes ++)

What this function is doing is making our variable flashes equal to zero, testing whether flashes is less than 10, and if it is, incrementing the flashes variable by 1.  Flashes ++ is the same as saying flashes + 1. Each time the loop completes, the variable has 1 added to it, and when the conditional statement flashes < 10 is no longer true (i.e. flashes is no longer less than 10), the loop stops.

We can further accomplish the same thing by counting backwards, or decrementing our variable.  We set the value of the variable to 10, test whether the variable is greater than zero, and if it is we subtract 1.  Here's how that looks:

for(flashes = 10; flashes > 0; flashes --)

The same number of loops is performed using both methods.

Remember earlier I said that when we only need a variable within one function, we can use a local variable to save memory space?  This is a great example of where we can use a local variable.  Rather than declaring "flashes" to be a global variable, we really only need it within the for loop, so we will remove the global variable and declare flashes as a local variable within the function.  We are only counting to 10, so we can use an unsigned char variable type.  Here's how it looks:

for(unsigned char flashes = 0; flashes < 10; flashes ++)

I have simply created the variable flashes right in the for function.  The variable is created, used, and discarded all within this one function

Putting it Together; Completing the Program

Our program will operate by counting a variable up from 1, and then flashing a LED an equal number of times to visually represent the value of the counter variable.  We will accomplish this by use of two variables, one global and one local. Each time we complete a loop cycle, the counter gets incremented, the value of the counter gets copied to the local variable, and the for loop flashes the LED that many times.  We make use of the delay macros to slow program operation down so that we can actually see the on/off cycles of the LED, with a longer pause after each count increment so we are able to discern the value of the counter.

Figure 4.

Our completed program is shown in figure 4. I have made use of comments to describe the program in lines 1 through 7, and included the xc.h header file on line 9. I have heavily used comments to break up the program code, as our programs are slowly growing lengthier and more complex, which keeps us in good practice of making our code easy to read.

The configuration word then follows, which is the same as in previous programs.  Line 16 defines our global variable "count", which will be our counter variable. I set the value to 1 here so that on the first pass of the for loop, the LED flashes 1 time rather than 0 times.

The code on lines 18 through 20 define the I/O map, which sets up our shadow registers for the databus and Input/Output. This also helps to keep the program code more portable to other devices, and easier to change for other uses. It is a good practice to follow.

Since we are using the delay macros we must define our crystal frequency; line 23 does this.

Our main program function begins on line 26. I have then copied the initialization values of TRISIO and GPIO to their respective registers to initialize data direction.

Our endless while loop begins on line 30.  Line 32 starts our for loop function, where we declare an unsigned char variable type named flashes, set the value of flashes to equal the value of count, test that flashes is greater than 0, and if so, we subtract 1 from the flashes variable.  The instructions within the for loop function brackets ({ and }) will be executed repeatedly until flashes is equal to 0, then the loop will stop.

Lines 34 through 39 contain the instructions within the for function. The LED is turned on for 100 milliseconds (0.1 seconds), then turned off for 100 milliseconds.

Line 40 has exited the for function, and we are now within the while(1) loop, so these instructions only get executed once the for loop is down counting flashes down to zero.  Line 40 is our slightly longer delay, which helps us to discern the number of flashes we had in the for loop. In this case, the delay is 500 milliseconds.  There is a typo in the comment following this instruction, this delay is 1/2 second, not 3/4 second.

Line 41 adds 1 to our "count" variable so that the next time flashes is made equal to count, we see the addition as an added flash of the LED. Our program wraps up by closing the brackets on the while(1) and main functions.

Note the comments at the bottom of this program.  There is still one more lesson we need to cover before we can fully utilize shadow registers without making the program confusing.  We will cover that topic in the next lesson.

You can now update your project by typing in the code in Figure 4 and flashing the .hex file to your microcontroller.  Use the same hardware as in the previous project, but simply remove the push button, as it is not needed.

I hope you have enjoyed this lesson and found it to be useful. Comments are always welcome.

No comments:

Post a Comment