Tips & Tricks

Using nRF52 with CLion

Are you interested in embedded development? In this guest blog post Nick Brook, founder at NRB Tech, shows how to use CLion for nRF52.

Nick Brook IoT consultant and founder at NRB Tech.
Nick founded NRB Tech in 2018 to build useful and sustainable IoT products through a combination of in-depth hardware, software and user experience expertise.

Nordic’s nRF52 series of Bluetooth Low Energy System-on-Chips (SoCs) are versatile and widely used in IoT products, supporting many different applications. Nordic provides a very complete SDK which contains many example applications and good documentation, making it easy to get started and build IoT devices. Some of the example projects available in the SDK include:

  • Beacons
  • Blood pressure monitoring
  • Cycling speed and cadence
  • Glucose monitoring
  • Heart rate
  • HID Keyboard + Mouse
  • Running speed and cadence
  • Many other technical example projects

These provide great starting points to develop a Bluetooth Low Energy device of your own.

At NRB Tech we use the Nordic nRF52 series SoCs to build products such as AirTurn’s range of foot pedals, which act as a keyboard, mouse, MIDI device, or use a proprietary protocol.

Nordic board

Bluetooth products are often a combination of a low power device and an App. It’s useful to be able to share code across those platforms, so that you can write and test the code just once for both the firmware and the App.

CLion is a great IDE for C and embedded development, and CMake provides many advantages such as the ability to create libraries that can be shared across platforms and to integrate those libraries into an application easily. However, the nRF52 SDK does not support development with CMake/CLion out of the box, but the Nordic nRF5 Mesh SDK does use CMake – although it does not support external projects.

So we built nRF5-cmake-scripts, which uses the mesh SDK scripts and provides CMake functions to add your own targets. It also makes adding the DFU bootloader very simple and provides many macros for adding the nRF5 SDK libraries to your project as you need them. Let’s set up an example project, using CLion as our IDE.

Full setup

  1. Setting up.
  2. Creating a nRF52 project.
  3. Debugging in CLion.

Setting up

This tutorial assumes you are using an nRF52 DK. If you are using different nRF5 hardware, you’ll need to modify the variables at the top of the root CMakeLists.txt later in the tutorial.

You will need the following dependencies:

  • Nordic command line tools (nrfjprog and mergehex) by Nordic Semiconductor – Wrapper utility around JLink.
    • This also includes the JLink installer – install it.
  • Python 3.
  • Nordic nrfutil by Nordic Semiconductor – a utility for generating DFU packages.
    • After installing Python, install using pip install nrfutil.
  • ARM GNU Toolchain by ARM and the GCC Team – a compiler toolchain for embedded ARM chips.
    • On a Mac, this can be installed with homebrew:
      brew tap ArmMbed/homebrew-formulae
      brew install arm-none-eabi-gcc
      
    • On other platforms, you can download it from the GNU-ARM toolchain page.
  • git – A version control system.
  • CMake – A build tool.
  • On Mac and Linux, your PATH is probably automatically configured correctly. But if some executables cannot be found, then edit your ~/.profile file and add export PATH="<additional paths, semicolon separated>;$PATH".
  • On Windows you will need to ensure that all binaries are in your PATH (instructions for editing). Your PATH additions might look something like this:
    C:\Program Files (x86)\GNU Tools Arm Embedded\9 2019-q4-major\bin
    C:\Program Files\Nordic Semiconductor\nrf-command-line-tools\bin
    %USERPROFILE%\AppData\Local\Programs\Python\Python37\Scripts
    C:\Program Files\Git\cmd
    C:\Program Files (x86)\SEGGER\JLink
    

Creating a nRF52 project

Once the dependencies have been installed, clone the base project in a terminal/command window to set up the project structure:

git clone --recurse-submodules https://github.com/NRB-Tech/nRF5-cmake-scripts-example-base.git .

Run a script to get the project ready for your own use (on Windows, run in git bash by right clicking in the directory > “Git Bash here”):

./cleanup.sh

Then, copy the example CMakeLists.txt as recommended in the nRF5-cmake-scripts readme:

cmake -E copy nRF5-cmake-scripts/example/CMakeLists.txt .

Note: You may also need to edit some of the variables in this file for your platform, such as by setting NRFJPROG, MERGEHEX, NRFUTIL and PATCH_EXECUTABLE manually if they are not in your PATH.

At this point, you can open the project in CLion. Build the download target to fetch the dependencies.

Using the Terminal tab in CLion, copy over some files from an SDK example project:

cmake -E copy toolchains/nRF5/nRF5_SDK_16.0.0_98a08e2/examples/ble_peripheral/ble_app_template/pca10040/s132/armgcc/ble_app_template_gcc_nrf52.ld src/gcc_nrf52.ld
cmake -E copy toolchains/nRF5/nRF5_SDK_16.0.0_98a08e2/examples/ble_peripheral/ble_app_template/pca10040/s132/config/sdk_config.h src/

Add a file src/main.c, and then add some source code. Here we add some simple code to log a message.

#include "nrf_log_default_backends.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"

int main(void) {
    ret_code_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();

    NRF_LOG_INFO("Hello world");
    while(true) {
        // do nothing
    }
}

Create an src/app_config.h file to override some of the default configurations in sdk_config.h:

#define NRF_LOG_BACKEND_RTT_ENABLED 1 // enable rtt
#define NRF_LOG_BACKEND_UART_ENABLED 0 // disable uart
#define NRF_LOG_DEFERRED 0 // flush logs immediately
#define NRF_LOG_ALLOW_OVERFLOW 0 // no overflow
#define SEGGER_RTT_CONFIG_DEFAULT_MODE 2 // block until processed

We are going to include the DFU bootloader too, so we need to generate keys. In the terminal/command prompt:

nrfutil keys generate keys/dfu_private.key
nrfutil keys display --key pk --format code keys/dfu_private.key --out_file keys/dfu_public_key.c

Now we need to create a file src/CMakeLists.txt to build our targets:

set(NRF5_LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/gcc_${NRF_FAMILY})

# DFU requirements
# List the softdevice versions previously used, or use FALSE if no previous softdevices
set(PREVIOUS_SOFTDEVICES FALSE)
# Set the location to the DFU private key
set(PRIVATE_KEY ${CMAKE_CURRENT_SOURCE_DIR}/../keys/dfu_private.key)
set(PUBLIC_KEY ${CMAKE_CURRENT_SOURCE_DIR}/../keys/dfu_public_key.c)
# Set the App validation type. [NO_VALIDATION|VALIDATE_GENERATED_CRC|VALIDATE_GENERATED_SHA256|VALIDATE_ECDSA_P256_SHA256]
set(APP_VALIDATION_TYPE NO_VALIDATION)
# Set the Soft Device validation type. [NO_VALIDATION|VALIDATE_GENERATED_CRC|VALIDATE_GENERATED_SHA256|VALIDATE_ECDSA_P256_SHA256]
set(SD_VALIDATION_TYPE NO_VALIDATION)
# The bootloader version (user defined)
set(BOOTLOADER_VERSION 1)
# The DFU version string (firmware version string)
set(DFU_VERSION_STRING "${VERSION_STRING}")

# Set the target name
set(target example)

# add the required libraries for this example
nRF5_addLog()
nRF5_addSeggerRTT()
nRF5_addAppError()

# include files
list(APPEND SOURCE_FILES main.c)
list(APPEND INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")

nRF5_addExecutable(${target} "${SOURCE_FILES}" "${INCLUDE_DIRS}" "${NRF5_LINKER_SCRIPT}")

# make sdk_config.h import app_config.h
target_compile_definitions(${target} PRIVATE USE_APP_CONFIG)

# Here you can set a list of user variables to be defined in the bootloader makefile (which you have modified yourself)
set(bootloader_vars "")

# add the secure bootloader build target
nRF5_addSecureBootloader(${target} "${PUBLIC_KEY}" "${bootloader_vars}")
# add the bootloader merge target
nRF5_addBootloaderMergeTarget(${target} ${DFU_VERSION_STRING} ${PRIVATE_KEY} ${PREVIOUS_SOFTDEVICES} ${APP_VALIDATION_TYPE} ${SD_VALIDATION_TYPE} ${BOOTLOADER_VERSION})
# add the bootloader merged flash target
nRF5_addFlashTarget(bl_merge_${target} "${CMAKE_CURRENT_BINARY_DIR}/${target}_bl_merged.hex")
# Add the Bootloader + SoftDevice + App package target
nRF5_addDFU_BL_SD_APP_PkgTarget(${target} ${DFU_VERSION_STRING} ${PRIVATE_KEY} ${PREVIOUS_SOFTDEVICES} ${APP_VALIDATION_TYPE} ${SD_VALIDATION_TYPE} ${BOOTLOADER_VERSION})
# Add the App package target
nRF5_addDFU_APP_PkgTarget(${target} ${DFU_VERSION_STRING} ${PRIVATE_KEY} ${PREVIOUS_SOFTDEVICES} ${APP_VALIDATION_TYPE})

# print the size of consumed RAM and flash - does not yet work on Windows
if(NOT ${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows")
    nRF5_print_size(${target} ${NRF5_LINKER_SCRIPT} TRUE)
endif()

Reload the CMake project (Tools | CMake | Reload CMake Project), then we are ready to build and run our example. First, build the START_JLINK_RTT target to open an RTT console, then build the flash_bl_merge_example target. You should see the “Hello world” log output in the RTT console! From here you can add source code and include SDK libraries with the macros provided in nRF5-cmake-scripts/includes/libraries.cmake.

To debug this code, we need to do a little more work to set up CLion.

Debugging in CLion

First, we need to add a toolchain to use the ARM debugger client. In CLion Preferences/Settings | Build, Execution, Deployment | Toolchains, create a new toolchain named arm-none-eabi and set the Debugger to arm-none-eabi-gdb:

Toolchains configuration
Then we need to use that toolchain for our CMake profile. In CLion Preferences/Settings | Build, Execution, Deployment | CMake set the Toolchain to arm-none-eabi:
CMake Profile

Next we just need to create the build configuration for debugging. On the top right of the CLion window, click the configuration drop down and choose Edit Configurations. Then Add a new Embedded GDB Server configuration:

Embedded GDB server config

Configure as shown:
Configuration details

  1. Set a name.
  2. Share through VCS to share this configuration with other CLion users of your project.
  3. Select the flash_bl_merge_<target> target.
  4. Select your executable.
  5. Set “Download executable” to “None.”
  6. Set ‘target remote’ args to tcp:localhost:2331.
  7. Set GDB Server to
    • Mac: /usr/local/bin/JLinkGDBServer.
    • Windows: C:\Program Files (x86)\SEGGER\JLink\JLinkGDBServerCL.exe.
    • Or the appropriate path for your system.
  8. 1. Set GDB Server args to -device nrf52 -strict -timeout 0 -nogui -if swd -speed 1000 -endian little -s.

Now we can build or debug this target, which will build the firmware and bootloader then flash to the device. When debugging, if your breakpoint is not hit when the debugger starts, just press the reset button and continue:
Reset and resume

You can also view the state of peripherals on the device when debugging. When debugging, click the Peripherals tab and click “Load .svd file”:
Load peripherals

Browse to toolchains/nRF5/nRF5_SDK_16.0.0_98a08e2/modules/nrfx/mdk and select the .svd file for your hardware (for nRF52832 use nrf52.svd), then select the peripherals you want to monitor. When the debugger is paused you will then be able to inspect the values of peripheral registers:
Peripherals

This tutorial was originally posted at NRB Tech blog.

Now you are ready to give it a try!

DOWNLOAD CLION

Update: Part II – Tutorial: Using nRF52 With nRF Connect SDK, CMake, and CLion

image description