Quick’n’easy Cmake tutorial

Introduction

Cmake is a tool used to generate build files. Build files are what is used to build a project. An example of a build file would be a makefile. You can then use make tool (or mingw32-make on Windows) to build the project and generate an executable. Cmake is not a build tool itself, but rather a build file generator. If you type:

cmake -G

You will get a list of all supported build file generators. These generators are essentially all build file formats that Cmake supports. But now we are getting ahead of ourselves. Lets first install Cmake and then we can talk more about what it is and how to use it.

Installing Cmake

Cmake installation is very easy on Windows. If installing with installer just make sure that you check the option which automatically adds Cmake to your PATH variable. Of course you can do it yourself, but for anyone that doesn’t know how, I would recommend to select this automatic option.

I won’t go into further details about this since there are a lot of tutorials available on internet on this topic. What is important for this tutorial is that you have Cmake installed, and somewhere in your PATH, so we can invoke it from terminal.

If you don’t want to add it to your path, that is fine also, you just have to write the whole path to the executable every time you want to run it, but you probably already know that. 🙂

So quickly check if you have it installed:

cmake --version

And we can move on.

Required tools

The tools you will also need for completing this tutorial are:

  • C and C++ compilers
  • some build system supported with Cmake, would recommend make (mingw32-make on Windows), or Ninja

You can find instructions on how to install these tools easily, so we won’t go through that.

Basic Cmake file

Without any further ado, I present to you a basic Cmake file, the CMakeLists.txt:

cmake_minimum_required(VERSION 3.12.2)

# If you dont have CC and CXX global variables set you can set compilers manualy.
set(CMAKE_C_COMPILER "D:/MinGW/bin/gcc.exe")
set(CMAKE_CXX_COMPILER "D:/MinGW/bin/g++.exe")

# Define project name.
project (cmake_tut)

include_directories("./mydir1/"
                    "./mydir2/")

# Adding sources to compilation.
add_executable(cmake_tut ./main.c
                         ./mydir1/test.c
                         ./mydir2/test.cpp)

And that is really that! Very simple and short. The name of this cmake file is very important and it should be CMakeLists.txt .

If you have compiler path set globally you don’t even need :

set(CMAKE_C_COMPILER "D:/MinGW/bin/gcc.exe")
set(CMAKE_CXX_COMPILER "D:/MinGW/bin/g++.exe")

These two lines. But I would recommend to have them, so that your compilers are explicitly defined. This way you always know what you are using to compile your code.

You should always define your compilers before project() function because otherwise it might not work.

include_directories() function is used to tell the compiler where to look for include files (.h), while add_executable() function determines the executable name and source files (.c and .cpp) to compile.

There is a github repository with the example project that uses this cmake file. You can find it here.

Basic command line usage

Now that we have our basic Cmake file ready, lets see how to use it. I will be using already mentioned repository as an example.

First things first, we need to change compiler paths. Find gcc and g++ executables on your machine and replace D:/MinGW/bin/gcc.exe and D:/MinGW/bin/g++.exe in the CMakeLists.txt file with full paths to compilers on your machine.

set(CMAKE_C_COMPILER "path/to/your/gcc.exe")
set(CMAKE_CXX_COMPILER "path/to/your/g++.exe")

Paths need to be enclosed in quotation marks, use “/” not “\” and if on Linux or Mac you won’t have to include .exe extension.

Ok, now that that is out of the way we need to create a build folder in this example repository. You can call it “build”. Open build folder and start a terminal in it. Type one of the following, depending on the build tool that you use:

#MinGW Makefiles
cmake -G "MinGW Makefiles" ..
#Unix Makefiles
cmake -G "Unix Makefiles" ..
#Ninja
cmake -G "Ninja" ..

For a complete list of available generators just type:

cmake -G

If everything went smoothly you should now have all your build files generated in the build folder and by typing:

mingw32-make

or

make

or

ninja

The project will get compiled, and executable with the name of cmake_tut should be generated in the build folder. If you run the executable you should see the following printout:

The program doesn’t do anything smart, it just showcases that both .c and .cpp files get compiled and used in the same project.

Conclusion

And that concludes the very basic Cmake tutorial! This very basic file template can serve as a foundation to build on top off. Cmake has a ton of cool stuff that are not covered in this short introduction, but that is for some other time. Also, it would be interesting to see how we can build a project for some embedded platform, like STM, but those are all topics for some other time.

Getting started with nRF5 SDK

This is aimed as a very basic tutorial on using the Nordics SDK, version 15.2.0 . There are already a lot of good guides directly from Nordic so this is just a quick brief on how to get things set up and running as fast as possible.

Although I’ll be using version 15.2.0 these steps should work with other versions as well.

Tools

You will need:

Download and install these two compilers, and download the SDK.

If you are wondering why MinGW is needed, it is because it contains mingw32-make.exe tool. This tool is used for building the examples of the SDK. Every example contains a makefile, which is a file that describes how each example should be built. Makefile lists all the necessary source files, as well as include paths needed for successfully building the example. It also contains compiler and linker paths, precompiled libraries, project specific defines and flags, as well as list of commands that invoke compiler and linker, and other tools if necessary.

mingw32-make.exe is a windows specific make tool, and if using some other operating system it is needed to download make tool for that system.

Preparing the SDK

Go ahead and download nRF5 SDK from Nordics website. After download is complete you will have a zipped SDK files. Extract it and I would suggest renaming it to e.g. :

nRF5_SDK_15.2.0

Now if you installed all the necessary tools go to:

…/nRF5_SDK_15.2.0/examples/ble_peripheral/ble_app_blinky/pca10040/s132/armgcc

Start a terminal window in this folder. This is just one of the examples that comes with SDK. All of these examples are ready to build, all you have to do is start a make tool… or at least this should be the case. Lets see what happens. Type in terminal:

mingw32-make

Now you might have succeeded, or you might have failed. If I do this make command it fails for me because I don’t have compiler in the path where SDK expects me to have it. This is the error message:

process_begin: CreateProcess(NULL, "C:/Program Files (x86)/GNU Tools ARM Embedded/6 2017-q2-update/bin/arm-none-eabi-gcc" --version, ...) failed.
Cannot find: 'C:/Program Files (x86)/GNU Tools ARM Embedded/6 2017-q2-update/bin/arm-none-eabi-gcc'.
Please set values in: "E:/nRF5_SDK_15.2.0/components/toolchain/gcc/Makefile.windows"
according to the actual configuration of your system.
../../../../../../components/toolchain/gcc/Makefile.common:129: *** Cannot continue.  Stop.

The great thing is that this error message provides me with a solution to the problem. We have to look in:

E:/nRF5_SDK_15.2.0/components/toolchain/gcc/

When we open this folder there are two files of interest:

  • Makefile.posix
  • Makefile.windows

These files define compiler path, version and name prefix for Windows and posix systems(Linux, macOS). Since I use Windows i will edit Makefile.windows . Path to compiler on my sistem is:

D:/gcc_arm/7_2017-q4-major/bin/

But this may differ for you. The important thing is to give a path that contains these files:

ARM GCC compiler tools

This is not the complete list, but it should give you an idea what files should be present. Also if on Windows make sure you use ‘/‘ instead of Windows default ‘\‘ for folder separation.

We can also set the correct version number, but it is not essential, everything will work without it. To find out compiler version we can use command:

arm-none-eabi-gcc --version

In my case it is “7.2.1“.

Building the example

Now finally lets build the example. If on Windows type in terminal once again:

mingw32-make

and hit ENTER. If using Linux or macOS your make tool can probably be invoked with simply typing:

make

and hit ENTER. Regardless of OS now compiler should start compiling all of the source files specified in the example makefile, and output should be something like this:

mkdir _build
cd _build && mkdir nrf52832_xxaa
Assembling file: gcc_startup_nrf52.S
Compiling file: nrf_log_backend_rtt.c
Compiling file: nrf_log_backend_serial.c
Compiling file: nrf_log_backend_uart.c
Compiling file: nrf_log_default_backends.c
Compiling file: nrf_log_frontend.c
Compiling file: nrf_log_str_formatter.c
Compiling file: app_button.c
Compiling file: app_error.c
Compiling file: app_error_handler_gcc.c
Compiling file: app_error_weak.c
Compiling file: app_scheduler.c
Compiling file: app_timer.c
Compiling file: app_util_platform.c
Compiling file: hardfault_implementation.c
Compiling file: nrf_assert.c
Compiling file: nrf_atfifo.c
Compiling file: nrf_atflags.c
Compiling file: nrf_atomic.c
Compiling file: nrf_balloc.c
Compiling file: nrf_fprintf.c
Compiling file: nrf_fprintf_format.c
Compiling file: nrf_memobj.c
Compiling file: nrf_pwr_mgmt.c
Compiling file: nrf_ringbuf.c
Compiling file: nrf_section_iter.c
Compiling file: nrf_strerror.c
Compiling file: system_nrf52.c
Compiling file: boards.c
Compiling file: nrf_drv_clock.c
Compiling file: nrf_drv_uart.c
Compiling file: nrfx_clock.c
Compiling file: nrfx_gpiote.c
Compiling file: nrfx_power_clock.c
Compiling file: nrfx_prs.c
Compiling file: nrfx_uart.c
Compiling file: nrfx_uarte.c
Compiling file: main.c
Compiling file: SEGGER_RTT.c
Compiling file: SEGGER_RTT_Syscalls_GCC.c
Compiling file: SEGGER_RTT_printf.c
Compiling file: ble_advdata.c
Compiling file: ble_conn_params.c
Compiling file: ble_conn_state.c
Compiling file: ble_srv_common.c
Compiling file: nrf_ble_gatt.c
Compiling file: nrf_ble_qwr.c
Compiling file: utf.c
Compiling file: ble_lbs.c
Compiling file: nrf_sdh.c
Compiling file: nrf_sdh_ble.c
Compiling file: nrf_sdh_soc.c
Linking target: _build/nrf52832_xxaa.out
   text    data     bss     dec     hex filename
  26032     532    2452   29016    7158 _build/nrf52832_xxaa.out
Preparing: _build/nrf52832_xxaa.hex
Preparing: _build/nrf52832_xxaa.bin
DONE nrf52832_xxaa

And that is it! Now we have nrf52832_xxaa.hex that can be flashed to nRF52832 chip. I’ll show how to flash it in another post.

Bonus: faster build time

The process of building this simple example on my machine took about half a minute. It is not too long, but it would be nice if it could go faster. Compiling of different sources can be easily parallelised, so if you have a multi-core processor, and you probably do, then compiling with multiple cores should speed things up. Make tool doesn’t do this by default, for some reason, so we have to explicitly enable it. It can be done by adding a -j option to make command, for example:

mingw32-make -j

or posix:

make -j

With including -j option my build time drops to only 8 seconds! That is more than 3x speed up! But it will depend on your hardware, so it could be less… or more!

Downloading softdevice to PCA10059 USB Dongle Board Using nrfutil

In the previous post I have described how to download firmware files to PCA10059 board that do not require a softdevice to run. This time I will expand on that and show how easy it is to add a softdevice to the mix.


Unfortunately it seems that you can’t merge your application .hex file and softdevice .hex file. You have to provide them separately to the nrfutil tool.

To demonstrate firmware download we will be using 
example ble_app_blinky from Nordic SDK located in SDK_root/examples/ble_peripheral/ble_app_blinky/ folder. In order to make your life easier I have bundled app .hex and softdevice together:

Unpack this file to a folder of your choice and start a terminal window in that folder. I am using PowerShell on MS Windows 10. Enter the following command into a terminal:

nrfutil pkg generate --hw-version 52 --sd-req 0x00 --sd-id 0xA9 --softdevice .\s140_nrf52_6.0.0_softdevice.hex --application-version 1 --application .\nrf52840_xxaa.hex zip.zip

This will generate a package zip.zip. Now we have to program our board with the following command:

nrfutil dfu usb_serial -pkg zip.zip -p COM6 -b 115200

And with that if you open nRF Connect on your mobile device and scan for the available devices you will see the Nordic_blinky device. You can even connect to it and turn the on-board led ON/OFF and read the button state.

Updating the application firmware without softdevice

From now on you can package only the application without packaging the softdevice along with it. The command is similar to the one used when softdevice wasn’t used, but with one simple change. First download this altered ble_app_blinky .hex file or use you own:

Unpack it and start terminal window in the same window as the unpacked .hex file. Once opened, type in the following command:

nrfutil pkg generate --hw-version 52 --sd-req 0xA9 --application-version 1 --application .\altered_nrf52840_xxaa.hex zip.zip

The small modification I mentioned is the –sd-req argument. Instead of 0x00 we are now using 0xA9. This is because 0xA9 is the s140_nrf52_6.0.0 softdevice ID. Every softdevice, and every version have their ID. You can find a list of those IDs and a lot more info on nrfutil here.

From here I would recommend creating your own program script, so you don’t have to manualy put in these two commands.

Thank you for reading!

Programming the New Nordic Chip NRF52840, and USB Dongle Board PCA10059 Featuring it

PCA10059 USB dongle featuring a NRF52840 microcontroller

Recently Nordic released a new board/dongle featuring their new microcontroller NRF52840. Although this board is not feature rich, like a full development kit, it still provides enough periphery for some basics, like blinky example and button example. There is also a possibility to do USB development because the board comes with a giant USB connector which makes it very portable, very simple to get started, and the most important thing, looks really nice 🙂 .

Of course the PCA10059 board has an on-board antenna which can be used for, among other things, Bluetooth. This makes the board great for Bluetooth development, especially if you need to have multiple boards interconnect with one another. You can just plug in as many as you need to USB ports, program them, and watch what happens. This is mostly what I’ll be doing with them.

Programming the PCA10059 USB dongle board

Back side of the PCA10059 board. Few test points and a 10-pin tag connect programming interface can be seen

Programming of the board can be done through a USB interface, which is the most convenient way to program it in my opinion. Just to mention that there is also a 10-pin tag connect interface that can be used for programming, but we won’t be discussing that here.

In order to be able to program the PCA10059 board through USB, some sort of bootloader has to be used. Nordic provides such a bootloader on every PCA10059 board so we don’t have to worry about it.

General memory layout

General structure of NRF52840 flash memory

The above image shows how the flash memory will generally look like. It can be seen that softdevice(SD) occupies the bottom part of flash pool, from address 0x1000 to somewhere around 0x26000. Start address will probably always be the same, but the end address could change with new revisions of softdevice. After softdevice comes the application part of the memory. The application can potentially occupy all of the empty space all the way up to the bootloader. Bootloader starts at 0xE0000 and consumes the rest of the available flash memory above this address. Bootloader part is always in the memory, but softdevice doesn’t have to be. If application doesn’t use any of the softdevice functionality, then softdevice can be omitted. More on this later.

PC side of things

Bear in mind that all of this is tested on MS Windows 10, with powershell(PS). It should work on other OSes, but I can’t give any guarantees.

With that out of the way, there are, according to Nordic, two ways to program the PCA10059 board. GUI or CLI. GUI way is by using the nRF Connect for Desktop app. CLI way is by using the nrfutil tool. Unfortunately I wasn’t able to make nRF Connect for Desktop work, so we will be using the CLI. However Nordic states that driver for PCA1059 board comes with nRF Connect for Desktop, so you will probably have to install that anyway.

Now lets install nrfutil tool. It is a python package that works only with python 2.7. Make sure you install it for python 2.7 and not for python 3. If you don’t have python, install it. You can find it here. Now check if pip is available, and check where is it:

pip -V
or as in my case
pip2 -V

If you are on windows output should be something like:

pip 9.0.1 from d:\python27\lib\site-packages (python 2.7)

And if pip command is not from python 2 folder, it will look something like:

pip 9.0.3 from d:\python36\lib\site-packages (python 3.6)

You can see either by the folder or by text in parentheses for which python version your pip command will install packages. If you don’t know what is the command to run the correct pip, you can just cd into python 2 directory and start pip from there like this:

cd d:/python27/Scripts
./pip.exe -V

If in folder run the following command, and if not just replace ./pip.exe with your pip command:

./pip.exe install nrfutil

Check if nrfutil got installed:

nrfutil --help

Now that everything is setup we are ready to start programming our board!

Downloading a simple blinky example to PCA10059 board

We will now finally download a blinky example .hex file from SDK to our board. If you have SDK you can go and find blinky example .hex file there, or just use this one:

Unzip this file and open a terminal window in a directory where you downloaded it. Type the following in terminal:

nrfutil pkg generate --hw-version 52 --sd-req 0x00 --application-version 1 --application .\blinky_pca10059_mbr.hex zip.zip

This will create zip.zip file in the folder you are currently in. You will probably get a big warning in your terminal 😀 . That is because we are not using keys to encrypt this newly generated .zip , and potentially someone could tamper with our precious .hex . We are currently not worried about that, but if you are going to use this in production, you should think about using keys to encrypt this .zip . We are not going to cover that here.

We will not go into detail about the options, just in short:

  • hw-version depends on the microcontroller that is on board
  • sd-req is the code for expected softdevice on board. This information was really hard to find, you can put 0x00 to basically say that you don’t care about this parameter
  • application-version is any number you want
  • application is the .hex file containing your program, but without softdevice

Now that our package is ready the only thing left to do is to finally program the board. Go ahead and insert it into a USB port on your computer. After you insert it two things can happen. You could see the LD2 slowly pulsing red, or you could see something else. If you see LD2 pulsing red that means that the board is in bootloader mode and is ready to accept our program. If LD2 is not pulsing red, then just reset it with a reset button that is located on the left side of the board.

PCA10059 board from above. On the left side is the reset button, which is pushed on the side, as the arrow shows. LD2 is located on the right side of the board, near the USB connector

After a reset, LD2 should start pulsing red, and you should have a new device connected on your computer. If on windows you can look it up on device manager:

NRF52840 DFU visible in device manager as a COM6 port

You need to remember what COM port is assigned to the board because we will need it now. Go to the terminal and type in this command:

nrfutil dfu usb_serial -pkg zip.zip -p COM6 -b 115200

Replace the COM6 with your port number, or if on Linux or Mac with a corresponding file path. When you hit enter, you should see a progress bar, and a short message “Device programmed.” after the programming is complete. All leds on board should be turning on one after another, and then turn off at the same time, and repeat indefinitely. If you see that then congratulations! The board is programmed successfully!

To find out how to flash a softdevice along with the application .hex file click here.