Due Tuesday Jan 15, 11pm
In this lab you’ll get hands-on experience with the AVR-based microcontroller board we’ll use for the duration of the course, the Pololu Orangutan SVP, which from now on we’ll abbreviate as the “org”. We also call this board the low level processor or LLP.
Remember, the course references page has links to important reference documentation for this board and its software environment.
If you are using one of the provided Linux environments—the virtual machine on your ohmmkey, the ohmmbook netbook, or the pandaboard—we have preconfigured Ubuntu 12.04 with all the needed development tools and libraries for the course. Otherwise, we recommend you set up Ubuntu 12.04 (using vmware if desired, but we don’t recommend other virtualization environments due to possible issues with USB support). Follow the instructions here to configure it. It may also be possible to configure OS X and Windows (with Cygwin) with the necessary software, but these options are not currently supported.
Please make a user account for each of your group members on your ohmmkey, pandaboard, and ohmmbook (if/when each of these are supplied to you). Make your usernames the same as your CCIS usernames (the passwords should be different for security).
This is most easily done with the graphical user manager at Main Menu -> System Tools-> Administration -> Users and Groups, or just run it directly from the command line with the command
users-admin. The default user “ohmm” has admin privlidges, which will be needed to add new users.
Make sure your newly created users are members of the groups
plugdev. This should be correct by default if you are using one of the preconfigured Linux environments on the ohmmkey, pandaboard, or ohmmbook.
Or, follow the instructions here to add users on the command line. This can be important, for example, when you receive the pandaboard and are logged in to it via a text terminal with an ssh or serial connection.
To continue with the instructions below, log back in as one of your group users.
To begin working with the code for this lab, you will need to make checkouts of several subversion repositories. Three of these are read-only repositories that we (the course staff) use to publish code for you to use. The fourth is your own group repository. Each member of your group should make their own checkout of all four of these repositories, and this needs to be done on each machine you intend to use for development. Note that once these four checkouts have been made for a given user on a given machine, you can continue to use them for the rest of the term. You may be periodically asked to do an
svn update in the read-only repositories as we update the code there, and you will do both
svn update and
svn commit in your group’s repository as you develop your code.
We suggest you make a subdirectory of your home directory and store all four checkouts in it like this:
> cd ~ > mkdir robotics
Checkout the OHMM robot and course repositories like this
> cd ~/robotics > svn co https://ohmm-sw.googlecode.com/svn/trunk ohmm-sw > svn co https://trac.ccs.neu.edu/svn/robotics-s13/ohmm-sw-site > svn co https://trac.ccs.neu.edu/svn/robotics-s13/g0
If prompted, supply your CCIS username and password. These commands should result in three new subdirectories of your
robotics directory named
g0. The first one is the open-source code repository for the OHMM robot. The second one is an “overlay” for that which adds robot code specific to this course. The third,
g0 is the “group zero” repository. The course staff is group zero. We will be publishing framework code for each lab under this repository.
Now, make a checkout of your group’s repository like this, where your group number is
> cd ~/robotics > svn co https://trac.ccs.neu.edu/svn/robotics-s13/gN
If prompted, supply your CCIS username and password. This should result in a fourth new subdirectory of your
robotics directory named
N is your group number.
Make a subdirectory of your group directory for this lab, and then copy the framework files we provided in the
g0 repository into your repository. Note that these commands assume you followed the instructions above to create a
robotics directory in your home directory with
gN subdirectories (remember
N is your group number).
> cd ~/robotics > svn up ohmm-sw; svn up ohmm-sw-site; svn up g0 > cd gN > rsync -Crav ../g0/l0 . > svn add l0 > svn commit -m "added lab framework"
The org ships with a nice little demonstration program. In this part you will use it to see some of the capabilities of the board.
Multiple demos are presented in a menu-based interface using the on-board LCD and the three “user pushbuttons” to the left of the LCD (orient the board so that the USB connector is facing away from you). Some of the demos will not do much without extra hardware connected (like motors), but many use only things available onboard.
Use the supplied USB cable to connect the org to any free port on your workstation. If you get a dialog box asking whether to connect the new USB peripheral to Windows or to your Linux guest OS running in VMWare, select Linux (you may also make this choice “permanent”).
Connect the org to the provided AC power adapter as shown in lab, plug it in, and turn the org on (refer to the org Reference Diagram) for the location of the power switch.
We have pre-programmed the org with the demo program. Experiment with it. Some documentation about it is provided in the org user manual. The full sourcecode is also provided under
Now you will compile four simple programs and run them on the AVR. Each is in a separate directory under
hello — “hello world” using the org LCD
led — turn on the red “user LED” (Light Emitting Diode), using either the Pololu AVR Library or direct access to the AVR Special Function Register (SFR) for the pin connected to the LED
add — add two integers and display the result on the LCD using
blink — blink the red user LED
Each of these simple programs is provided with a makefile that automates the process of both building (i.e. compiling) the code and programming the code to the org. We have coded this makefile in a general way so that exactly the same makefile can be used for each program; by default, it assumes the name of the program is the name of the containing directory.
This may be a more complicated makefile than you may have encountered before. It uses make’s facilities to define and use variables and conditionals and to include other makefiles. The makefile is in fact split into two files:
makefile is the top-level file, it contains documentation and default settings that could be modified on a per-project basis if desired.
makefile_AVR.inc is included by
makefile to provide basic functions related to building C code for AVR processors and for programming them to flash with a program called
avrdude. Normally we should not need to modifiy
makefile_AVR.inc; it is best not to, so that if we fix a bug or add a feature to it “upstream”, you can drop in the new version without clobbering any local modifications you might have made to it.
Use an editor to examine
hello/makefile_AVR.inc but do not modify them. You may not understand all the syntax; later you can spend more time and look things up in the make manual.
While the makefile may appear complex under the hood, it is very easy to use. Just run the command
In any of the program directories — we recommend you start with
hello and work through them in the order listed above — to compile the code. Several files will be generated, the most important of which will be
hello.hex. This is a machine-readable file containing the data that should be programmed to the AVR flash. The other files include
hello.lss — dissassembly listing, human readable text, for debugging. This shows the assembly code corresponding to the machine code that the compiler generated for you. In most cases you can ignore this (and it is not used for anything else), but it can be very informative to read so you understand how your C code was translated to machine instructions. In some cases, it can also be helpful to understand some types of bugs that can crop up due to misunderstandings between you and the compiler.
hello.o — binary machine readable object code file. Normally the compiler will generate one
.o file corresponding to each
.c file in your project. The
.o file contains “object code”, which is basically the machine code corresponding to each function in the corresponding
.c file. While it is not usually helpful for you to read the contents of
.o files directly, they are very important to the compiler. See the section below for more explanation.
hello.elf — binary machine readable file of your executable program, including info to help an OS load and run it. If you were writing C code for your PC, this would be the most important final product, the program you could run at the command line to test your code. Since we are programming the AVR, the makefile generates the
.hex file from this
.bin file, which puts it into a format that can be programmed to the AVR’s flash.
hello.bin — similar to
hello.elf but does not contain the OS loader info.
makefile.depend — a working file created by make that helps it figure out dependencies.
To get rid of the non-essential build products, you can run the command
> make clean
This will delete all generated files except the
.hex. If you want to get rid of that too, run
> make realclean
It is now finally time to program the org with your own code. Examine
hello.c carefully in an editor. Look up things you don’t understand in the appropriate documentation. When you are satisfied you understand what it’s supposed to do, run this command (in the
> make program
This will (re)generate the
.hex file if needed, and then it will attempt to program it to the org, which should still be connected by USB to Ubuntu. You will see a sequence of messages in your terminal window that help you monitor the programming process. If all goes well you should eventually see
avrdude done. Thank you.
The org will automatically reset itself after programming, and you should see the expected results of your code on the org.
Examine each of the other provided programs, build them, program them to the org one at a time, and test them out. Later, we’ll ask you to make some modifications to these programs.
Part of the challenge of learning C, even if you already know another language like Java, is understanding the mechanics of how the compiler works. What is up with
#include <foo.h>? Why do we need
.a files? What is the linker?
The answers to the “why” questions are partly due to the historical development of C programming, which was related to the development of Unix-like operating sytems stretching back 30 years or more.
Let’s just focus on what happens in practice using avr-gcc for programming your org (though just about all of this also applies to other C programming systems).
A C project may contain code in any number of
.c files. The first thing that happens when you build the project is that avr-gcc runs them through a textual preprocessor called the C preprocessor, sometimes abbreviated as “cpp”. The preprocessor looks for preprocessor directives which are lines starting with
directive is commonly something like
ifdef, etc. You can read the avr-gcc manual (actually the preprocessor used for avr-gcc follows the regular C standard, so it should not be AVR-specific) to understand all the possibilities. Some of the most important ones are
file.h in one of the system directories (
/usr/lib/avr/include for avr-gcc on Linux) and inserts a copy of its contents textually into the file being processed;
similar to the above syntax, but using double quotes instead of angle braces changes the search behavior so that “local” directories are searched first. These include the directory containing the file being processed as well as any directories explicitly listed with
-I incdir options on the avr-gcc command line;
#define NAME value
defines the symbol
NAME (by convention, preprocessor defines are all caps) to have the text replacement
value — any later instance of
NAME will be replaced by
#define ADD_MACRO(arg1,arg2) (arg1+arg2)
defines a preprocessor macro taking two arguments; any time
ADD_MACRO(foo,bar) is encountered later, it will be replaced with
A very common use of the
include directives is to load header files (
.h) that contain function declarations. Recall that in C, a function may be declared with syntax like
int myFunc(int arg);
that tells the compiler the function exists and how it may be called (its interface). The body of the function (its implementation) is called its definition and typically is not included in the header file, but rather in some
.c file. This is an encapsulation mechanism—a reader of a header file can see what functions are available and how they are called, but cannot see how those functions are implemented.
The final result after cpp finishes handling all its directives is called a translation unit. (Usually, the translation units resulting from cpp’s work are not stored as files on disk, though they could be.) A common case is that a file
foo.c defines some functions which are declared in
foo.h near the top. The C preprocessor expands
foo.c and the result is a translation unit that starts with a set of function declarations and continues with the function definitions. The reason
foo.h is used is because the functions it declares (which are defined only once in
foo.c) might be intended for use in (i.e. to be called from ) other translation units; those would include
foo.h as well, but not
foo.c. The C compiler will compile a translation unit as long as all the called functions are declared; they don’t need to be defined at the time of compilation. The linker (see below) will later resolve references to functions that are defined in other translation units. The linker only requires the machine-readable binary compilation result for the translation units (typically object files,
*.o), not the original source code.
The next step is that the compiler actually compiles each translation unit, separately. Usually there is one translation unit per
.c file, but the translation unit will also include any code that was
#included, or that otherwise resulted from preprocessor directives (like macros). The result will be one
.o object file for each translation unit. Usually, these are written to disk, though that can be optional.
The third and final step is called linking. The primary job of the linker is to resolve symbols in object code. This is where a call to
myFunc() in one translation unit will be matched up to the definition of
myFunc(), possibly in another translation unit. Importantly, some of those tranlsation units might not be in your own code; the linker will also look in
.a library files, which actually are just archives of object code from
.o files (like tar or zip, but actually even simpler because they are typically not compressed). The linker of course needs to know in which library files it should look. Most of the C standard library calls, like
printf(), are automatically resolved by the liker with no further action requried. One exception is the math library, for calls like
sin(). The way you specify a library in which to search is by including a command line argument on the avr-gcc command line that you use to link your code, which for simple projects can also be the same command that does the preprocessing and translation; the details of composing and running these commands are abstracted for you by rules in the provided makefile. The argument has the syntax
name is the library name; for the math library this is simply
m. The compiler will then search for a file named
libname.a in a set of directories where it normally expects to find libraries. These include system locations, which for avr-gcc on Linux usually includes
/usr/lib/avr/lib, as well as directories that are specifically listed on the command line by
-L libdir options. There can be multiple
-l options; note that a common problem for beginners is that for the
-l options, order matters: the linker tries to find remaining unresolved symbols in each library specified on the command line in order. So if you have a library
libfoo.a that contains code that calls a standard math function like
sin(), make sure that you have command line options
-lfoo -lm in that order. Again, the makefiles we provide you will usually relieve you from having to worry about composing avr-gcc command lines.
Now we’ll challenge you to make some modifications to the provided code.
led program to light the green user LED instead of the red one. You need only make it work when
USE_POLOLU_LIB is defined. For an extra challenge, make it work when
USE_POLOLU_LIB is not defined, by directly accessing the SFR that controls the green user LED (hint: refer to the org reference diagram and page 12 of the org user manual, which describes how the port pins control the user LEDs).
blink program so that pressing the top user pushbutton makes the led blink faster, pressing the bottom makes it slower, and pressing the middle resets it to the default speed. More specifically, design the code so that the blink half-cycle period decreases by 10ms when you release the top button, and increases by 10ms when you release the bottom button. Make sure that you don’t allow the user to reduce the period further once it hits zero. You will most likely find the pushbutton functions provided by the Pololu AVR library very useful for this task.
Hand in your code following the general handin instructions by the due date and time listed at the top of this page.