This tutorial explains how to install the toolchain, build your first project and flash it to the microcontroller. If you don't have a board yet, you might still find the Under the hood section interesting. Otherwise, you will need to decide whether to use a JTAG/SWD adapter or the bootloader in order to flash your code into the microcontroller.
Before making your first LED blink, you need to get a few things in order to compile your code, install it on the microcontroller, and optionally debug it. Let's go!
First, you need a compiler, or more precisely, a cross-compiler. This is because a compiler is usually designed to compile code for the same architecture it is running on, which is not the case here : we want to compile on the computer, but for the microcontroller. Because this compiler is quite complex and usually packaged with other tools, it is often called a toolchain.
The toolchain we'll be using is the one provided by Atmel/Microchip which you can download here (look for the latest version of the "ARM GNU Toolchain (32-bit)" for your OS). It is based (with a few modifications) on the official GNU ARM Embedded Toolchain. Don't forget to take a look at the Release Note on Microchip's download page, which gives interesting information about the tools provided by the toolchain.
Once the toolchain is downloaded, unpack it somewhere convenient. Take note of this location, you will need it later.
OpenOCD is a tool which is able to connect to a JTAG/SWD adapter and take control of a microcontroller. We will use it as the interface to flash and debug some code into the microcontroller.
Download links and installation instruction are available on the official website. On Linux, most of the time it is available in the repositories and you only need to type sudo apt-get install openocd
or something similar.
Finally, you need a board to program. As explained here you have multiple choices on the matter, but whatever you choose, the procedure will be similar.
If you don't want to use the bootloader, you'll also need a JTAG/SWD adapter in order to communicate with the microcontroller and flash your program. Look at the JTAG/SWD vs Bootloader tutorial for more details.
Alright, you should now have everything you need and you can start creating a new project. Let's begin with the mandatory initiation rite : making an LED blink (yeah!).
Start by creating a new directory blink
for this project and point your text editor to this directory. Now, create two new files : blink.cpp
and Makefile
, and fill them with the code available in the Blink example page (you can download them using the links below and wget for example) :
blink.cpp
[download] wget http://libtungsten.io/static/code/examples/blink/blink.cpp
#include <core.h> #include <gpio.h> int main() { // Init the microcontroller Core::init(); // Define the pin on which the LED is connected // On Carbide, this is the red LED const GPIO::Pin led = GPIO::PA00; // Enable this pin in output to a HIGH state GPIO::enableOutput(led, GPIO::HIGH); while (1) { // Turn the LED on GPIO::setLow(led); // Wait for half a second Core::sleep(500); // Turn the LED off GPIO::setHigh(led); // Wait for half a second Core::sleep(500); } }
Makefile
[download] wget http://libtungsten.io/static/code/examples/blink/Makefile
NAME=blink BOOTLOADER=true # Available modules : adc dac eic gloc i2c spi tc trng usart. # Some modules such as gpio and flash are already compiled by default # and must not be added here. MODULES= # The toolchain's bin/ path, don't forget to customize it. # If this directory is already in your PATH, comment this line. TOOLCHAIN_PATH=/opt/arm-none-eabi/bin/ # Include the main lib makefile include libtungsten/Makefile
First, we include two headers from the library : core.h
and gpio.h
. They ensure that the fonctions from the Core
and the GPIO
modules respectively will be available.
Core
module is a bit special : it is not related to a peripheral, but instead gives access to the features of the ARM Cortex M4 core itself. These features are actually not specific to Atmel, they are common to all chips embedding a Cortex M CPU. More info in the Core module documentation.GPIO
module (General Purpose Input Output), as its name implies, takes care of the input/output pins of the microcontroller. We will use it to control the pin connected to the LED. More info in the GPIO module documentation.Inside the main()
, we first need to initialize some basic stuff inside the microcontroller using Core::init()
. When using libtungsten you should always call this function at the beggining of your main()
(when using the Carbide
module this is done for you inside Carbide::init()
). Then we define the pin we want to act on, using the Pin
type defined in the GPIO
module (therefore GPIO::Pin
). A pin is defined by its port and its number, here PA00
(first pin on port A). The enableOutput()
fonction is used in order to define this pin as an output and control the LED.
Since returning from the main()
doesn't make any sense in the context of a microcontroller, we need an infinite loop where we will put the code which will constantly be executed. Here, this code is simple : turning the LED on and off. For this purpose, we need two things : a function for setting the desired level on the pin, and a function to wait for a certain amount of time. That would be respectively GPIO::set()
and Core::sleep()
. Here we use two helper functions instead of the first : GPIO::setLow()
and GPIO::setHigh()
. They behave exactly the same way and only make the code easier to read and quicker to write.
setHigh()
turns the LED off and setLow()
turns the LED on. This is because the other side of the LED is connected to Vcc and the pin is used as a current sink, so it needs to be pulled to GND in order to let the current flow. The reason for doing this is that the microcontroller is not designed to supply lots of currents on its pins, however, it can tolerate to let a bit more current pass to ground. Anyway, if you plan to drive more powerful devices than a LED (e.g. a motor), remember to use a dedicated circuit for this (such as based on a transistor for example).That's it for the code : we only need to initialize the microcontroller, define the pin on which the LED is connected as an output, and indefinitely turn it on, wait, turn it off, wait, repeat.
The Makefile
is really simple : the only mandatory parameter is NAME
, which indicates the name of your project. This must match the name of your main .cpp
source file. Another important parameter is MODULES
, which defines the list of modules that need to be compiled with your project. The list of available modules and their descriptions is detailed in the Reference but the most important and useful ones, such as core
and gpio
, are already compiled by default. Here, we do not use any optional module so this can be left empty. If you want to flash the program directly using a JTAG/SWD adapter, comment the BOOTLOADER=true
line to compile the code accordingly. If the toolchain's bin
directory is not in your PATH
, you need to tell the Makefile where to find it with the TOOLCHAIN_PATH
parameter (this is what you noted earlier — make sure you don't forget to append a trailing slash). When your code starts to grow, it is a good idea to divide it into separacted modules, which you can ask the library to compile for you using USER_MODULES
. Finally, the main Makefile
containing all the necessary rules is included from the library.
TOOLCHAIN_PATH
parameter, the one given here probably won't work for you. Either specify the full path to the arm-none-eabi/bin/
directory, or remove/comment this line if this directory is already in your PATH
environment variable.Finally, we need a copy of the library in our project directory (as we saw in the Makefile). You can either clone it from the Git repository :
git clone git@github.com:Foalyy/libtungsten.git
Or download an archive : download.
Your project directory should now look like this :
blink/
blink.cpp
Makefile
libtungsten/
Makefile
sam4l/
...
Compiling your project is quite easy : just type make
in a terminal. This will generate two files : blink.elf
, the compiled code in the ELF format, and blink.hex
, generated from the .elf
and containing the code data ready to be written into the microcontroller.
There are two ways to flash your program into the microcontroller : either using the bootloader and codeuploader, or with OpenOCD and a JTAG/SWD adapter. Look at the JTAG/SWD vs Bootloader tutorial for more details.
Using the bootloader is simpler and cheaper, because you don't need an external adapter. All you need to do is :
make upload
You can find more details in the bootloader documentation.
This method is a bit more complex because it requires OpenOCD, however don't worry, the Makefile takes care of most of the work for you. All you need to do is :
make openocd
. This will start a new GDB instance which will connect to your JTAG/SWD adapter and, through it, to the microcontroller. Keep this terminal open in the background.make flash
: this will connect to the OpenOCD instance, read the content of the .hex
file and write it into the microcontroller's flash memory. When the process is done (this can take up to one or two minutes), the microcontroller will automatically reboot and start executing your code.make autoflash
, which will spawn a temporary OpenOCD session and use it to flash your code. However, you still need to use the procedure above if you want to perform other operations such as make debug
or make reset
.
Congratulations, the LED is now blinking !
You can now take a look at the documentation overview and the other examples to see what this microcontroller can do!