Wednesday, March 27, 2013

Introduction to MPLAB X, First Project

In the first blog post we downloaded and installed MPLAB X and the XC8 compiler.  We will now use these to set up our first project, using an I/O pin on the PIC12F629 to turn on a light emitting diode.  Since this is our first project, there's quite a bit to cover here, so this lesson will be a little long in order to cover a lot of "basics".

In addition to the software, you will need the following hardware:

1 Battery or Voltage Source 9-12V
1 PIC12F629 Microcontroller
1 LED with a forward current ~20-24mA
1 10K Resistor
1 Current Limiting Resistor for LED
1 0.1uF Capacitor
1 LM7805 Voltage Regulator IC
1 Normally-Open Momentary Switch
1 Breadboard
Jumper Wires

Creating a New Project

Figure 1

Upon opening MPLAB X IDE you will see the program's Start Page as shown in figure 1.  Under the Dive In section of the start page, click "Create New Project".  Alternatively, you can click File>Create New Project.  In the new project window you should see that under the categories column "microchip embedded" is selected and "standalone project" under the projects column is selected, click next.  We'll now select our device (the MCU we're using).  On the family drop down menu, select "Mid-Range 8-bit MCUs", and then "PIC12F629" under the device drop down menu.  Click next.  The next step will ask you to select a Supported Debug Header which we will leave selected to "None", then click next again.

The next step is to select a tool.  This is where we select which programmer we're going to use.  I have the PICkit 2, which I have selected for my project.  Click next.  The next screen asks us to choose a compiler and you should see the XC8 compiler listed there.  XC8 only appears in this menu after you have installed the compiler, so if you don't see it, make sure that you installed the XC8 compiler software.  After selecting XC8, click next.  Finally, we need to name our project.  Let's name our project "learn_c", and click finish.

We should now see our projects in the top-left portion of the IDE, as seen in figure 2.  This is a blank project (there are no files associated with it), so we need to add a file before we can start writing a program.
Figure 2
Click the file menu and select "New File".  Under categories, highlight "C" then under file types highlight "C Source File" and click next.  Let's name our source file the same as our project "learn_c" and set the file extension to ".c".  The .c file extension is associated with c source code files.  Click finish.  We should now have a new window within the IDE open as seen in figure 3.1.
Figure 3.1
If your .c file within the projects window is placed somewhere other than in the "Source Files" drop-down, we should drag the .c file into the Source Files, as shown in Figure 3.2.

Figure 3.2

Our First C Program

Our first program is very simple.  It will teach you the basic input/output interface by illuminating a light emitting diode when we press a button.  The LED will remain lit for as long as we're pressing the button, and will extinguish when we release the button.  The main purpose is to show how to read an input, and how to place data on an output.

The first thing we're going to want to do is plan out what I/O pins we want to use for the button and the LED.  Planning the hardware in advance is necessary for programming.  The PIC12F629 datasheet contains the pin diagram for the MCU as seen in figure 4.
Figure 4
Each pin of the MCU and the associated functions available on those pins are described in the datasheet's pinout description section.  As a brief rundown though, VDD is +5V, VSS is ground, and pins 2 through 7 are the General Purpose I/O pins.  There are additional functions which reside on these pins, such as analog to digital converters and comparator modules, but we're only interested in the I/O function right now.

Reading through the datasheet, we discover that the GP3 pin is an input only port, which means that we cannot use it to output data.  Let's use GP3 (pin 4) for our button input.  We'll select GP0 (pin 7) as the output for our LED.  A schematic and breadboard image of this project is at the bottom of this post so you can build it and follow along.

Let's have a look at the program now, then we'll walk through it.

Figure 5
In reference to figure 5, please view my post which corrects an error made here.

As we can see at the very top of the program, I have made use of the ability to place comments in the code.  It is good practice to place a description of the program at the top of each program we write.  Following the comments, we need to include the xc header file.  As stated before, the header files contain a bunch of stuff that the compiler needs in order to generate code.  The xc.h file automatically includes the MCU specific header file so that all of the definitions and addresses of the special function registers are there.  Remember to place the file name within the <> symbols.  I have made use of comments to describe what the program is doing, and also to break up sections of the software to make it easier to read.  I haven't just done this for the sake of making it easier for the learner to look at the software, I always do this.  As you start writing longer pieces of software, you will really appreciate breaking up the code like this.

Now, let's take a slight detour for just a moment.  One of the the things that always bothered me about reading books that were supposed to teach you MCU programming in C is that they would do one of two things.  They would show you an example first program and not include this section on the CONFIGURATION WORD, or they would have that section in the code, but never explain it until later in the book.  The configuration word is SUPER important and will most likely be used in every program you write, so why wouldn't you explain it right away?  Well I suppose because it's a long-winded explanation, but I'll try to keep it short and to the point.

Configuration Word

Microcontrollers have special options in their program memory separate from the Special Function Registers called the Configuration Word.  The configuration word cannot be accessed during program execution, so they must be set during programming of the device (with the compiler).  The configuration word allows the user to define certain configurable features of the microcontroller.  We will talk briefly about those features now, focusing only on those that are currently relevant.  Section 9.1 of the datasheet shows the PIC12F629 configuration word register (figure 6).

Figure 6
Under the configuration word register in figure 6 is a legend.  This legend is useful when looking at any register because it tells you how you can manipulate the bits, and what their default status is.  In the case of the configuration word bits, they show "R/P - 1" which means they are readable bits (can be read in program memory), they must be set during programming, and their default status is 1 (which means "set").

The datasheet defines each bit and it's available settings.
Bits 13 and 12 control the Bandgap Calibration.  These bits are set at the factory and are used partly to calibrate the MCU's internal oscillator.  We wont be messing with them.
Bits 11 through 9 are unimplemented.
Bits 8 and 7 are used to enable Code Protection for EEPROM data and program data respectively.  Code protection is a means of keeping your software safe from theft.  Basically, if you are a company producing a product, and you want to help keep someone from stealing the code off your MCU and using it to produce a "knock-off", code protection helps.  We will always keep code protection disabled because once it's set, you cannot read from or write to your MCU anymore.
Bit 6 controls Brown-Out Detection, an internal trigger that shuts down the MCU when operating voltage drops below a threshold.  It is useful for situations where the embedded system runs off batteries.  The Brown-Out Detection (BOD) circuit will reset the MCU when voltage is restored above the threshold.
Bit 5 controls the Master-Clear Reset (MCLRE).  The MCLRE function is used to externally reset the MCU.  When enabled, the MCLRE pin must be provided with positive voltage for the MCU to operate.  The MCU is held in reset as long as this pin is low (no voltage), and restarts the MCU from the reset vector when voltage is restored.  MCLRE can be turned off so that the MCLRE is internally tied to the VDD pin.  We will explore using the MCLRE function in later projects.
Bit 4 controls the Power-Up Timer (PWRTE).  The power up timer is disabled by default, but is used to hold the MCU in reset mode upon power up.  This feature allows time for the power supply to stabilize before program execution begins.  The power up timer's delay is a nominal 72 milliseconds.  We will always use this feature, since there is little reason not to.
Bit 3 is the Watchdog Timer (WDT) bit.  Many times, embedded systems operate without human intervention, and in places that access by humans would be impractical.  If for some reason during program execution the device get's hung up, the WDT will reset the device.  The WDT must be periodically cleared in software in order to prevent the device from resetting during normal operation.
Bits 2 through 0 are used to select the Oscillator mode.  Microcontrollers use an oscillator for all timing operations, including normal program execution.  Precise timing operations require a more precise oscillator, using a crystal or resonator.  Less precise operations may utilize a less expensive Resistor-Capacitor (RC) oscillator.  Many MCU models include an internal RC oscillator, but not all.  We will dedicate a lesson later specifically to the MCU's oscillator clock and modes.

To view the configuration word settings for a PIC chip, you can view them in the chips html files located at:
C> Program Files> Microchip> xc8> version #> docs> chips.  Scroll down to the chip you're using, such as the 12f629.html file and open it. All of the options are listed under the "#pragma config Settings" header.

Back to our Program

We left off in the Configuration Word section.  The configuration bits are set using the preprocessor directive #pragma.  The pragma directive is a method of providing additional information to the compiler.  It tells the compiler to do something, set some option, take some action, override some default, etc.  We use it here to set the configuration bits during programming.  The format of using #pragma is #pragma keyword options where in our case the keyword is "config" since we're setting the configuration options, and then the actual option we want.  In my program I have strung all of my options together using commas, but you could indicate them separately if you chose, for example:
#pragma config BOREN = OFF
#pragma config MCLRE = OFF
#pragma config PWRTE = ON
And so on.  You can find the configuration word options in the device's chip file.  Chip files are located in: program files>microchip>xc8>version#>docs>chips.  Looking in this document for the PIC12F629, we can find the options listed at the bottom.  We do not need to specify bits we're not going to change from their default.  For example, Code-Protection is disabled by default, so we don't need to specify CPD = OFF since it's already off.  For this program, I have specified in my configuration word to turn off the BOD, MCLRE, and WDT; turn on the PWRTE; and am using the internal RC oscillator with I/O function on the clock-in and clock-out pins.

After the configuration word our main function begins.  We're not passing any information into or out of the main function, hence the void keyword and the use of empty parenthesis.  The following line opens our main function group with the left brace.  Program execution begins with the main function and the entire executable program resides within it.

We use the TRISIO register to set the data direction of our I/O bits.  Setting a TRISIO bit (=1) will make the corresponding GPIO pin an input, while clearing a TRISIO bit (=0) will make the corresponding GPIO pin an output.  I have specified the entire TRISIO byte (8 bits) using a hexadecimal number.  All hexadecimal numbers are preceded by a zero and lower-case x "0x" to indicate to the compiler that you're using a hexadecimal number.  Since GPIO pin 3 is the only input we're using, and unused pins should be outputs, our TRISIO byte is 00001000.  I have used a Binary to Hexadecimal Converter to convert this into a hexadecimal number.  In reference to the preceding paragraph, I have made an error. Please see my blog post, which corrects this error.

After specifying port direction, I have cleared the GPIO port (turned all the pins off).  We do this at the beginning of a program in case the device gets reset while a GPIO pin is on.  It's good practice to do that, as not doing so could cause your embedded system to not operate properly.

The next line is the conditional function while( ).  A while loop checks a condition and, if the condition is true, executes the loop for as long as the condition remains true.  The loop is everything within that function's set of braces {}.  If the condition is false, the loop will not execute.  The condition to test resides within the parenthesis.  The first while loop we use is a simple one that pretty much every program we write will contain, the while(1) loop.  We place a 1 within the parenthesis to ensure our condition is always true, thus the program will continue to execute over and over again.  Without this loop, we are depending on the compiler to assume we want the main function to execute repeatedly, and that doesn't always happen, so a while(1) function is a safety net.

Following while(1) is a second while loop.  Here we have asked the program to check the condition of the GPIO3 pin that our button is connected to.  When we press our button, it applies VDD to GP3, thus placing it in a logic high state (pin reads 1).  When we release the button, no voltage reaches the pin and the logic state is low (pin reads 0).  The operator "==" is the conditional operator.  In plain English, while(GP3==1) says "while GP3 pin reads 1, do this..."  The "this" is whatever is inside the function braces.

The C language differentiates between a single equals sign, and two equals signs.  Example: GP0=1 means "make the GP0 pin equal to 1", whereas GP0==1 means "if the GP0 pin is equal to 1".  Getting these confused causes problems in your code.

Okay, so our while function says that while we're getting a logic high on GP3, do whatever is inside the braces.  In this case, there is only has 1 instruction (statement), GP0=1;  This turns on the GP0 pin, which turns on our LED.  Then the function is ended with the right brace.  When we press the button, GP3 is evaluated and is at logic high, so the condition GP3 is equal to 1 holds true, which in turn allows the function to be executed and GP0 is turned on.  This loop continues to repeat until we release the button and GP3 is no longer 1.

If the button is not pressed, GP3 is evaluated to be 0 and the while loop condition operator is false, therefore the loop is not executed.  Then the default instruction GP0=0; is instead executed, which turns the LED off.

Our program ends by closing up our open functions.  Adding a right brace for each open function, in this case the while(1) and the main functions, will end the function loops.

Compiling our Program

Okay, our program is written, now we need to turn it into a hexadecimal file (.hex) that the PIC can read.  Refer to figure 7.
Figure 7
Label A shows the location of the "build" button.  This button will compile our program into a .hex file, which is the file that we flash into our PIC.  Before we can press the build button, we need to add the xc.h file into our project so the compiler knows where to find it.
Label B shows where I have added the xc.h file into the IDE.  Right click on "Header Files" and select "Add Existing Item..."  This file is located in Program Files>Microchip>xc8>version#>include, be sure to add xc.h and not xc.inc.  Once you have added the xc.h file, we can press Build.
A new Output window (label C) will pop up, and you'll see some text popping up within it.  When the compiler has finished, you should see the words BUILD SUCCESSFUL.  If something is wrong about your code, you would see BUILD FAILED here.  This would indicate that there is some kind of mistake in your code, and the output window will show you what kind(s) of error(s) you have using a blue link.  For instance, if you had forgotten to place a semicolon after a statement, the compiler would say "learn_c.c:22: error: ";" expected".  If you click on this text it will point you to the location of your error(s).  The number 22 within the error is the Error Code, and you can find a list of all the error codes and what they mean in the compiler manual, which is in the XC8 directory in the "docs" folder.
Finally, label D shows how much memory your program has taken up of the memory available in your PIC chip.  Our program has only used 24 bytes out of 1024 available, or 2% of total memory.

Our program is successfully compiled now, so let's find the .hex file it generated.  A .hex file is a hexadecimal file that we flash into the PIC.  The .hex file get's put into your project folder by the compiler, which is located at: C:>users>(your name)>MPLABXProjects>learn_c.x>dist>default>production.  Yea, I know that's a bit of a long directory, it's buried deep.  You'll see your .hex file in there, learn_c.X.production.hex.

Flashing the Microcontroller

Alright, the project is compiled and we know where our .hex file is, now we need to get it inside the microcontroller.  Break out your PIC Programmer, in my case, the PICkit 2.  The PICkit 2 (and presumably the PICkit 3) come with their own software interface, and they flash the microcontrollers with what's called In Circuit Serial Programming (ICSP).  The setup is fairly easy, and when properly set up you can flash your PIC without having to remove it from the project circuit.  Microchip provides a lot of information on ICSP, and it is well covered in the PICkit 2 or 3 documentation.

Figure 8
Figure 8 shows the breadboard setup for programming our chip, be sure that the PICkit is aligned properly.  These connections are taken straight from the PICkit user guide.  Plug your PICkit into the computer's USB drive and open the programmer software.  Remember that I'm using the PICkit 2, if you're using something different, you'll need to attempt to find the comparable operations.  Figure 9 shows the PICkit 2 software interface.  If your PICkit is plugged into the computer you should see the message "PICkit 2 found and connected" in the message window.  If you have already built the connections to the ICSP and are connected to the PIC12F629, you will also see the message "PIC Device Found".  Let's load the .hex file into the programmer by clicking File and then "Import Hex".  Browse to the .hex location and select the file.  Your screen should now appear as in figure 9.  After you have made all the connections from the PICkit programmer to the PIC microcontroller, pressing the "Write" button will flash the PIC chip.  The message window will change to green and display "Programming Successful" when the operation is complete.  We're ready to build and test our project!
Figure 9
Project 1 Hardware

The project schematic and breadboard layout images are below.  When your project is assembled, you can test the program in action.  Pressing the button will illuminate the LED and releasing the button will extinguish it.
IMPORTANT NOTES: Do not exceed 5V input to the MCU as doing so will destroy the device.  There is a tolerance to about 5.5V but it is safest to use 5V, hence the use of the LM7805 voltage regulator.  Also, the current limit of each MCU I/O pin is 25mA thus, be sure to use an appropriately sized current limiting resistor on the LED.  My schematic shows the use of a 270 Ohm 1/4W 10% resistor.  Voltage output on the I/O pin is VDD (5V) when turned on, thus using Ohm's law provides that current sourced from pin 0 to the LED is 5V/270R = 18.5mA.  DO NOT EXCEED VOLTAGE OR CURRENT LIMITS!

Project 1 Schematic
Project 1 Breadboard Layout


November 25, 2014: Post edited to direct the reader's attention to an error in the text. Links have been added to allow a direct-open to the correction. The original information was left intact because I felt that the information provided, which instructs how to use a hexadecimal number in a C program, and a link to a hexadecimal converter were still useful.

14 comments:

  1. by linuxosaure

    very nice sample, and very good electronique description

    ReplyDelete
  2. im starting with this app thanx :)

    ReplyDelete
  3. very good explanation...Too much helpful for beginners...!!

    ReplyDelete
  4. I am moving up from picaxe programming and your site sems to be a great place to start
    many thanks for providing this information
    hopefully age will not be a problem as I have just retired and am trying to keep the bain active.

    ReplyDelete
  5. Very thorough. You explained many omission in Microchips' examples.

    ReplyDelete
  6. Extra clear and useful info. Thanks for improving the world!

    ReplyDelete
  7. Glad I could help you get started.

    ReplyDelete
  8. Very helpful tutorial, however for some reason my breadboard works exactly opposite with a button press. If I make the first GP0 = 0 and the second GP0 = 1 then the circuit works properly. Have confirmed wiring is ok and code is identical. It doesn't make any sense to me, but I'm new.

    ReplyDelete
  9. This comment has been removed by the author.

    ReplyDelete
  10. hai pls help me......how write coding .to execute the if condition for one time eventhough the condition is true .... am using mplab x ide beta version 7.12.and hi-tech pic compiler and am using pic16f877a mc

    ReplyDelete
  11. you have the pinout for the pic kit reversed;it would only work if the front of the 'kit2 was facing away from the breadboard,standing upright...

    ReplyDelete
  12. I know this is almost 10 years later but I'm currently learning about PIC programming and your blog posts here were a super useful resource, thanks for writing them!

    ReplyDelete