C program

hzxlove
Lab1.pdf

UNIVERSITY OF CALIFORNIA, SANTA CRUZ BOARD OF STUDIES IN COMPUTER ENGINEERING

CMPE13/L: INTRODUCTION TO PROGRAMMING IN C

Lab 1: Compiling, Running, and Debugging

Introduction

This is the first lab in CMPE13. Here we will demonstrate the basics of compiling and running C programs in the simulator and on the Uno32 hardware. We will also explore the tools we will use and some of their features for debugging problems you might encounter.

Reading

• Document on compiler errors

• Document on Unix and Git

• Document on software installation (if you want to run everything on your own computer)

• Document on style guidelines

• Document on MPLAB X

• Document on serial communications

• K&R Preface and Introduction

• K&R Sections 1.0-1.2, 4.5, 4.11

Provided Files

• part1.c: This file contains code that performs a simple sorting algorithm on five randomly

generated numbers. Follow the setup procedures listed below, add the requested

documentation, and format the code to follow the provided style guidelines.

• part2.c: This file contains an empty main() to be filled with the exercises from section 1.2 of

K&R. In addition, you will be asked to modify these exercises to add some additional

functionality. Detailed steps are listed below.

• BOARD.c/h - Contains initialization code for the UNO32 along with standard #defines and

system libraries used. Also includes the standard fixed-width datatypes and error return values.

You will not be modifying these files at all!

• Oled.c/h, Ascii.c/h, OledDriver.c/h – These files provides all the code necessary for calling the

functions in Oled. You will only need to use the functions in Oled.h, the other files are called

from within Oled Library. You will not be modifying these files at all!

Assignment requirements

0. Perform “Hello World” with the UNO32

1. Complete the requested modifications to this code.

o Complete the setup procedures

o Add the requested documentation to a README.txt file

o Format the code to follow the provided style guidelines

2. Complete the temperature conversions tables based on code provided.

o Implement code to output a table of equivalent Fahrenheit and Celsius values.

o Extend this code to also output a table of equivalent Kelvin and Fahrenheit values.

o Format the code correctly according to the style guidelines.

3. You will implement a simple module to implement two very simple functions. Detailed steps are

listed below.

4. Extra credit: “Hello World!” on the OLED.

Grading

This assignment consists of 12 points:

• Two points for Part 0

• Four points for Part 1

• Four points for Part 2

• Two points for Part 3

• One point of extra credit

Note that you will lose points for any and all code that fails to compile!

Part 0 – “Hello World”

All programming languages have a “hello world” program1. This program is generally simple as possible and demonstrates that both the code and system runs. While the “Hello World” performed on the UNO32 prints “Hello World,” other embedded systems might have a blinking LED to indicate success.

1 In fact, this is true for all languages because K&R states so at the beginning of chapter 1: “The only way to learn a new programming language is by writing programs in it. The first program to write is the same for all languages: Print the words

1. Follow the MPLAB X new project instructions (link) to generate a project for this part. I would suggest working with the git repository2. We have seeded your repo with folders for each lab. When the graders pull labs this is where they will be pulled from.

2. Right-click on source files and click on New->Other… a. Choose Microchip Embedded-> XC16 Compiler and select the mainXC16.c file (We’re not

using the XC16 compiler, but this will generate the correct file anyway). Click “Next.” b. Name the file Part0 and click “Finish”. This will generate a new main file for the project.

i. Remember for future reference that only one file with a main() function can be added to your project at a time.

c. Make the contents of this file appear as those shown below

3. Go into project properties and navigate to simulator->Uart1 IO Options and check Enable Uart1

IO.

4. Now press the “Debug Main Project” button: . This will run your code inside the simulator.

a. If this fails for some reason and your code looks exactly the same as shown, make sure you go back and check the new project document.

b. If it still does not work and you have followed the new project instructions, get help from the teaching staff.

5. At the bottom of the window will be an Output tab. One of its subtabs will be title "UART 1 Output", selecting this will show the serial terminal output. It should show the words “Hello World” showing that we have successfully ran the hello world program.

6. Click on to stop the program and continue working. 7. Now that we have successfully run the code on the simulator we can run the code on the

UNO32 itself. This process is started by plugging in both USB cables that came with your kit into the computer.

hello, world This is a big hurdle; to leap over it you have to be able to create the program text somewhere, compile it successfully, load it, run it, and find out where your output went. With these mechanical details mastered, everything else is comparatively easy.”

2 This symbol designates a good place to commit in the process. This should get you used to commiting early and often.

G

G

G

8. Once the drivers have installed themselves go back into the project properties. a. To program the UNO32 the only change needed is to select PICkit3 as shown below:

b. Hit “OK” and exit out of the project properties. Don’t worry about the SN as each PICkit3

has a unique one.

c. We can now hit the Make and Program Button ( ) to load the code onto the UNO32. To actually see the output you will need to setup a serial port as described in https://classes.soe.ucsc.edu/cmpe013/Spring15/Labs/CMPE13_SerialCommunications.p df

d. Once the serial port is setup you can hit the program button and MPLAB-X will load the code onto the UNO32 and run it.

i. If you encounter any errors, recheck the serial port documentation. ii. Note that sometimes there are issues with the PICkit3 connecting.

iii. If it is still not working, contact the teaching staff. e. You should now see “Hello World!” on your serial program window.

Part 1 – Debugging and Code Style

1. No hardware needs to be connected yet.

2. Create a new project in MPLAB X using the new project guide being

sure to select the simulator. Name the project something that

makes sense like “Lab1_part1”.

3. Go Into project properties and navigate to simulator->Uart1 IO

Options and check Enable Uart1 IO as was done in the previous

section.

4. Add part1.c to the project (Right-click on the "Source Files" folder in the project window

obtainable from View -> Project)

5. Build the project by clicking the hammer on the toolbar. This will result in a

"Lab1_part1.X.production.hex" file under "\Lab1_part1.X\dist\default\production"

a. You should see black “BUILD SUCCESSFUL” text at the end of a successful build. A failed

build will show a red “BUILD FAILED” message. Get help if your build fails for part 1.

6. Now press the “Debug Main Project” button: . This will start the simulator.

a. Debug controls, in order: Debug, Stop, Pause, Reset, Continue

7. Press the pause button. It should be located to the right of the “Debug Main Project” button.

The debugger should be stopped on the final while(1); at the end of the code. A green bar

with an arrow on the side is used to indicate the line of code the debugger has paused at (this

line of code has not been executed yet).

8. At the bottom of the window will be an Output tab. One of its subtabs will be title "UART 1

Output", selecting this will show the serial terminal output. It should be a sorted list of five

numbers, like the following: "[46, 92, 105, 174, 212]".

9. Press the reset button in MPLAB (the blue one with two arrows). This will reset the debugger to

the start of main() and stay paused. Don't press play quite yet.

10. Place a breakpoint on the first line inside the curly braces of the first for-loop (line 85). Do this

by clicking the line number in the left margins of the source code view. It should place a red

square on the line number and highlight the whole line red. Placing the breakpoint inside the

for-loop ensures that the debugger will stop before every iteration of the loop. Note that you

G

may only have 4 breakpoints active at a given time.

a. Removing a breakpoint is done by pressing the red square again.

11. Now press play. The debugger should pause at the top of the for-loop, changing that line to

green and showing a green arrow in the left margin.

12. Below the code window should be a number of tabs. Click the one labeled “Variables”. This tab

shows the values of all variables within the scope of where you are paused. It will be updated

whenever the program is paused, but not when it is running.

13. Find the array valsToBeSorted. You can expand it to see the value of every element of the

array by clicking the plus-sign/arrow next to it.

14. Right-click on the valsToBeSorted and select “Display Value Column As” -> “Decimal” to see

everything as base-10.

15. Record the values of the elements in valsToBeSorted in a new file, README.txt.

a. Also place your name at the top of the README.txt, along with the names of anyone

you collaborated with.

16. Press play. It should stop at the top of the for-loop again. Record the values of valsToBeSorted

again in your README.txt file and repeat 4 more times (the breakpoint will not be hit on the last

run).

G

17. After the last run through the loop it will continue to the end of the program and will not hit the

breakpoint we placed. The final sorted result of the array will be shown in the Output tab under

the UART 1 Output sub-tab.

18. Complete Task 1 - Add documentation

Add a comment directly above the sorting for-loop, but below the comment that says "Sort the

array in place", listing the 5 recorded values of valsToBeSorted from the last run (some may

be the same, but list them anyways).

19. Complete Task 2 - Correct formatting

a. part1.c does not conform to the dictated style guidelines. While we could fix the source

code manually MPLAB X has the capability to reformat the code for us. Press “Alt-Shift-

F” and watch as MPLAB-X fixes all the code for you.

b. After saving the properly formatted Part1.c we can view the history of this file by right-

clicking on the filename in the bar and selecting Local History -> Show Local History.

This brings up the interface as shown below.

c. Clicking on any of these dates will compare the current file to its content when it was

saved. (Note that this history is local to the computer and by default only lasts 7 days)

d. Similarly if instead of selecting Show Local History we click on Diff To… we can compare

it to an arbitrary file. In this case we can compare it to the Part1.c before we started by

using a fresh copy of the file.

e. Open up this diff and record the line numbers where changes were made to the code,

not the comments, to your README.txt.

Part 2 – Temperature Conversion

1. Standard Conversion Table

a. Create a new MPLAB project named Lab1_part2 following the same procedure as in Part 1

i. Don’t forget to enable the additional warnings in the compiler options!

ii. Make a new subfolder to keep things nicely organized.

G

G

G

b. Add the provided "part2.c" in it to your new project

c. Implement the code example below3. Type out the code between the comments that say

“Your code goes in between this comment and . . .” in your part2.c file.

d. Compile and run your code in the simulator by clicking the debug button that you used for

part 1. Now check that your output looks similar to the following output:

e. Fix the coding style for this code such that it follows the provided Style Guidelines. To do

this, select all of the code and click Source -> Format (or alt-shift-F) to allow MPLAB X to

automatically fix the formatting. Note the differences between the formatted and

unformatted code (use Edit -> Undo and Edit -> Redo to compare or use the local history as

in part 1). 3 Note that this is example is from K&R 1.2, please reread this section before attempting part 2 of the lab.

G

G

G

f. Change the display format (currently "%f") of the Fahrenheit value so that it has a minimum

width of 7 characters and a precision of 1. Also change the display format of the Celsius

value so that it has a minimum width of 4 characters, it's left-padded with 0s, and displays

no characters after the decimal point.

i. To see an explanation of these format specifiers, click on printf() in your code,

and press CTRL+SPACE to bring up Code Assistance, and scroll down to the bullet

points. To see examples, scroll all the way to the bottom. Code assistance can also

be used for auto-completion, which we will later see.

ii. You can also access this information through help in MPLAB X as well. Go to “Help”

->”Help Contents”->”XC32 Toolchain”->”XC32 Standard Libraries”->”Standard C

Libraries with Math”->”<stdio.h> Input and Output”->”STDIO Functions” and find

printf (note that we think ctrl-space is simpler).

g. The output should now look as follows:

G

2. Column Headers

a. Add column headers to above the Fahrenheit-to-Celsius table

with the letters ‘F’ and ‘C’ spaced nicely using the printf()

function you read about in the MPLAB X help. This header should

be printed only once and be before any of the conversions are

calculated and printed.

3. Kelvin to Fahrenheit Table

a. Now print a newline character after the Fahrenheit-to-Celsius

table by typing prin, press CTRL+SPACE, and then press ENTER to

auto-complete4 into printf(). Use the string "\n" to add a

blank line in the output.

b. Now make a Kelvin to Fahrenheit conversion table, complete with

its own header. This can be done by selecting the block of code

that you modified:

4 Auto-complete, that is typing the first few characters and ctrl-space is you friend; this will complete function names, variables, members of structs, #defines, pretty much anything defined in scope in the current project. It is incredibly useful, use it often.

G

G

Then copy (Edit -> Copy), and paste (Edit -> Paste) the code below the printf() you

just added.

i. Rename the variable fahr by selecting the pasted block of code, and click

Edit -> Replace. For “Find What:” type fahr, and for “Replace With:” type

kelv. Click “Replace All”, and Close.

ii. Notice how the kelv variable was not defined (MPLAB X underlines it in red). To

fix this, declare it as a float by adding float in front of “kelv=lower;”.

iii. Now replace celsius with fahr in the same block of code. Next, modify the

line “fahr = … “ by changing the expression to convert from kelvin to

Fahrenheit.

iv. Now format the table to look like the image on the previous page. The Kelvin

values should be displayed in the left-hand column with a minimum width of 3

characters, left-padding with zeros, and a precision of 3. The Fahrenheit values

should be displayed in the right-hand column with a minimum width of 5

characters.

c. Note that a compiler error will occur if two variables of the same name are declared.

You need to have unique names for all variables within a single scope (scope will be

explained later, but for this part of the lab you will need unique names for all variables

you use). You can reuse your variables from the first table for generating the second

table, just make sure that the “float fahr…” and “int lower,…” lines only appear once at

the top5. Also you should make sure that your variable names make sense with the

values that they store6; for instance, you should use a variable named kelv to store the

calculated Kelvin values.

d. Refactoring (renaming) variables and functions is another useful feature in MPLAB X.

You decide that kelv should be renamed to kelvin in your code. Click on kelv

anywhere in your code, and click Refactor -> Rename, and type kelvin. This renames

the variable everywhere in the project! Be careful when using refactor.

e. Again view the output in the UART 1 Output tab of the Output window. The final output

of the program should look like the picture above.

5 In general, putting all of your variables at the top of your function is a very good practice. 6 Good naming of variables and functions is a key part of software design, start practicing it now.

G

G

Part 3 - #include directives and Modules

In this section you are going to learn to build your first module (which is a .c and a .h file

that each share the same name) in order to implement two very simple mathematical

functions. Note that these are really simple functions that you would never implement

this way, but serve to get you used to using functions and how to call them.

The two functions are: IncrementByOne and AddTwoNumbers.

Before we get into the nuts and bolts of implementing these, we need to know what a

module in C is. Basically, a module is a pair of files [.c,.h] which are called the “header

file,” which ends in “.h” and the “source file” which ends in “.c” which are added to a

project to encapsulate the functionality of the code into a common holder. The

advantage of this form is that is separates how to use the functions (instructions in the

.h) versus how the functions are implemented (in the .c). This is particularly useful in

complicated projects or where several people are working on the same code, as it

enforces partitioning the problem into smaller parts. A useful analogy is that the .h file

contains a contract between the end user and the programmer on how to use the

functions. As long as the user abides by this contract, the programmer is free to change

his/her code without causing the users code to fail.

In order to explore the functionality of a module, we’re going to first have you use the

built-in math functions of C in order to test the functions, and then later you are going

to use your own functions that you write yourself. This example is trivial, but

demonstrates how to use functions.

Make a new project with the part3.c file provided, and uses the two math operators +

and ++. These correspond to addition and (post) increment. They are used as:

z = x + y;

x++;

which is to say that the first line adds two numbers, sets a variable with the result. The

second line increments a variable. What we are going to want to do is modify the hard

coded numbers in there, and run both the addition and increment and make sure you

believe the numbers. As a point of interest, keep everything in integer variables.

Once you convince yourself that the math functions are working, and you have a decent

test capability, then we are going to substitute our own functions for the ones (in this

scenario, we are going to assume that the normal operators are not available).

In order to implement these two functions, we first need to define how the functions

are going to work. In the case of the AddTwoNumbers function, we take in two integer

values and returns the sum. In order to specify this to C, we will declare a function

prototype that looks like this:

int AddTwoNumbers (int y, int x);

For the IncrementByOne, we are going to need only a single integer input, and the

function returns a single integer. We are actually going to implement this by using the

other function we’ve created (that is, a function will call another function). Again, to

specify this in C, we are declare:

int IncrementByOne (int x);

Putting this together into a header file, we are going to have a full header file that looks

like this: /* module containing implementations of AddTwoNumbers and IncrementByOne */

#ifndef SIMPLEMATH_H

#define SIMPLEMATH_H

int AddTwoNumbers (int y, int x);

int IncrementByOne (int x);

#endif

This should go in a file named: SimpleMath.h

In order for your code (in main.c) to use these functions, you will have to include your

header file (the source file will get included automatically) by using the C-preprocessor

directive: #include “SimpleMath.h”

Note that you will need the #include line inside the SimpleMath.c as well in order for it

to compile. This is because C requires that all functions (and variables) be declared

before they are used.

Now, onto the implementation, which will go into the SimpleMath.c file. Let’s tackle the

Add Two Numbers first, since it should be more familiar.

We are just going to implement the addition within a function (sometimes very simple

functions are known as wrappers or wrapper-functions).

The increment by one function is going to accomplished by calling the add two numbers

function with one of the arguments set to 1. That is:

IncrementByOne(x) ↔ AddTwoNumbers(x,1)

If you wanted a helper function, you would put that as a private function within the .c

module. This helper function can be used by both of your functions, yet is fully

contained within the .c file (thus not exposed to the world). In order for the compiler to

be happy, inside the .c file you will need both the function prototype and the function

declaration itself. The function prototype tells the compiler how much space to set

aside, and lets you give it the rest of the details (actual code) later. Lets say you needed

a function of Bigger, which would return the bigger number. In this case the prototype

will look like:

G

G

int Bigger(int x, int y);

This should be up near the top of the .c file. The function itself, is going to appear

farther down in the file (lets go ahead and put at the end of the .c file), and is going to

look like:

int Bigger(int x, int y)

{

// you get to fill this in

}

Note that there are no semicolons at the end of the function. Once you have this

function written, you will need to test it. It should work for any two integer numbers,

positive and negative number, and also for 0.

Now go ahead and implement the other two functions (IncrementByOne and

AddTwoNumbers), and test them both using the previous part3.c main function to

convince yourself that they are working correctly. You will need to copy the lines that

tested + and ++ and use function calls to your own implementation. This will allow you

to compare the results of your own functions directly to what you got from the standard

versions and see how close they are. Print out the results from both calculations to see

that they are identical.

Part 4 – “Hello World!” on the OLED

You are going to print “Hello, World!” to the OLED on your UNO32 board. In order to do

this, you will need to add ascii.c/h, Oled.c/h, OledDriver.c/h to your basic HelloWorld

program. In order to use the OLED display, you will need to call the functions: OledInit(),

OledDrawString(), and OledUpdate(). Take a look at the Oled.h comments to see how to

use these functions.

G

G

G

Required Files (Case sensitivity matters)

• part0.c

• part1.c

• part2.c

• part3.c (technically optional but nice to have)

• SimpleMath.c

• SimpleMath.h

• part4.c

Frequently Asked Questions:

I see Connection Failed. in the PICkit3 tab when trying to program the ChipKIT.

Unplug the PICkit3 and plug it back in. Try to program or debug again. A window will pop up

prompting you to reselect the PICkit3 device. If this doesn’t work repeat a few more times. If

that fails, ask a TA/tutor.

I see The programmer could not be started: Could not connect to tool hardware: PICkit3PlatformTool,

com.microchip.mplab.mdbcore.PICKit3Tool.PICkit3DbgToolManager in the PICkit3 tab when trying

to program the ChipKIT.

Just disconnect and reconnect the USB cable for the PICkit3 and try again.\

How do I know which files to #include?

A source file (.c) should include it's complementary header file (.h). The header file should

include anything that's required for it to compile. That is, any types or enums that are used in

function declarations should be included in the header. Because the source file includes the

header, anything included in the header is effectively already included in the source file. If the

source file uses any outside functions or standard library functions, the matching header files for

those need to be included as well. You should never include a .c file, only a .h.

Steps should be:

1. Try to compile

2. If a function is not defined (causing an error) find the header file that defines it and

include that header.

3. Repeat until there are no undefined functions.

My code doesn't compile!

If the error message says something about multiple definitions of 'main' it's because you

included two files that have a main function in your project. Each of the three parts of this lab

need to be in separate projects; part1.c part2.c and part3.c cannot be in the same MPLAB X

project.

If the error message says something about expecting a character or identifier it's probably

because of a syntax mistake in the code. You can click on errors to go to where they appear in

the code. Keep in mind that for many syntax errors, the error that MPLAB X tells you about may

have been caused by an earlier line, and that a single-character mistake can create many errors.

Refer to the Compiler Errors document provided for some assistance with the more common

compilation errors you will encounter..

I see Failed to get Device ID in the PICkit3 tab when trying to program the ChipKIT.

Just disconnect and reconnect the USB cable for the PICkit3 and try again.