- A makefile is a file with a specific formatting that is understood by a program called "make". Like .pdf files are understood by "Adobe Acrobat" and .doc files by "Microsoft Word", makefiles are understood by "make", but they don't have a specific suffix. If you type make in a Linux terminal, the make program is executed, and it looks for a file named "makefile" in the current directory. You can alternately specify a makefile, but we won't do that.
- Our makefiles basically consist of lists of variable definitions and rules. Rules specify a "target", a set of "prerequisites" and then a series of commands for getting to the target. The commands are calls to other programs. The main program executed by our makefiles is the C compiler for AVR avr-gcc. This is the program that takes our C code generates the .hex code that is copied onto the microcontroller to be executed. In a very general sense, the makefile says "We want to a .hex file. We have some C files and here are the commands that need to be executed to generate the .hex file". Because the compiler is quite complicated, it is easier to use a makefile to specify a lot of options than to type them at the terminal every time you need to compile a program. The variables in the makefile define certain options or file locations to the compiler that need to be specified. These often don't change from one program to another, so we can copy a makefile from another project and change only the bits we need to. This is how EVERYONE uses makefiles. NOBODY WRITES MAKEFILES FROM SCRATCH. Well, not nobody. But almost nobody.
- Definitions in the makefile follow a simple form: VARIABLE = something. For example, the first line of our makefiles (other than comments which begin with a #) is:
MCU = atmega16
This specifies which microcontroller we are using to the compiler. The variable MCU is then used when the compiler is invoked, and will be replaced by atmega16, just like a macro substitution or #define in C. This way, if we want to change the AVR microcontroller we are using, we only need to specify it here. Variables can be defined in terms of other variables using $(). In our makefiles you might see:
TRG = fourbutton
...
...
SRC = $(TRG).c
#Now, SRC = fourbutton.c
- The heart of our makefile, where the compiler is invoked requires so few changes from one project to another that it is actually located in the AVRLib and simply included in each the project's makefile using:
include $(AVRLIB)/make/avrccrma_make
Where $(AVRLIB) has been defined as the path to your copy of the AVRLib. You can use a makefile in one of the demo projects to find the path to it and look at it if you want.
- There are only 2 parts of a makefile that you will likely need to change. The first has already been shown - the name of your C file. For a new project with a .c file called myproj.c, you need to have the following line in your makefile:
TRG = myproj
The other part of the makefile to change is the list of sources that are used. In the makefile for demo5, you will see the line:
SRC = $(AVRLIB)/timer.c $(AVRLIB)/rprintf.c $(AVRLIB)/lcd.c $(TRG).c
This line specifies all the .c files that are used by your program. $(TRG).c is your .c file, and the others are in the AVRLib. If you use a2d functions and included a2d.h in your .c file, you would need to add $(AVRLIB)/a2d.c to this line.
- When you call "make" from a command line, you can specify a target. This specifies a label in a rule that you want to generate. The default target is "all". Our makefile basically contains a rule that says:
all: $(TRG).hex
This means that in order to satisfy the target "all", you need to have a .hex file. This is known as a dependency. In other words, "all" depends on $(TRG).hex. There are then other rules that specify how to generate the .hex file by invoking the compiler.
- We have a special target in our makefile called "load". To satisfy this target, make calls a program called "uisp" (with a bunch of options that are defined as variables) that copies the .hex code over the serial cable onto the microcontroller. As you probably guessed, the "load" target depends on $(TRG).hex.