Using the Arduino IDE or the Antipasto IDE can be limiting for serious development owing to their design being focused for computer newbies, abstracting away what is going on under the hood. This is especially pertinent when using the Antipasto IDE, for boards like the Liquidware Touchshield Slide, because the IDE does not by default appear to support the adding of custom .cpp and .c files to the .pde source directory, forcing the use of a single .pde file, which is ridiculous. Furthermore, both IDEs force the user to stick the PDE in a directory of the same name. Perhaps there are some workarounds for these issues, but since I'm too lazy to RTFM I decided to write a makefile to compile and upload AVR programs to boards such as the Arduino.
This example will consider the Arduino Uno since this is a popular board. It will demonstrate how to create a static library using the Arduino source, which can be called upon to compile and upload custom c and c++ programs to the Arduino Uno. It is straightforward to modify this example for other boards, including the Mega1280 and the Touchshield Slide (I have done both). This explanation is written for FreeBSD and Linux users, similar steps can probably followed on Windows using win-avr and cygwin.
The first step in building an Arduino library is to obtain the src files for the Arduino board. You can get these from git, or from your own system if you've already installed the IDE (They'll be somewhere like /usr/local/arduino/hardware/cores/arduino/src/components/board). However they are obtained, put the src files into a directory of your choice. Alternatively you can save yourself the hassle and download this complete tutorial in a gzipped tar: arduino_gcc_tutorial.tar.gz
I'm going to assume arbitrarily, that you want to put headers in ~/include/arduino and the Arduino library in ~/lib, but of course you can put these wherever you want.
These should be placed in the same directory as the Arduino src so the listing looks like this:
binary.h HardwareSerial.cpp Print.cpp Tone.cpp WInterrupts.c wiring_digital.c wiring_pulse.c WProgram.h forward.cpp HardwareSerial.h pins_arduino.c Print.h WCharacter.h wiring_analog.c wiring.h wiring_shift.c WString.cpp forward.h main.cpp pins_arduino.h Stream.h WConstants.h wiring.c wiring_private.h WMath.cpp WString.h
Now for the Makefile. I'm using GNU make for no specific reason. On FreeBSD when installed this will be called "gmake", as BSD already has its own make. So you'll have to type this unless you alias it. Since most Linux distros use the GNU tools, it's likely that on a Linux box you can just type "make". Here is the Makefile:
When the make (or gmake) command is executed from the shell in the src directory the target named default is called. Now the default target depends on OBJECTS, which are all the object files required to build the Arduino library. These objects correspond to all the .c and .cpp files (except main.cpp which is no longer needed by us). Since default depends on these objects, and they do not presently exist, gmake tries to create them and looks for rules to create them. The rules which match are:
These are very similar to rules implicity defined by gmake, but they have been modified slightly. They are simple rules to create object files from .c and .cpp files respectively. To take the first rule as an example, it says: in order to create a %.o target, where % is a wildcard, check for a corresponding %.c dependency and perform the action specified on the following line. In this case, the action defined invokes the avr-gcc compiler upon the automatic variable $<, which denotes the first dependency. For example, when the target wiring.o is substituted for %.o, the corresponding %.c is wiring.c. This is also the first dependency and so substitutes $< as an argument to avr-gcc.
CFLAGS specifies the compiler flags, and is defined at the top of the Makefile:
The microcontroller (MCU) is specified with the -mmcu flag, and the CPU speed set to 16Mhz. The correct flags to use for a given Atmel MCU can be obtained by reading the manufacturers specification or by consulting Arduino's boards.txt.The flag -Os specifies that generated code should be optimised for size, note that speed optimisations are also performed upto gcc's level 2 so long as they don't typically increase code size. The -w flag suppresses warnings (which I don't care about). The final flag which appears in the rule is -c, which tells avr-gcc not to link the object file. And finally, going back to the action line, -o specifies the desired name of the generated object file. In this case $@ is used, which is another automatic variable which matches the file name of the target of the rule, i.e the object file matched, for example wiring.o.
gmake will perform this process for all objects specified, using the second rule for C++ files, the only difference being that avr-g++ is used instead of avr-gcc. Once all the objects are created, the default rule continues as follows:
The avr-ar line creates an archive named LIBNAME containing all the object files matched by the automatic variable $^. This automatic variable matches all of the rule dependencies, i.e the variable OBJECTS in this case. The next two instructions make the directories: ~/include/arduino and ~/lib. The forth instruction copies all the header files into ~/include/arduino so that they can be referenced later. The fifth instruction copies the generated archive libarduino.a into ~/lib so it can be used later. The final instruction deletes the generated object files which are no longer needed once the archive has been constructed. The library has now been constructed and is ready to use.
To build a library for another target board, such as the Arduino Mega only the MCU and processor speed need to be changed accordingly.
Now that the library has been constructed, it is time to demonstrate how to link it against a real program, capable of being uploaded to the Arduino. Consider the following trivial example:
Notice that the usual Arduino setup() and loop() functions have been maintained for convenience, but the real work is now being done by the main function familiar to any programmers of c or C++. The header file WProgram.h is needed to reference the Arduino function such as pinMode(13,OUTPUT) or whatever. The function init is needed by wiring.c in its setup. The main function can be reused like that and so there is very little difference between a .pde and a file like this. This program flashes the LED on pin 13 like the classic blink.pde example sketch that comes with the Arduino. To compile program, link it to the library created before, and upload it to the Arduino, the following makefile can be used:
This time the default target depends on the build and upload targets, which means these will be called in turn. The build target depends on the file Test.hex. This is the hex file which will be uploaded to the Arduino over the USB to serial interface. This target depends on Test.elf, which is the actual gcc compiled object file. The target Test.elf, depends on Test.cpp and any other objects specified in the variable OBJECTS.
Objects specified in the OBJECTS variable will be compiled by the rule:
Which is very similar to before. Once all objects are compiled, Test.elf is compiled using the rule:
There are a few differences to the gmake rule described previously. First all the dependencies are pulled in using the automatic variable $^ since there can be multiple inputs. And secondly the -c flag is NOT specified since it is desired to link the compiled executable to the dependent libraries. The compiler generates the compiled code and then tries to find any functions used. The places to look is specified using the flag -larduino, which causes the compiler to search for a library called libarduino.a. More generally, in this context, -lX causes the compiler to search for libX.a. The compiler searches for libarduino.a in several default locations as well as any specified using instances of the -L option, which in this case is set to ~/lib, which is the directory the library was stored in earlier. Finally, observe that the CFLAGS variable is different:
The only difference is the addition of a few new flags:
Once the Test.elf this rule is done, and the dependency for the Test.hex rule is satisfied, so it executes:
This straightforward rule invokes avr-objcopy to convert Test.elf into a format that can be recognised by the Arduino's bootloader (which accepts new programs via the USB to serial interface). The flag -o IHEX specifies the intel hexadecimal format accordingly. Note that there are several options available to avr-objcopy which are claimed to reduce hex file size, which can be useful since these boards don't have a lot of FLASH. I tried a few but it didn't seem to make any difference, so I haven't bothered with them. That's the build target finished, which means that make goes onto the upload target.
The upload target flashes the hex file Test.hex to the Arduino using avrdude:
The flags are as follows
Most of the values from these options I obtained from boards.txt but one could also refer to the MCU manufacturers specifications. Notice that I've also included some code to choose the port based on platform (only FreeBSD and Linux):
Different boards require different options, and FTDI devices can appear on a different port such as /dev/ttyUSB0. But you should easily be able to figure out how to work with any of the common boards given this framework, so I'll leave the generalisation as an exercise for the reader... (or in otherwords, I'm too lazy since I don't need it for my purposes). Incidentally, laziness gets you everywhere in programming. Whilst one cannot be so lazy so as to do nothing, having taken up the challenge, the lazy programmer takes the most efficient and direct path to completion. The lazy programmer also produces speedy code, because he cannot be bothered to wait long for his programs to start, and he hates waiting for them to finish doing their jobs.