The library follows the internal organisation of the micontroller (or MCU for MicroController Unit, or simply the microcontroller), so let's look at that first.
There are 4 main components inside the microcontroller :
These two other components are also useful to make your life easier :
DMA
module for more details)With the exception of the RAM and the JTAG/SWD controller, every one of these hardware components is matched by a dedicated module in the library. The CPU is grouped with some other central features inside the Core
module.
The library itself is only a folder. Its only dependencies are a working toolchain (as explained in the Getting Started tutorial), GNU make, and optionally OpenOCD. You will usually want to copy it inside your project folder (yes, there will be a copy for every project, but don't worry : it's very small and this will allow you to customize the library).
The two main parts of the library are the modules' code (inside sam4l/
) as explained above, and the Makefile.
A Makefile is some sort of script that is read and executed by the GNU make tool. It allows you to define settings and rules that can be used to compile your code or do about anything you want. All the operations defined by the library (such as compiling your program, flashing it into the microcontroller, …) are implemented with these rules. When compiling code, a Makefile is also smart : it will only compile the files that have been modified to save time.
This may sound complicated, but it is actualy really easy. For example, in order to compile your code, just go to your project directory with a terminal and type make
. That's it!
In order to execute a rule, type make rule
. Omiting the argument will execute the default rule which is, you guessed it, compiling the program. Here is the complete list of available rules :
make clean
: clear every compiled files, to make sure the next make
will re-compile everything from scratch (if you have modified the Makefile, make sure to do this to avoid weird behaviour)make openocd
: start a pre-configured OpenOCD session and connect to your JTAG/SWD adapter (you will usually do that in another terminal in background)make flash
: flash your program into the microcontroller using OpenOCDmake autoflash
: flash your program into the microcontroller by automatically launching a temporary OpenOCD sessionmake erase
: erase the microcontroller's Flash memory (i.e. the current program inside the microcontroller)make pause
: pause the microcontroller executionmake reset
: reset the microcontrollermake debug
: start a debug session with GDBmake flash-debug
: shortcut to flash and immediately start a debug sessionmake bootloader
: compile the bootloadermake flash-bootloader
: flash the bootloader into the microcontroller using an existing OpenOCD sessionmake autoflash-bootloader
: flash the bootloader into the microcontroller by automatically starting a temporary OpenOCD sessionmake debug-bootloader
: start a debug session aimed at the bootloadermake flash-debug-bootloader
: shortcut to flash the bootloader into the microcontroller and immediately start a debug sessionmake codeuploader
: compile the codeuploadermake upload
: flash your program using the bootloader using USB by defaultmake upload-usb
: flash your program using the bootloader using USBmake upload-serial
: flash your program using the bootloader using a Serial portYou probably won't have to modify the main library Makefile. As explained in the Getting Started tutorial, all you need to do is to create a Makefile in your project directory, set up a few settings, and include the main Makefile.
All the modules are built around the same structure : a namespace shared between a .cpp
file containing the code and a .h
header describing the module interface. In order to call a function inside a module namespace, use the following notation : Module::function()
, for example Core::init()
. Feel free to look inside the modules code if you are curious about how something works!
What are all those const uint32_t OFFSET_...
inside the header files?
At the lowest level, the program interacts with the microcontroller using registers. A register is a small memory space (usually 32-bit (4-byte) long), that have a defined address and a documented content. You can access it exactly like if it was in RAM, even though it is actually located inside a peripheral. Reading and writing registers is the most common way to control peripheral components. For example, there are write-only registers that allow you to issue commands, read-only registers that give you details about the controller status, and read-write registers with configuration settings.
All these OFFSET_...
are register addresses. They are used internally by the module in order to operate, but you usually won't need to mess with them.
If you want more information about the registers, the datasheet is the place to go. You can also find more information in the Hacking section in the documentation of each module.
libtungsten can be used for projects ranging from very simple to very complex, and for this reason, some functions are aimed at more advanced users. Throughout the documentation, modules and functions follow a simple color code so that you know quickly if this is the feature you are looking for :