Sunday, December 28, 2014

New Project: Small Traffic Signal

We will use what we have learned up to this point to build a new project, a traffic light.  This project hopes to accomplish a few things:

Look at project planning from the ground up.
Introduce the PIC16F628A.
Use the device datasheet to make project decisions.
Introduce the use of flow-charts for project planning.
Show how changing to a new PIC device does not change everything about how programs are written.

A note about this traffic signal. I understand that in different parts of the world, traffic signals can vary in how they operate. Because I am familiar with traffic signal operations in the United States, this project will be based off standard US signals.

PRE-PROJECT PLANNING

When planning a new project, it is likely that you are working on that project because you want to accomplish something, whether it be control of peripheral features or some timing operation for something.  The very first thing in planning a new project is to write down the goals of your program.  What do you need it to do?  Writing down the needs of your program as a list on paper will help you to itemize your programming tasks.  Figure 1 shows the goals of this traffic signal program.

Figure 1.
Both the main road and the crossing street should have red, yellow, and green lights.  Many intersections in the USA will change to a flashing signal after a certain hour when traffic is less, so I would like this program to contain a flashing "Night Mode" which flashes red to both streets.  The signal should be safe, meaning that the traffic light never shows an unsafe signal, such as green in both directions.  The flashing night mode will be selected using a single-pole single-throw toggle switch connected to an I/O pin.  To keep the part count lower, I am only representing half of a standard signal, which requires 6 LEDs.  A full signal would have three lights for each direction of an intersection, requiring 12 LEDs.  Since we have 6 lights which are independently controlled plus the toggle switch for night mode selection, we need 7 I/O pins.  The PIC12F629 that we've been using up to this point only has 6, so we need to upgrade to a larger device, the PIC16F628A.

Why was the PIC16F628A selected?  Not for one specific reason.  It is a great practice and teaching microcontroller because it has two I/O ports, a wide range of features, an internal oscillator, plenty of program memory space, etc.  You really could use any PIC that has enough I/O ports and the features you need, but I selected this one because I have it in my parts bin, I have used it a lot for my own practice, and they are cheap and easy to get.  At the time of this writing, they are available here for just $2.08USD each.

As programs grow more complex, even slightly so, you will find it beneficial to create a program flow chart (Figure 2).  A program flow chart is used to show how a program proceeds through tasks, and what kinds of decisions the program has to make.  For the traffic signal program, we can see in the flow chart that at the beginning of the program the ports are initialized and then the main loop is entered.  The first task in the main loop is to evaluate whether night mode is selected. Program execution branches here depending on a yes or no response.

Figure 2.
If night mode is not selected, the traffic signal enters standard operation, which starts by displaying a red signal in all directions for 3 seconds before the main road is given a green signal.  You can see that the flow chart depicts fairly standard traffic light operations.  Notice at the bottom of the standard operation flow chart that after the main road red/cross road yellow signal, the program returns to evaluate if night mode is selected.  You might ask why we don't show red in all directions here again.  The answer is that if we programmed red in all directions for 3 seconds here, then the program went to the top and was evaluated no night mode again, we would proceed back to the first standard operation instruction which is red in all directions for 3 seconds.  Having this instruction at the bottom and at the top would result in an all-way red signal for 6 seconds.  Drivers would get impatient.

If night mode is selected at any point, night mode would be evaluated as a yes and a red signal would flash on and off in both directions at a 1/2 second interval.  This is the standard flash rate for flashing signals.  Night mode selection is evaluated again after each on/off cycle.

These flow charts are very useful to understand what we need our programs to do at a glance.  We can look at a block as a task and ask ourselves, how can I accomplish this task?  It makes it easier to look at and write a program one task at a time, rather than trying to think of the whole program.  Trust me, that can be pretty overwhelming.

EVALUATING HARDWARE

We now know what our program is going to do, and what tasks we need to accomplish.  The next step is thinking about hardware.  We need to make the following decisions about our hardware:

What I/O pin will I use for the toggle switch?
What I/O pins will I use for the LEDs?
Can I drive the LEDs directly from the PIC, or will that exceed the voltage/current/power maximums of the device?

To make these decisions we need to reference the device datasheet from Microchip. We will decide what I/O pins to use first.  Figure 3 shows a section of the I/O PORTS chapter (Section 5.0) of the datasheet.  The PIC16F628A device has two I/O port registers designated PORTA and PORTB whose data directions (input or output) are controlled by the TRISA and TRISB registers.  Reading through the I/O PORTS section of the datasheet you will learn that PORTA pin 5 (RA5) is an input only pin, meaning that you cannot output data to it.  We will select this pin as our toggle switch input.  We also learn that PORTB is an 8-bit wide bidirectional port, meaning that PORTB has 8 I/O pins, all of which can be input or output.  We will select RB0 through RB5 for our 6 LEDs.

Figure 3.

We also need to look at the ELECTRICAL SPECIFICATIONS section 17.0 of the datasheet.  Since we have been using LEDs which have a roughly 20mA forward current, we will need to know if the I/O pins can handle it without damage to the device.  Also, since we will have two LEDs on at the same time, we need to know if the PIC can handle upwards of 40mA combined on the I/Os.  The maximum ratings show us that each I/O pin can handle 25mA, and the total current by PORTA and PORTB pins combined is 200mA.  We will be fine driving our LEDs directly from the PIC.

PROJECT SCHEMATIC & HARDWARE

We now have our pins selected.  The PIC16F628A operates at voltages between 3.0 to 5.5V, and as such we will continue to use a 5V linear voltage regulator to supply the device's power.  Our switch for selecting night mode will take pin RA5 high (supply 5V).  Since RA5 will be an input, it is a good idea to tie it to ground with a 10K Ohm resistor.  We will continue to use 220 Ohm 1/4 W resistors for the LEDs, which are tied to ground from the I/O pins through the resistors, into the LEDs and then to ground.  Figure 4 shows our project schematic.

Figure 4.
THE PROGRAM

Since we are using a new PIC microcontroller, I have created a new project in MPLab X.  You may do the same if you like, just be sure to select the PIC16F628A microcontroller as the project device.  The full program is shown in Figure 5.

Figure 5.
Lines 1-20 are a large block of comments that explain what the program is for, and for what microcontroller.

22-24 show an example of sectioning my program so that the code is easier to read.  You will note this format in several locations within the code.

Line 25 shows the use of a preprocessor directive.  This #define simply states the version number of this program.  I had written this program before but made changes to it for this blog, therefore I updated the version to 2.0.  This can be helpful for the programmer for a number of reasons, including if more than 1 person is writing the program, than each user can sequentially update the version number to help keep track of changes.

Continuing with helpful things for the programmer, lines 28-31 show the use of a #if 0 and #endif block.  An if statement only gets executed if the result is true, even in preprocessor directives like #if.  Using #if 0 is a good way to write a large section of comments, or keep a section for updates.  Here I have used the #if 0 for keeping track of known issues, or ideas for revisions.  You can see that I have made a note to maybe try a yellow light cycle for 4 seconds rather than 3.  In a large program, this is a great way to keep track of code bugs and things that you know need updated or fixed.  The #if 0 must always be closed with a #endif.

Line 34 includes the xc.h header file.

Lines 39 and 40 set the configuration word options for the PIC16F628A.  These options can be found in section 14.1 of the device datasheet, or in the chips html file in the xc8 folder of your PC (xc8> docs> chips).  The configuration word bits are set (1) by default, so you must unset (0) them manually if you desire. For those bits that you wish to leave set, there is no need to include them in the #pragma config.

On line 43, we define the oscillator frequency using _XTAL_FREQ.

Lines 44 through 46 show how we can design our own compiler error messages.  If we change our oscillator frequency for some reason to something other than 4MHz, this "#if (_XTAL_FREQ != 4000000)", which means if XTAL_FREQ is NOT EQUAL TO 4000000, will generate an error when we try to compile the program.  In this case, the error is whatever is in the quotation marks following the #error preprocessor directive.  I have asked the compiler to tell me to "check XTAL_FREQ, check timing operations" if the oscillator frequency isn't 4MHz.  This #if is then closed with a #endif.

Lines 50 and 51 define bit states.  I have defined 1 as TRUE and 0 as FALSE.  The preprocessor directive #define TRUE 1 for instance, is used to replace every instance of "TRUE" with "1".  This allows me to write my code in more of a readable English.

Now we get into mapping our Input/Outputs.  First, we tell the program what I/O pin our switch is connected to.  Line 57, which says #define NightMode_Input PORTAbits.RA5 does just that.  PORTAbits.RA5 points the compiler to PORTA, bit 5, and we have named this bit "NightMode_Input".  Later in the program when I type "NightMode_Input", the compiler automatically knows I'm referring to bit 5 of Port A.

We see an example of that in the very next line of code, line 58.  I am defining a bit state here that I have named "NightMode_ON".  NightMode_ON defines that NightMode_Input is TRUE, meaning that bit 5 of Port A is equal to 1.  Now, throughout the rest of the program, if I type "NightMode_ON", the compiler knows I'm saying that bit RA5 is equal to 1.

Line 59 defines NightMode_OFF as bit RA5 being equal to 0.

Lines 62 through 67 define the bit masks for all of our LEDs.  Since we have used bits 0 through 5 of port B for our LEDs, we can mask each one and assign a name to the mask which makes writing the program easier.  For example, the red light of our main road is bit 0, thus our mask for this LED is named "main_REDmask", and bit 0 is given a value of 1 for our bitwise operators later in the program.

Lines 70 through 73 define the initial state of our registers for program start. TRISA, which defines the data direction of port A is set with all bits being outputs except for our switch on bit 5, which is an input.  TRISB is all outputs.  PORTA and PORTB are both cleared for program startup.

Line 78 declares our global variable "PORTBimg" for our shadow register using the 8-bit unsigned char data type.

We now enter the section of code where are functions are declared.  Remember that functions must be declared (defined) before we call them in the program, otherwise the compiler doesn't know what we want and will produce an error. Our lesson on functions talked about passing parameters to and from the functions, and that when we have no parameters to pass the function format is void function_name (void).  This program passes no parameters, so this is the format we use.

The first function on line 84 is "doBOTH_RED_ON".  When we learned about functions, we were turning only one LED on.  We are now dealing with multiple LED combinations to cycle our traffic light, and the doBOTH_RED_ON function turns on the red LEDs for the main road, and the cross road.  Using bitwise operators and the LED masks, the compiler first performs the operation within the parenthesis just like in mathematical equations, then performs the operation outside the parenthesis with the result.  Since we want both red lights on, the first thing that occurs in this function is that both red LED masks are bitwise ORed together inside the parenthesis.  A bitwise OR operation produces a true (1) result when one OR the other bit is 1.  Since both masks have a 1, the result of this OR operation produces an 8-bit result with two bits true (00001001).  Next, the compiler takes this result and ORs it with PORTBimg, which is 00000000.  At the same time, since we have also used the equal operator, PORTBimg is made equal to the result of our OR operation, and PORTBimg now equals 00001001.  The next line of our function copies PORTBimg to the PORTB register, which turns on both of the red LEDs.  Here is a visualization on how this whole operation looks:

main_REDmask is 00000001
cross_REDmask is 00001000

The function says PORTBimg |= (main_REDmask | cross_REDmask)
In binary, this looks like:
00000000 |= (00000001 | 00001000)

The compiler does the OR in parenthesis first.  We now have 00000000 |= 00001001.  This operation makes PORTBimg equal to this result, which is 00001001, and this is then copied to the PORTB register.

The remaining combinations for our traffic signal are represented in the rest of the functions.  To turn off the LEDs, a function was created which bitwise ANDs the PORTB shadow register with the initialization state of PORTB.  PORTBinit is all zeros, so no matter what PORTBimg is currently equal to, ANDing the two together produces a result which is all zeros.  This result is then copied to the PORTB register turning off all the LEDs.

Line 119 is a delay function which is used to generate our long multi-second delays.  This function is named "delayS" to represent delay seconds.  Its format is void delayS (unsigned char S).  It does have a parameter passed into it, which is a local unsigned char variable named S.  S represents the number of seconds we want to delay.  The function is called with a value for S in the program, such as delayS(5);.  The value 5 gets passed into the function, which then creates a for loop.  This loop says that for S, while S is greater than 0, decrement S.  Then a one-second delay macro is used.  If we tried to delay in the program by writing __delay_ms(5000), which would represent a 5 second delay, the compiler would likely generate an error that the 5000 argument is too large.  These macros tend to have size limits, and we would definitely not be able to use this delay macro for a 20 second delay.  Creating a function like this one gets us around those limits by keeping the argument to a small 1 second.  What happens in this function is that the argument "5" in the function call is represented by the variable S.  Then the for loop says for (5; 5>0; 5--), then __delay_ms(1000);.  The function says "okay, 5 is greater than 0, so I will delay 1 second and then subtract 1 from 5."  When this loop has completed 5 times, S is no longer greater than 0 because 0>0 is not true, so the loop stops and the function is exited.

This completes our function declarations, and we now enter into the main program on line 129.  Lines 132 through 136 use our previously defined initialization values to set data direction for TRISA and TRISB, and clear the port registers and shadow register.

We then enter the main while (1) program loop on line 141.  The program loop starts by checking the status of the NightMode_Input.  The program makes a decision on which loop to execute based on whether NightMode is on or off.  If the night mode switch is on, the if loop on line 144 is executed, which flashes the red lights on and off for 500mS each.  If the night mode switch is off, the if loop on line 153 is executed, which follows a normal traffic light operating pattern.  If at any time during program operation the night mode switch is changed, the program will fully complete the current loop before evaluating the switch again and changing loops.  This ensures that modes are changed in a safe and orderly fashion without ever displaying an unsafe signal.  Imagine approaching a traffic signal which is showing a green light, and suddenly without warning it starts flashing red!  This signal program avoids issues like that.

This program demonstrates how the actual program can be comprised almost entirely of function calls.  Functions are very useful.  I really hope this lesson helped you learn a few things.  Thanks for reading.

Sunday, December 14, 2014

Making Decisions with if/else

We have seen a number of ways in which blocks of code can be executed.  What if however, we only wanted to execute a block of code if a certain condition within the program existed?  We can do that using the "if" statement.

USING "IF"

In an earlier lesson, New Concepts, The if statement, and Aliasing, we wrote a program that turned on a LED if our button was pressed.  This was accomplished using the following:

if(button == 1)
     led = 1;
led = 0;

That code says that if the button is equal to 1 (on), then turn on the LED, otherwise turn the LED off.  This is rudimentary code, but it sets us up for more complex examples of making decisions within our programs.  What if we had two buttons, and we needed different code to be executed depending on which button was pressed?  What if we had a variable that depending on the value, different code would be executed?  "if" allows us to do these kinds of things.

2 Buttons:

if (button1 == 1)
    {
        do these instructions;
    }
if (button2 == 1)
    {
        do these instructions;
    }

Variable:

if(variable <= 10)
    {
        do these instructions;
    }
if(variable > 10)
    {
        do these instructions;
    }

These examples show how we can use the if statement to execute or not execute some instructions.  As a reminder from the previous lesson, we only need to include the braces around our instructions if there is more than one instruction, otherwise we can simply write our one instruction without the braces.  Example:

if(this condition is true)
    do this one instruction;

The if statement tests the operator for a true/false state and only continues with execution if the result is true. If the result is false, the instruction(s) is/are skipped.

IF/ELSE

Now let's ask ourselves this question.  If I have two buttons, and I want some instruction to execute if neither button is pressed, how can I do this?  C gives us the "else" statement, which is basically a default.  Button 1 is not pressed, and button 2 is not pressed, so this default code block will execute.  Here is how that looks:

if(button1 == 1)
    {
        do these instructions;
    }
if(button2 == 1)
    {
        do these instructions;
    }
else
    {
        do these instructions;
    }

You can have pretty much as many of these options as you need to:

if(condition)
    instruction
else
    instruction

or

if(condition1)
    instruction
if(condition2)
    instruction
if(condition3, 4, 5...)
    instruction
else
    instruction

SIMPLE DECISION MAKING IN AN EXAMPLE

I have written a simple program to show decision making based on the condition of a variable.  There are much better examples, but we have not yet covered some concepts that are required in order to fully implement those examples.  In order to keep things simple and maximize understanding, I chose to use an example that operates without user input (i.e. runs on its own without any input from outside sources like buttons).

Figure 1.
 The full program is listed in Figure 1.  Our variable "count" is declared on line 16. Line 17 declares our shadow register variable for the GPIO register.
We have defined the initialization state of GPIO and TRISIO on 20 and 21 respectively.
GPIO ports 0, 1, and 2 have been defined as our LED masks on lines 24 through 26, and for our delay routines we indicate the clock frequency on line 29.
We then declare our functions, which in this case will only be to turn on each LED. This program doesn't require a function to turn them off, because we will simply clear the port register within the program.
Line 51 begins the main program, and we begin here by setting the TRISIO bits to outputs, and clearing the GPIO shadow register, then copying the shadow register to the GPIO register.  We also set the value of our count variable to zero.
Line 58 begins the main while loop.  Within this loop, the program makes a decision about which "if" block to execute based on the value of "count".  Since we set count to zero at the beginning of the program, the first if statement, which asks "if count is greater than 2, make count equal 0 is skipped.  Now, the if count equals 0 block is executed because "count" is equal to zero.
This if block, beginning on line 63 will clear the shadow register and the GPIO register, which would turn off any LEDs that might be currently on.  Then the red LED is turned on using a function call.  The program then delays 500ms before incrementing the count variable.
Count now equals 1.  The program flow at this point returns to the top of the while loop.  Count is now equal to 1, so the if (count == 0) block is skipped, and instead, the count == 1 block is executed.
This block, beginning on line 72, turns off any LEDs that are on, then turns on the blue LED.  The sequence completes the same as the red sequence.
Count is now equal to 2, thus, the two "if" blocks are skipped.  Since both if blocks are false, the default sequence "else" is executed, which again clears the shadow and port registers.  The default state is to turn on the white LED, which this if block does. Count is again incremented and is thus equal to 3.
Now when the program returns to the top of the while loop, count is greater than 2, so the if (count > 2) instruction gets executed, which makes count equal to zero again.  Note that there are no braces around this instruction since there is only one instruction.

We have had some hardware changes for this example.  To follow along, you will need:
1 PIC12F629 microcontroller
1 LM7805 linear voltage regulator
1 0.1uF ceramic capacitor
3 220 Ohm 1/4W resistors
1 9V battery or other power source
1 each red, blue, and white (or whatever colors you may have) LEDs with a forward current of about 20mA.
Various jumper wires
1 breadboard

A schematic and breadboard layout for your reference are included below in Figures 2 and 3.

Figure 2.
Figure 3.

Sunday, December 7, 2014

Logical Operators, and Relational Operators

We have discussed bitwise operators and how they are used to change the status of bits.  Logical and Relational operators are all binary operators in that they operate on two operands and return a result, but unlike bitwise operators these deal entirely in comparing the two operands to return a true or false result.

LOGICAL OPERATORS

Figure 1.
The logical operators are shown in figure 1.  These can be confusing to beginners because their names are the same as two of the bitwise operators, and even the symbols are similar.  Looking at examples makes their differences easier.  A logical AND operation evaluates two operands and says "If operand 1 AND operand two are true, the result is true, otherwise the result is false.  Both operands must be true to return a true result.  Here are some examples.  We have two variables that we are comparing, A and B.
A = 2, B = 5
(A && B) returns a true result, because both A and B are non-zero.  If B = 0, then (A && B) returns false because both operands are not true.
We can use this logical operator to influence program flow by asking the program to execute or not execute some instruction based on the condition of two operands.  For instance, if A is greater than zero AND B is greater than zero, perform this task.

A logical OR performs in much the same way except that it says if A OR B is true, the result is true.  Both operands do not need to be true to return true, only one of them does.  However, if neither are true, the result is false.
A = 2, B = 0:  A || B equals true
A = 0, B = 1: A || B equals true
A = 0, B = 0: A || B equals false

RELATIONAL OPERATORS

We have seen relational operators already in these lessons, but it's time we define them.  Figure 2 shows the relational operators, which compare the relationship of two operands and produces a true or false result based on the relational operator being used.
Figure 2.
I'm sure many of these operators look familiar to you since they are almost all used in everyday math, greater than and less than, for instance.  When we are using relational operators, we are comparing the two operands based on their relationship to each other.  Is X greater than Y?  Is Y equal to X?

Two of the relational operators that you may not be familiar with are "is equal to" and "is not equal to".  Notice how the relational operator "is equal to" uses two equal signs ==, it is dangerously close to the operator "make equal to", which uses only one equals sign.  Here is the difference:
A = B changes the value of A to equal the value of B. If B were equal to 5, this operator would make A equal to 5.
A == B produces a true or false result depending on whether A and B have the same value. If A = 2 and B = 5, then A == B is false because A is not equal to B.  If A = 1 and B = 1, then A == B is true.

Is not equal to (!=) generates a true or false result based on whether the operands are different. If A = 2, B = 5, then A != B returns true because A is not equal to B.

EXAMPLE

Let's look at a couple examples to understand these operators better.  These examples will use what is called "pseudo code" meaning that it's not legitimate code, but more like paraphrasing the code to get the point across.

Assume we have a button connected to a pin, and a LED connected.  Let's use a variable "count" to count up from 0 to 10.

unsigned char count = 0;   //this operator makes "count" equal to 0


while (1)
    if (button && count)
         turn on LED;
    count ++;

  if (count >= 10)
    count = 0;

This program would turn the LED on only if the button is pressed (making the button pin a non-zero) and count is not equal to 0.  If the button were held down, the LED would only be off when the count variable passes through 0. Then count gets incremented using count ++. Then we use a relational operator so that the value of count cannot exceed a value of 10. This is accomplished by saying "if count is greater than or equal to 10, make count equal to 0".  As long as count is less than 10, the count = 0 instruction gets skipped.

If we change the logical operator AND (&&) to OR (||):

if (button || count)

Then the LED is only off if the button is not pressed and count is equal to 0, because we are saying if button is true OR count is true.  If the button was continuously held in this example, the LED would never turn off because button is always true.

We will see logical and relational operators used in practical examples soon.

Tuesday, December 2, 2014

Creating and Using Functions

All C programs run on functions, which are blocks of instructions used to execute a sequence of events. main ( ) is a C program's main function (hence the name), and is included in every program.  Other examples of typical functions include while (1) and for (xxxx), among others.  Every C program has at least one function, but most programs are divided up into several functions.  The division of these functions is up to the programmer, but logically they would be divided so that each function performs a specific task.   As the programmer, you can create your own functions for specific tasks aside from the typical functions which can be called throughout your program.

BASIC FUNCTION STRUCTURE

When creating your own functions you must first define the function ahead of time so that the compiler knows what we want when we call the function.  Think of this as learning a new language. If someone says a word you don't know, you won't understand the meaning.  The compiler reacts the same way.  Defining the function ahead of time is called a function declaration, which tells the compiler about the function's name, return type, and parameters.  The function declaration also provides the actual body of the function, or list of instructions the function performs.  The general form of a function is as follows:

return_type function_name (parameter list)
{
body of function
}

Return Type: A function may return a value, and the return_type specifies the data type to be returned such as "char" or "long".  Many functions return no value, so the return type would be declared as "void" to specify what basically equates to "expect nothing".
Function Name:  This is the actual name we give to our function, just like naming a variable.  The name of the function is what we use throughout the program when we want to call the function.
Parameters:  A function may have values passed into it.  An example of this would be if the function uses a variable, then we can place a global variable, or define a local variable within the parameter list.  You may pass more than one parameter to a function by separating them with commas, such as function_name (variable1, variable2).  Also, just like return type, many functions require no parameters and we would again use the C keyword "void" within the parenthesis.
Function Body:  This is where the function's instructions reside. This is what defines what the function actually does.

The macro __delay_ms(x) is an example of a function.  To see the body of this function, we must look at a header file.  Remember how our programs include the xc.h file?  This points the compiler to the xc.h header file which includes a bunch of definitions, functions, and macros the compiler needs in order to generate the program code for common and basic tasks.  Figure 1 shows the contents of the xc.h file.  These files open with the window's notepad program and are located in the program files> microchip> xc8> version#> include directory.
Figure 1.
The contents of the xc.h header file basically define the file as _XC_H_, and say that if XC8 is our compiler (which it is) then include the "htc.h" header file.  Basically, this file does nothing more than point to another header file.  What is the point then, you might ask? The xc.h file is a top-level file that points the compiler to everything else it needs without the programmer (you and me) having to include it directly.

The htc.h header file defines some common definitions and some debugger stuff, but most importantly it includes another header file depending on what compiler we're using.  Microchip used to have a C compiler known as PicC, and for some reason still refer to XC8 as PICC within this header file. We can see that the htc.h header file points us next to the pic.h header file, since it says #if defined(__PICC__) #include <pic.h>

The pic.h header file is where a lot of definitions and macros reside, as well as some important functions.  Let's look at the section that includes the __delay routines (Figure 2).


Figure 2.
 #ifdef __PICC__ states that if XC8 (PICC) is defined as our compiler, include the following statements.
Look at the comment "NOTE: To use the macros below, YOU must have previously defined _XTAL_FREQ".  See why we have been including that definition?
#define is where these macros are defined, and we can see the use of parameters here.  The macro __delay_us(x) is defined with the name "__delay_us" and includes a parameter being passed to the function.  The parameter in this case is "x".  The instruction that is executed in place of "__delay_us(x)" then follows, which is "_delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0))).  This is an example also of the compiler doing math.

The compiler accepts the instruction "_delay(x)" which is a delay for x number of instruction cycles.  The microcontroller performs one instruction for every 4 clock cycles (see the blog post about The Microcontroller Oscillator).  One clock cycle is our oscillator frequency, so one instruction is executed every (frequency/4).  One microsecond, denoted "us" is equal to 0.000001 seconds, or one one-millionth of a second.  Therefore, the statement "_delay((unsigned long)((x)*(XTAL_FREQ/4000000.0))) says that delay(for this many clock cycles) where the parameter is a mathematical formula.  If we ask the compiler to delay for 40 microseconds by writing the statement "__delay_us(40)" using a 4MHz crystal frequency, the compiler does the following things:
Replaces XTAL_FREQ with our defined number, which is 4000000 Hertz.
Replaces x with our requested 40.
Defines an unsigned long integer to hold the result.
Divides XTAL_FREQ (4000000) by 4000000.0, where the result in this case is 1.
Multiples x (40) by the previous result, which results in 40.
The unsigned long integer becomes 40.
The microcontroller delays for 40 instruction cycles, where each cycle is equal to 1 microsecond.

When we write the statement "__delay_ms(100)" we are asking the PIC to delay for 100 milliseconds. One millisecond is equal to 0.001 seconds.  Using the same XTAL_FREQ, the math that occurs is:
((100)*(4000000/4000.0))
(100)*(1000)
100,000
Thus the PIC delays for 100,000 instruction cycles resulting in a delay of 100 milliseconds.  This math can be checked in reverse since we know that 1 instruction is executed every 1/1000000th of a second.  If we are delayed by 100,000 instruction cycles and 1 instruction is executed every 1,000,000th of a second:
100,000*0.000001 = 0.1 seconds, or 100 milliseconds.

These delay macros are functions, and they are defined externally meaning the definitions don't reside within our own program file (the learn_c.c file that we write our program in).  Rather, these macros are defined externally in the pic.h header file, which is indirectly included in our program by the preprocessor directive #include <xc.h>.

MAKING OUR OWN FUNCTION

In our previous program we used bitwise operators to manipulate the led mask bit of our GPIO shadow register, then copied the shadow register to the GPIO port, which turned our LED on and off.  We can create functions to do this.  The ultimate goal of these instructions is to get the LED to turn on, and then to turn it back off, so let's create a function for each task.

We will name the function that turns the LED on "doLED_ON", and the function that turns the LED off "doLED_OFF".  We don't need to pass any parameters to the functions, nor will they return any results.  Let's do the LED_ON function first.

Let's declare our function, which is return_type function_name(parameter list). Our return type is void, our function name is "doLED_ON" and our parameter list is (void), thus we have:

void doLED_ON(void)

Now we need a function body, which is a list of instructions enclosed in braces { }.  To turn the LED connected to GP0 on, we ORed GPIOimg with the LEDmask and then copied GPIOimg to the GPIO register:

{
GPIOimg |= LEDmask;
GPIO = GPIOimg;
}

Putting this all together completes our function declaration.

void doLED_ON(void)
{
GPIOimg |= LEDmask;
GPIO = GPIOimg;
}

Doing the same thing for the LED_OFF function results in:

void doLED_OFF(void)
{
GPIOimg ^= LEDmask;
GPIO = GPIOimg;
}

We will want to dedicate a section in our program code to these function declarations.  This is shown in Figure 3.  I call these declarations "Shadowed I/O Routines" since the functions perform manipulations in the Input/Output bits via a shadow register.

Figure 3.
CALLING A FUNCTION

Now when we want to turn the LED on or off, we can simply call these functions to do that task.  If we had a long program that at various points turned on or off the LED, it is much simpler to have these functions and then simply call them throughout the program than to rewrite the instructions within the function over and over again.  When the program calls the function, the program flow jumps to the function declaration where the list of instructions there are executed.  At the end of the function, denoted by the closing brace "}" the program flow returns to where the function was called and continues on with normal program flow.  Figure 4 shows where the functions are called in our main program.

Figure 4.
Within our for function, the doLED_ON( ) and doLED_OFF( ) functions are called.  Calling a function is a statement and must be followed with a semicolon.  Additionally, since we are passing no arguments to the function when we call it, we must use empty parenthesis.  Let's look now at our full program, which is shown in figure 5.  You can update your program and flash it to your PIC. Program operation is exactly the same as our previous program, since our functions perform the same tasks that were replaced.  Again, all of our program hardware is unchanged so there is no need to change the breadboard.

Figure 5.
Global variables are defined and set, and our I/O map defines the initialization state of the TRISIO and GPIO registers, as well as defining our LEDmask bit location.
XTAL_FREQ is defined for our delay routines.
Our functions are then declared, and the instructions they perform are stated within the function bodies.
Our main program then starts by setting data direction in the TRISIO register (are the I/O bits input or output), clearing the GPIOimg variable (making all 8 bits zero), and clearing the GPIO register.
The endlessly repeating while(1) loop then begins, which contains a for function that defines a local variable "flashes" and makes flashes equal to the variable "count". For as long as flashes is greater than 0, this loop repeats and after each repetition, "flashes" is decremented (variable minus 1).
Within the body of for, the doLED_ON( ) function is called, which takes program flow to the function declaration. The instructions there are then executed, which ORs GPIOimg with the LEDmask and makes GPIOimg equal to the result. That result is then copied to the GPIO register. This function is now complete, and program flow returns to the for loop where we left off.
The program is now delayed by 100 milliseconds.
Now the function doLED_OFF( ) is called, which again breaks program flow and executes the instructions in the doLED_OFF function before returning to another 100 millisecond delay.
At this point "flashes" is decremented.  If flashes is still greater than 0, this loop is repeated until flashes is not greater than 0.
When that happens, the program delays for 500 milliseconds, then adds 1 to the variable "count".  Program flow now returns to the beginning of while(1).

This concludes the example of creating and using your own functions within your program.  What if however, you find that you repeatedly use the same functions in many of your programs?  Is there an easier way to include the function declarations in multiple programs without having to write them out in each one?  Yes there is!  We can create an external file for the functions and then just #include that file in each program.

CREATING AND USING EXTERNAL FUNCTION FILES

Let's create a new file in MPLAB X by clicking the new file button (Figure 6.) or by clicking on File> New File.
Figure 6.
Under "Project" make sure that our learn_c project is selected.
Under "categories" select "C" and then within the "file types" window select "C Header File".  This file type will include #define macros to prevent multiple inclusion, and contain external c statements so that it can also be included by C++ files (a feature we will not use on this blog).
Select the "next" button.
Name your file, I will name mine "functest", and be sure that the extension drop-down menu has "h" selected.
Both "project" and "location" should show "learn_c".
Note the "Created File" location or choose your desired location for this file
Now click on "finish".

You should now see a blank header file (Figure 7.)  You will see the macros and external c statements discussed just above for C++.

Figure 7.
After the last #endif we can start writing our functions.  This would be on line 23 in Figure 7.

We can copy our do_LED_ON and doLED_OFF functions to our functest.h file.  Now, something very important to remember: the functions include instructions that reference both the LEDmask and the GPIOimg declarations, but as far as functest.h is concerned, it doesn't know what these are.  Therefore, we will need to also include them.  The trade here is that since we have them defined in the external file, we don't need to define them again in our main c source file.  You'll see this in just a moment.

Figure 8.
Figure 8 shows the completed functest.h file starting on line 23. Reference Figure 7 for lines 1 through 22, as they are unchanged in the completed file.  You can see I have defined the LEDmask the same as in the original program, and I have defined the GPIOimg variable.  Following this is an exact copy of the function declarations from Figure 3 above.  With the functest.h window open, select File> Save (if save is greyed out, the file is already saved).  Now let's return to our learn_c.c file where our program is.
Remove the "Shadowed I/O Routines" section, since these are now in the functest.h header file.  We can also remove #define LEDmask 0b00000001 and unsigned char GPIOimg; since these definitions are also included in the header file.  Super Tip: to remove lines of code for testing purposes, or code you intend to put back, just "comment out" the code rather than erasing it. Then all you have to do is remove the comment markers and your code is as good as new.  We must now include the new header file in our code. Under the #define <xc.h> let's add #define "functest.h".  You might wonder why xc.h is in carrots and functest.h is in quotation marks.  The answer is simple, <carrots> are for system files, and "quotes" are for user files.

Figure 9 shows the main c source file with the functest.h file included, and the irrelevant code commented out.  In the Projects Window of MPLAB X, right click on the "Header Files" under the "learn_c" project and click "Add Existing Item". Find and select functest.h so that the file is included in your project hierarchy.  Clicking the "Build Project" icon in MPLAB X should successfully compile your project using the external functions, resulting in a "build successful" message.
Figure 9.
Now you know how to use your own functions in a program, and how to include them externally if you want to use them across many programs.  I hope this lesson was useful to you. Questions and comments are always welcome.  Thank you for reading!

Sunday, November 30, 2014

Bitwise Operators, and the Conclusion of Shadow Registers

The purpose of using a shadow register is to avoid directly accessing an input/output port, since doing so can cause stability issues with the program.  We use a shadow register to copy a desired value for a bit or bits to the I/O register.  This however, is a byte oriented process, meaning that our shadow register is a representation of the entire 8-bit I/O register.  How then, would we manipulate individual bits of a register without accessing them directly?  With Bitwise Operators, and a shadow register.

BITWISE OPERATORS

What is a bitwise operator?  Bitwise operators are used to manipulate one or more bits of operands. Our variables and port registers are operands, and things like +, -, and = are operators.  Here is an example of using an operator:

00010101 + 10110110 = 11001011

In this example, we have taken two bytes and added them together using the "plus" operator to produce a result.  Let's think about our last project, where we flashed a LED on and off by manipulating a single port register bit (GP0).  How can we change the state of only GP0 on the port register without accessing it directly?  Using a bitwise operator would do the trick.

Figure 1.
The bitwise operator compares the input of two bytes, makes manipulations one bit at a time, and outputs a single byte with the result of the comparisons.  These comparisons are made based on the state of each bit, meaning whether the bit is true (1) or false (0).  This allows the programmer to easily change the state of individual bits while still performing the operations using entire bytes.  The comparisons of these bit-wise true/false states are represented using what are called Truth Tables (figure 2.).  A bitwise operation looks at the true/false relationship between two bits at the same position (i.e. the 2nd bit of each variable), and produces a true/false result based on what bitwise operator was used.  Let's look at the truth tables now, and see how each bitwise operator works.

Figure 2.
With the exception of "NOT", each bitwise operator compares two inputs and produces one output.

AND

The bitwise operator AND, represented by the ampersand symbol "&" compares the two inputs and says that if input 1 AND input 2 are true, the output is true.  Let's look at an example where we have two variables, A and B.

A is equal to 10011010
B is equal to 00110111       If we perform a bitwise AND operation on these two variables:
AND result    00010010

Both bits must be true for the result to be true, otherwise the result is false.  Here is how this looks in the C language:

A & B = result

OR

The bitwise operator OR, represented by the vertical line symbol "|" or sometimes called a pipe, says that if bit 1 OR bit 2 are true, the result is true.  If neither bit is true, the result is false.

10011010     A
00110111     B
10111111     Result

A | B = result

EXCLUSIVE OR

The bitwise operator XOR (Exclusive OR) represented by the carrot symbol "^" says that if either A OR B but not both is true, the result is true, otherwise the result is false.  This is the same as saying the output is on if the inputs are different.

10011010     A
00110111     B
10101101     Result

A ^ B = result

NOT

Let's look at the bitwise operator NOT real quick, because the last two operators that follow are combinations of NOT and other bitwise operators.  NOT, represented by a tilde "~" is nothing more than an inverse and does not compare.  It simply makes a true a false, and a false a true, and has only one input and one output.

10011010     A
01100101     NOT A

~A = result

NOT AND

The bitwise operator NAND is a combination of NOT and AND "&~".  It says if, and only if both inputs are true, the output will be false, otherwise the result is true.  It is the direct inverse of a regular AND operation.  The result is thus the inverse of AND.

10011010     A
00110111     B
11101101     Result

A &~ B = result

NOT OR

Finally, the bitwise operator NOR (NOT OR) "|~" is a combination of NOT and OR, which is the direct inverse of the OR operation.  It says that if any input is true, the output is false, thus producing a result which is the inverse to a regular OR.

10011010     A
00110111     B
01000000     Result

A |~ B = result

USING BITWISE OPERATORS

Using bitwise operators is the easiest way to compare every bit across two bytes.  We can make a change to just one bit of a port without changing the other bits and without directly accessing the bit, such as in GP0=1.  Let's look at some ways we can do this using the bitwise operators.

All of our projects to this point have involved turning on and off a LED using the output of one of our I/O pins.  We know ahead of time what the state of all the GPIO pins will be when we're working with a simple project like the LED, as we have cleared all the pins at the beginning of our program (made them all 0).  So GPIO will equal 00000000.  When we want to turn on the LED we need to make GP0 equal to 1, and up to this point we have been accomplishing this task by writing GP0 = 1. But we know now that directly accessing the port pins is not advised, so we need to do the same thing using a bitwise operator.

A very common practice in programming is using what is called a mask, where we define the position of a bit we want to manipulate.  A mask for our LED connected to port 0 would look like this: #define LEDmask 0b00000001.  The # define preprocessor macro tells the compiler that every time it sees the label "LEDmask", it is replaced with the binary value 00000001 (remembering that binary values are preceded by "0b").

We now have two operands, the GPIO port register "00000000" and the LEDmask "00000001".  If we want to perform a bitwise operation on these two operands to make GP0 turn on, how could we do that?  Let's perform a bitwise OR operation!

GPIO 00000000
mask 00000001
result 00000001

Each bit has been compared with "if input 1 OR input 2 is true, result is true", and the result is that bit 0 is true.  In order to use this result to make GPIO equal to the result, we will combine operators and tell the program, perform this OR operation and make GPIO equal to the result.

GPIO |= LEDmask;

Now the OR operation has been performed and the result has been copied to the GPIO register.

What if we don't know the state of the GPIO pins, except for the bit we want to change? Our LED on pin GP0 is off, but the other pins are performing other tasks and could be 1 or 0 at any given time, will this mask still work?

GPIO 10011010
mask 00000001
result 10011011

Yes! None of the other GPIO pins have been changed from their original state except for the bit we wanted to change.  How cool!

Now we want to turn off the LED, how can we do that?  We return now to knowing the state of our pins, GPIO is equal to 00000001 and our led mask is equal to 00000001.  What bitwise operator will change GPIO to 00000000?  We know that if we have 0 and 0, we want the result to be 0, and if we have 1 and 1, we want that result to be 0 as well.  We definitely don't want any result to be 1 because we don't want to turn on any pins.  What truth table matches up to what we want to do?  Exclusive OR (XOR) looks good.

GPIO 00000001
mask 00000001
result 00000000

That worked out good!  Now what if our pins are unknown state, will XOR still work?

GPIO 10011011
mask 00000001
result 10011010

That worked too!  Because the XOR operator produces a false result when both pins are the same (0 or 1) but a true result when they're different, all pins will stay in the same state except the one our mask indicates.  Pretty neat huh?

Let's look at one more way to turn off the LED using bitwise operators.  What if we inverted the LED mask using the NOT operator. Then 00000001 becomes 11111110.  Now if we AND together GPIO with GP0 turned on, and the inverted mask:

GPIO 00000001
mask 11111110
result 00000000

GP0 is now cleared and thus turned off.  The code that would fully perform this function looks like this:

GPIO &= ~LEDmask;

This says GPIO ANDED together with the inverse of LEDmask and made equal to the result.  All of these bitwise operators have a use, and the more you write programs in C and want to manipulate bits, the more useful you will find them.

So far in our examples we have still been directly manipulating the GPIO port register.  Even writing GPIO &= LEDmask; is directly manipulating the bits of the GPIO register because the operation is performed one bit at a time.  There is one more thing we must do to avoid doing this, and it will conclude our discussion on shadow registers.  We have already seen the use of a variable named GPIOinit, which is a copy of the GPIO port register to define the initialization state.  To fully avoid direct manipulation of the register, we must declare another GPIO variable that we will make all of our changes to. I like to use GPIOimg as my variable name, which indicates that the variable is an image of GPIO but you could easily name this variable something like GPIO_copy.  It should be an 8-bit variable, so the unsigned char type would work great.

Now when we use the bitwise operators, we perform them on the variable instead of the actual register so that the bit-by-bit operation is not done on the actual register.  Then we copy the variable to the actual register which takes place as a byte-size operation. Here is how that looks:

GPIOimg &= LEDmask;
GPIO = GPIOimg;

The bitwise operation took place using the image, then the image is copied to the register.  No direct access to the GPIO bits is allowed using this setup, which completely eliminates the read/modify/write issues discussed earlier.  Our code is stable, and we are happy programmers!

PUTTING THIS ALL TOGETHER

We have learned quite a lot in this lesson and your head may be reeling, so let's put this lesson to practice and rewrite our last program using the bitwise operators.  Figure 3 shows the updated program, which flashes the LED for the number of times our count variable is equal to.  This time however, we are using bitwise operators and shadow registers.

Figure 3.
This program is all the same as the previous program except that on line 24, we have define our LED mask, and I have replaced the use of GP0=1 and GP0=0 with the bitwise operator routines.  The comments following each line explains the purpose of the line.

The large blank section in this program between lines 28 and 42 was code for the next lesson and can be ignored in this lesson, thus it was blanked out to avoid confusion.  This program will operate exactly the same as in the last lesson, the only difference being that this program is considered good, stable code.  You can copy this program into your MPLab software and try it for yourself.

I hope that at this point you are more familiar with the full use of shadow registers, and have a beginner's grasp on using bitwise operators. Please feel free to comment.

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.

Sunday, November 16, 2014

Introduction to Shadow Registers

One of my good friends in years past over at allaboutcircuits.com, RJ Oldenkamp was kind enough to help me out a lot with the code for a project I was working on.  I am very much a novice now but far much more then.  I had written my program which was 565 lines of code, thinking it was pretty good. Everything worked well but I wanted a second set of eyes on it to maybe point out some things I'd missed and give me some help on proper code writing. Well RJ got a hold of the project and after teaching me endless things I'd never even thought of, and the "proper" way to write embedded C, the same project was now 815 lines, but far more stable than I even dreamed of at the time.

All of this transpired during the first year of me writing this blog, and in fact is a part of why I had been absent from it for so long. That and making a career change twice, moving half-way across the United States, and getting settled in a whole new routine.  The point of this story is that one of the things that RJ brought to my attention was the concept of Shadow Registers.

It is very difficult sometimes when explaining microprocessor things to beginners not to get too technical and start throwing terms that might confuse them; and the point of this blog after all, is to be truly beginner friendly, so I'm really trying to keep this as simple as possible.  After looking around, the best explanation I could find comes from Michael Rigby-Jones from Nortel Networks.

When you perform nearly any operation on a register, the PIC first reads the register, then it performs the operation on the register it just read, and finally the PIC writes the result back to the register. This is known as read/modify/write.  This process is fine when dealing with normal registers and most special function registers, but when you perform a read/modify/write on a port register such as TRISIO, PORTA, PORTB, etc or some timer/counter registers, it could cause problems.

When the microprocessor reads a port register, it reads the ACTUAL state of the pins on the port rather than the output latch, which can cause two problems.  If the pin is an input, then the input pin state will be read, the operation performed on it, and the result sent to the output latch. This may not immediately cause problems, but if that pin is made into an output later on, the state of the output latch may have changed from the time it was deliberately set by the code.

If the pin is defined as an output, the output latch and the actual pin should be in the same state, though in practice sometimes they aren't.  If you are controlling a capacitive load, for example, the pin may take some time to respond as it charges and discharges the capacitor.  A common problem occurs when using the bit set (GP1=1) or bit clear (GP1=0) function directly on a port.

Example, consider two lines of code:
GP4=0
GP4=1
If pin 4 is loaded in any way, such as being connected to a capacitive load, then it may not have time to respond to the first instruction before the second one is executed.  During the second instruction, the microcontroller reads the data port pins, sees that GP4 is set low (0), and during the write portion of the read/modify/write it writes GP4 low back into the output latch.  The result would be that GP4 never gets set.

Confused?  Think of it this way. A capacitor holds a charge. If you have a closed circuit with a battery, a capacitor, and an LED connected, the LED is on.  Think of the LED as our pin and your eyes as the microcontroller.  Turn on the pin (connect the battery), what is the state of the pin?  The LED is on right?  Now turn off the pin (disconnect the battery) and immediately determine the state of the pin.  Since the capacitor is discharging through the LED, we still see the LED on even though the battery is disconnected.  We have told the pin to turn off, but our eyes (the PIC) is seeing it as on.  It's the same kind of concept, and that's the best I can do to explain it.

So how do we fix it?  I have mentioned already that it is bad practice to use read/modify/write instructions directly on a port, so you use a shadow register.  We are taking one byte of RAM and using it to mimic the data port.  We perform all operations on the RAM location and then simply copy the RAM location to the port register.  This essentially works because rather than telling the MCU to write to just one I/O pin, we are writing to the entire register (TRISIO, PORTA, etc.).  What we are doing is creating a RAM variable (the shadow register) that enables us to control an individual GPIO pin without the risk of corrupting the settings of the other GPIO pins on the register.

Let's see this in practice.  We want to turn on our LED, which is connected to GP0.  We have been doing that by writing to the individual GP0 I/O port.
GP0=1;

Now we want to use a shadow register.  When we are setting aliases we will set a variable (more on this in the next lesson) to a RAM location to mimic the entire GP register. That is, rather than setting just GP0 to something, we will also set all the other pins at the same time.
unsigned char GPIOimg;   //I've just defined a RAM location as a GPIO Image.
GPIOimg = 00000001;    //I've just set the image of GPIO to set GP0 to on
GPIO = GPIOimg;           //I've just copied the image of GPIO to the actual GPIO port.

So rather than setting the single bit of GP0, I have written the entire port.  We are getting to a point in lessons where overlap is occurring, and as such, I'm having to use examples we haven't covered yet.  All will come together in the next lesson or two and it will make a lot more sense, and we will write another full program to put the lessons to constructive use.