Arduino DUE Makefiles
The Arduino IDE is good for starters as it hides some of the complexity of C++ development: notably the linker and project management. It is intended for beginners so they can have a LED blinking in 2 seconds. And you can extend it using libraries based on C++ classes, which is great.
Correction, this SEEMS great and it would be if it were implemented right. Of course, the editor is somewhat limited compared to matured platforms such as Sublime text, or the typical IDE editor.. However, that’s not the real problem here. It’s good that it’s simpler than those powerhouses, otherwise beginners would run away. The real problem lies with the project management.
Normally, you’d have a project setup which defines dependencies between classes. Mostly, the user sets this up manually or semi-automatically (for instance using CMake or GNU Autotools). The major advantage of this is that no unnecessary code is ever compiled or
linked.. and when changing only 1 file, only that file is recompiled and linked.
What Arduino IDE does wrong for the DUE (and I’m talking about the recent version 1.5.6):
- It recompiles everything. Even the core libraries.
- It cannot reliably tell which libraries to include or not.
- Verification of upload is always done, even when you explicitly set a flag not to. This takes ages.
Especially the second point is deadly. It causes error messages about missing
#include statements, while these shouldn’t have to be there at all. Not only this but an unnecessary library can be linked, causing the maximum code size to be exceeded!
The other points are primarily annoyances of ours. But compilation and upload speed IS important. Uploading big programs to the due can take 30 seconds or so. And verification doubles that. But verification is only required when you’re performing diagnostics. Forced recompilation of core libraries adds another 10 seconds. Better make some tea.
But back to the deadly second point. Assume we have the following piece of sketch code:
Foo and Bar are our libraries and we disabled their inclusion by using preprocessor directive
#if 0. Much to our bemusement, this didn’t do anything. The libraries were still linked and the upload was still as big as before. The first thing to do in Twilight Zone episodes like this is to wipe your glasses. Correct your monitor angle and try again.. But no.. Nothing was wrong with our code.
What then, was the problem? What happened under water in the depths of the IDE was that our sketch got scanned for library includes. But the scanning was implemented in such an improper way you’d actually be surprised that it works at all. It’s just a “grep”.. A line by line search for the #include directive. It doesn’t take into account the context at all. Instead it should do parse the C++ code, but it doesn’t. We don’t know why. But we also shouldn’t care. After witnessing this it was clear that the IDE just wasn’t designed with bigger programs in mind.
Finding a bug in a big program can be a very time consuming task, and this could make things a lot worse, especially when maximum program size is exceeded for no reason. Added to this is that fact that the IDE also seems to think #include’s are missing, like mentioned before, was really the limit. Let’s just do it properly, shall we?
Our solution is just a typical makefile. Makefiles are ancient, their syntax a bit cryptic, but they always get the job done and they never change. Learn once, and use the rest of your life.
The start was to check the IDE’s compiler output:
Arduino -> Preferences -> Show verbose output during compilation
This produces a huge mountain of GCC and GPP command lines. We’ll spare you the actual raw text here and analyze it for you. What is there to do when building a DUE sketch:
- Build the Arduino core library. This features implementations of the well known digitalWrite(), analogWrite(), and everything else an Arduino sketch uses. Eventually we intend to completely replace this with our own code (as it’s a million times more efficient). But for now it has to stay. The process is hard to generalize. Every piece of source can be in a different subdirectory so we ended up with 100 lines of Makefile code.
Sources get compiled to object files (.o) and these get archives into core.a.
- Then there’s the non-core libraries. For instance, SPI.cpp. Compilation proceeds similar to parts of the core.a lib but are not archived. Your own libs should be compiled in a similar fashion. Note: For some reason syscalls_sam3.c should also be present as a separate object and not (only) be present in the core.a.
- The ELF should now be built from the sketch source and all the objects and the core.a should be linked.
- The ELF should be dumped to a .bin using objcopy. This is used with “
-O binary” mode, meaning it strips all symbols and relocation info.
- Finally the .bin should be uploaded using
bossac. This is what we came up with:
# erase the DUE!.. done by opening comms @ 1200 bps
stty -f /dev/cu.usbmodemfd121 raw speed 1200
# actually upload the binary
bossac -p cu.usbmodemfd121 -e -w -b test_ili9340.bin
- Serial output can be monitored with a combination of
sudo cu -l /dev/cu.usbmodemfd121 -s 115200
..And just kill the screen Using ctrl-a k when we’re done.
As you can see it’s not that crazy. Of course, loads of definitions of include paths, GNU tool locations and compiler and linker flags are necessary. For your convenience I’ve put them in a ready-to-go Makefile attached to this post (it’s a display driver test):