Getting started - Polidea/cmake-nRF5x GitHub Wiki
Installing the required tools and nRF5 SDK
Opposite to IDEs, nRF5 CMake is not a standalone application but rather a solution which integrates multiple separate tools in order to create a lightweight and versatile development environment. Follow these steps to make sure you have these installed:
1. Install CMake
Proceed to CMake Download page, download the distribution specific to your OS and install it. You should use CMake version 3.14 or newer. Also, make sure to add cmake
to your PATH variable.
2. Download the GNU Arm Embedded Toolchain
Go to GNU Arm Embedded Toolchain Downloads page and download the toolchain meant for your operating system. We recommend using a non-installer version. Extract the archive and save it under an easy accessible path.
3. Install the J-Link Software and Documentation Pack
Go to J-Link Software and Documentation Pack page, download the package meant for your operating system and install it.
4. Download the nRF5 SDK
Go to nRF5 SDK page, download it and extract it under a path that is easy to access.
IMPORTANT: Be sure to download a version that is supported by the nRF5 CMake. Currently supported versions are 15.3.0 and 16.0.0.
5. Install nRF Command Line Tools
Download the nRF Command Line Tools. Its Windows distribution requires you to run an installer. Make sure that nrfjprog
is added to your PATH variable.
6. Install the build system you want to use (optional)
As you're probably aware, in general, CMake does not build software directly but rather generates input files for build systems like make
or Ninja. If the desired build system is already installed on your machine, you can skip this step.
IMPORTANT: This step is required if you want to use nRF5 CMake on Windows since the popular make
is obviously not natively available. When builing on Windows, we recommend using Ninja. Go to Ninja page, download a Windows binary and add it to your PATH. Alternatively, you can use make
available as a package in Cygwin or MSYS2 projects.
Setting up a project and building it with nRF5 CMake
Using the nRF5 CMake, we're going to set a up a simple project based on one of the official examples from the nRF5 SDK - the BSP Example. We're going to build it for the nRF52 DK board with the nRF52832 SoC (PCA10040).
Setting up a project structure
Let's start by creating a basic project directory structure. Create a new empty directory where we will keep the project files. Let's call it nrf5_example_bsp
.
Instead of writing code from scratch, copy the main.c
file of the BSP Example found in your nRF5 SDK, in the examples/peripheral/bsp
directory, to the main directory of your project.
Alongside source code, we're going to need 2 more files specific to the nRF52-DK board (PCA10040):
- Linker script -
examples/peripheral/bsp/pca10040/blank/armgcc/bsp_gcc_nrf52.ld
- SDK configuration file -
examples/peripheral/bsp/pca10040/blank/config/sdk_config.h
In your project directory, create a config
subdirectory and pca10040
within it and copy those 2 files there.
In the end, your project directory should look like this:
nrf5_example_bsp
├── config
│ └── pca10040
│ ├── bsp_gcc_nrf52.ld
│ └── sdk_config.h
└── main.c
Adding nRF5 CMake to your project
Once you have all the tools and the SDK installed, you need to add the nRF5 CMake to your project. The simplest way to do that would be to download the ZIP archive of this repository, extract it and only copy the cmake
folder from the main repo directory into your project. The cmake
directory contains all the files you're going to need if you want to setup a new project from scratch.
Alternatively, you can add the nRF5 CMake to your project as a git submodule. The advantage of doing that is the fact that you can conveniently keep the nRF5 CMake up-to-date. This time however, we're just going to copy the cmake
directory into our project directory. After that, it is supposed to look like this:
nrf5_example_bsp
├── config
├── cmake
└── main.c
Writing CMakeLists.txt
Create a CMakeLists.txt
in the main project directory. It's going to define what we're going to build.
We start by declaring a minimum CMake version required and by declaring the project:
cmake_minimum_required(VERSION 3.14)
project(nrf5_example_bsp LANGUAGES C ASM)
Next, we need include the nrf5
CMake module which will provide the essential functionality of the nRF5 CMake to the project but before we can do that, we need to append the cmake
subdirectory to the CMAKE_MODULE_PATH
so that CMake can find it:
list(APPEND CMAKE_MODULE_PATH "cmake")
include("nrf5")
After that, we can create a CMake target representing our binary executable. We specify the only source file of this project (main.c
):
add_executable(nrf5_example_bsp "main.c")
Next, we need to call the nrf5_target()
function available in the nrf5.cmake
script on our executable target. This function configures some necessary link options (e.g. linker script), adds the required sources like startup and system files and also adds additional targets to the build system that will allow us to conveniently program our board once the project is built:
nrf5_target(nrf5_example_bsp)
Finally, we have to link the libraries from the nRF5 SDK which are used in this example. In nRF5 CMake every such library is represented as CMake target. Normally, you would link such targets to your executable by using the standard target_link_libraries()
CMake clause. However, we're going to use an alternative nrf5_target_link_libraries()
provided by the nRF5 CMake. The reason for doing that will be explained later.
As you can see in the main.c
source file and by inspecting the SDK configuration file (sdk_config.h
), the BSP Example utilizes the following libraries:
- Application error (
app_error
) - commonly used for error-handling - Board Support Package (
bsp
) - for handling button presses and LED indication - Logger with UART backend (
nrf_log
) - for printing log entries on UART
We can link them to our executable target by writing:
nrf5_target_link_libraries(nrf5_example_bsp PRIVATE
nrf5_app_error
nrf5_log
nrf5_log_backend_serial
nrf5_log_backend_uart
nrf5_log_default_backends
nrf5_bsp
)
As you can see, we actually need some additional Logger-related libraries i.e. nrf5_log_backend_serial
and nrf5_log_default_backends
but that's due to the example configuration in sdk_config.h
and because of how the nRF Logger is structured.
The complete CMakeLists.txt
for our project should look like this:
cmake_minimum_required(VERSION 3.14)
project(nrf5_example_bsp LANGUAGES C ASM)
list(APPEND CMAKE_MODULE_PATH "cmake")
include("nrf5")
add_executable(nrf5_example_bsp "main.c")
nrf5_target(nrf5_example_bsp)
nrf5_target_link_libraries(nrf5_example_bsp PRIVATE
nrf5_app_error
nrf5_log
nrf5_log_backend_serial
nrf5_log_backend_uart
nrf5_log_default_backends
nrf5_bsp
)
Generating input files for the build system
Once our CMakeLists.txt
is complete, we are ready to call CMake on it in order to generate input files for the build system. This will require us to pass multiple parameters (CMake cache entries) to properly configure the project. Because of that, we recommend that you create a script for that purpose. Depending on your operating system, it can be a Shell or a Batch script.
This is a sample Shell script to call CMake for this project:
cmake \
-DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi.cmake \
-DTOOLCHAIN_PREFIX=~/tools/gcc-arm-none-eabi \
-DNRF5_SDK_PATH=~/tools/nRF5SDK160098a08e2 \
-DNRF5_BOARD=pca10040 \
-DNRF5_SOFTDEVICE_VARIANT=blank \
-DNRF5_SDKCONFIG_PATH=config/pca10040 \
-DNRF5_LINKER_SCRIPT=config/pca10040/bsp_gcc_nrf52.ld \
-G "Unix Makefiles" \
-B build \
-S .
We're going to break down CMake variables defined in this script as well as other CMake options:
-
-DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi.cmake
Here we specify so called toolchain file which is a CMake script that specifies which compiler, linker and other tools should be used when building the project. It also specifies the set of flags and other options that should be used when calling those programs. Configuring those is necessary due to the fact that when creating a binary for an nRF52 SoC, we are cross-compiling. You can read more about cross-compilation and toolchains in CMake documentation: cmake-toolchain(7).
nRF5 CMake provides a
cmake/arm-non-eabi.cmake
toolchain file for the GNU Arm Embedded Toolchain. -
-DTOOLCHAIN_PREFIX=~/tools/gcc-arm-none-eabi-9-2019-q4-major-win32
Once CMake knows which compiler, linker and tools to use, it has to know where those are located on the local machine. Here, we specify a path to the GNU Arm Embedded Toolchain we previously downloaded and installed.
-
-DNRF5_SDK_PATH=~/tools/nRF5SDK160098a08e2
We specify a path to the nRF5 SDK. In this case, we're using version 16.0.0 of the SDK.
-
-DNRF5_BOARD=pca10040
We specify the nRF52 board we're building the binary for. In this case it's the nRF52-DK with the nRF52832 SoC. If you have a custom board or you simply do not wish to specify the board, you can use the
NRF5_TARGET
variable, for instance:-DNRF5_TARGET=nrf52832_xxaa
. -
-DNRF5_SOFTDEVICE_VARIANT=blank
When using nRF5 CMake, you need to specify which SoftDevice is used by your application e.g. S132, S140 etc. Our application does not use SoftDevice at all so we're setting this variable to
blank
. -
-DNRF5_SDKCONFIG_PATH=config/pca10040
We are setting the directory where the SDK configuration file (
sdk_config.h
) is located. -
-DNRF5_LINKER_SCRIPT=config/pca10040/bsp_gcc_nrf52.ld
We specify the linker script to use by the linker. Among other things, it defines the address ranges of Flash and RAM memories.
-
-G "Unix Makefiles"
This CMake option tells CMake which build system to generate input files for. If you're using Windows, please specify
"Ninja"
instead (assuming you have it installed). -
-B build
This specifies the CMake build directory where CMake will put the generated input files. If it doesn't exists, it will be created.
-
-S .
This specifies the CMake source directory where the
CMakeLists.txt
file is located.
Run the script you created. You should now have the input files in the build
directory.
Building the project
Once you have the input files for your build system, open a terminal, navigate to the build
directory and run the build system by typing make
or ninja
depending on which system you chose. If everything went alright, you should see a short summary with the size of your binary:
$ ninja
[43/43] Linking C executable nrf5_example_bsp
text data bss dec hex filename
30824 288 2208 33320 8228 nrf5_example_bsp
Programming the board
If you have the nRF52-DK board with the nRF52832 SoC, connect it to the computer and from the build
directory of your project, build the flash
target e.g. make flash
or if you're using Ninja: ninja flash
.
This should load the binary into your board. You can now verify if the example is functioning correctly.
What's the next step?
Hopefully, you have learned how to build a simple nRF52 project using the nRF5 CMake. As an exercise, you can create a configuration of this project for the PCA10056 board with the nRF52840 board.
We encourage you to take a look at the CMakeLists.txt
we provide for the official examples from the nRF5 SDK so they can be built using the nRF5 CMake.
In our opinion the best way to learn nRF5 CMake is learning-by-doing so consider using it for your next nRF52 project or perhaps, try to migrate your existing project.