nRF52840 Advanced Development With the nRF5 SDK

Pages
Contributors: jimblom
Favorited Favorite 6

Introduction

Are you an nRF52840 user ready to take a leap away from the comforts of the Arduino IDE? Feeling limited by CircuitPython's support? Maybe it's time to set up an armgcc-based development environment of your own and dive into Nordic's nRF5 Software Development Kit (SDK).

nRF5 SDK Icon

This tutorial will cover download and setup of every software tool -- armgcc, nrfutil, and the nRF5 SDK -- you need for nRF52 development. It focuses on developing for the SparkFun Pro nRF52840 Mini Breakout.

SparkFun's nRF52840 ships with a USB-Serial DFU bootloader. So this tutorial will put extra focus on modifying the SDK to seamlessly bootload code via USB.

Pros and Cons

As with any development approach, there are benefits and disadvantages to building a homebrew programming environment.

Pros

  • Lots of example applications! The list of examples in Nordic's nRF5 SDK is exhaustive. Everything from blinky to BLE heart-rate monitors are are included in the SDK.
  • Nordic's documentation is equally-comprehensive and well-organized. Just take a peek at the nRF5 SDK Documentation.
  • Access to more advanced features that may not have Arduino or CircuitPython support built out: NFC, ANT, capacitive-touch sensing, FreeRTOS just to name a few
  • Flexibility to use your favorite IDE/editor. Whether you'd prefer to use Eclipse, Sublime, Notepad++, or, as we'll demonstrate in this tutorial Visual Studio Code, your options are nearly endless!

Cons

  • We won't lie. Setting up a from-scratch toolchain and IDE can be a daunting task. You'll encounter errors that may seem to be impossibly isolated to your setup and your computer. If you're stuck, feel free to reach out in this tutorial's comments section.
  • We're not in Arduino- or Python-land anymore. Developing with the nRF5 SDK is hard if you've never done it before. Whether you want to blink an LED or set up your nRF52 board as a BLE central device, there are a lot of new functions, macros, and programming paradigms to familiarize yourself with. Luckily there are a lot of examples!
  • The SDK isn't tailor-made for the SparkFun nRF52840 Mini Breakout. For most examples, you'll need to make some Makefile (and potentially linker-script) modifications to load it onto your nRF52840 using the pre-programmed bootloader.

Required Materials

This tutorial is primarily designed to support the SparkFun Pro nRF52840 Mini Breakout -- especially the section covering SDK modifications to enable programming-via-bootloader.

SparkFun Pro nRF52840 Mini - Bluetooth Development Board

SparkFun Pro nRF52840 Mini - Bluetooth Development Board

DEV-15025
$32.50
1

That said, the general setup parts of this tutorial can be made applicable to any nRF52840 -- or even nRF58232 or nRF51 -- board. Nordic's nRF52840 Development Kit (DK) or the nRF52840 Dongle are great boards that should work with most of this tutorial. Beyond an nRF52840 board, you shouldn't need anything else besides a micro-B USB cable to attach between the device and your computer.

Suggested Reading

If you haven't read through it already, we recommend following along with our SparkFun Pro nRF52840 Mini Hookup Guide before continuing on. This tutorial will familiarize you with the hardware features of the board as well as the intricacies of the DFU/UF2 bootloader.

SparkFun Pro nRF52840 Mini Hookup Guide

November 29, 2018

A hardware overview and hookup guide for the SparkFun Pro nRF52840 Mini -- a breakout for Nordic's impossibly cool Bluetooth/ARM Cortex M4 SoC.

Installing the ARM Toolchain & Nordic Tools

All of the nRF5 SDK's examples, components, and libraries are written in C. To compile those source files you'll need a C compiler for the ARM architecture. There are a handful of ARM C compilers out there, but we'll be focusing on the open-source ARM GCC toolchain in this tutorial.

In addition to the compiler, you'll need software that can take that compiled code and upload it to a Nordic nRF5 chip via either the bootloader or the SWD interface. Nordic's nRF5x Command Line Tools include nrfjprog, which can be used to upload via the SWD debug interface. adafruit-nrfutil -- a modified version of Nordic's nrfutil is used to upload via DFU bootloader.

Compiler -- ARM GNU Tools

The GNU Arm Embedded Toolchain is a collection of open-source compilers, build, and debug-tools for ARM microcontrollers -- including the ARM Cortex-M4 that make up a major chunk of the nRF52840 SoC. These tools include:

  • GNU C/C++ Compiler -- A C/C++ compiler that turns your C/C++ code into machine code that the ARM can understand.
  • Binary tools -- Tools that can link your compiled code together, compile assembly code, and perform a variety of file-conversion operations.
  • Debugging tools -- GDB, the GNU Project Debugger, can be used to step through your code and view memory contents in real-time.

The ARM GNU tools are free to download and use. To get the latest version, visit the Arm GNU download's page.

On Windows, for example, click the "Download" link next to "Windows 32-bit\File: gcc-arm-none-eabi-7-2018-q2-update-win32.exe (82.98 MB)".

Where to download ARMGCC

Having a hard time seeing? Click the image for a closer look.

Windows users can run through the install wizard. By default, the ARM GNU tools will be installed in a directory like "C:\Program Files (x86)\GNU Tools ARM Embedded\7 2018-q2-update" (the last bit will vary by version of the tools).

Windows ARM GNU Tools install

Mac and Linux users will need to download and extract the ARM GNU tools to a preferred location on your computer, for example ${HOME}/opt.

SWD Programming Tools -- nRF5x Command Line Tools

Note: Broken Links

It appears that the Nordic links have changed. For additional assistance locating the resources on the Nordic website, users can try checking out the Nordic DevZone.

In order to upload new, compiled applications to your nRF52840 you'll need to install a handful of Nordic command-line tools, including nrfjprog.

To download these tools, visit the Installing the nRF5x Command Line Tools page of Nordic's information center.

The software is available for Windows, Linux 32-bit, Linux 64-bit, and macOS. Select your appropriate flavor from the pulldown towards the bottom of the page.

On Windows, run the installer and follow the instructions in the wizard. On Linux and Mac, extract the .tar file somewhere on your computer -- you may need to add the Nordic tools directory to your $PATH environment variable.

DFU Bootloader Tools -- adafruit-nrfutil

Nordic's nrftuil is a Python-based tool for packaging device firmware updates (DFU) and updating an nRF chip with that package over either serial or Bluetooth. adafruit-nrfutil is derived from Nordic's original version of the software. It updates nrfutil to be Python 3-based, and provides DFU support for a variety of boards equipped with their nRF52 bootloader.

adafruit-nrfutil is used to update code on your nRF52840 with the serial bootloader.

To install adafruit-nrfutil, you'll first need to download and install Python 3. (If you have Python 2.7 installed, you'll still need Python 3 to install adafruit-nrfutil.)

Once you have Python 3 installed, the easiest way to install adafruit-nrfutil is with PyPI:

language:python
pip3 install --user adafruit-nrfutil

If you use a different method to install adafruit-nrfutil, don't forget to add adafruit-nrfutil too your PATH environment variable!

Setting Up the nRF5 SDK

Nordic's nRF5 SDK is a thorough collection of the component drivers, libraries, and tools that are required to build applications for the nRF52840. The files included in this package provide the foundation for most nRF5 projects and products.

Download/Extract the SDK

Download the latest nRF5 SDK from Nordic's nRF5 SDK product page. As of this tutorial's writing, the latest version is 15.2.

The SDK is bundled as a ZIP file. Extract the contents of the ZIP to a desired location on your computer. As a Windows example, we'll use C:\nRF5. Your extracted directory should look something like this:

nRF5 install in C:\nRF5

Configuring Your Toolchain

Once you've extracted the SDK, you'll need to configure it to point to the gcc toolchain you've just installed.

Navigate to the Nordic SDK, then components/toolchain/gcc. In this directory you'll find three Makefiles, one of which will need to be modified to point to your GNU build tools. The file you modify depends on your operating system:

  • Windows: Makefile.windows
  • Mac and Linux: Makefile.posix

Both of these files look pretty similar. They're both looking for directory locations for the GNU tools installation, version, and prefix. Modify the values of GNU_INSTALL_ROOT and GNU_VERSION to match that of the ARM GNU tool directory and versions you just installed.

As an example, here's what I've modified the file to look like on my Windows machine:

GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/7 2018-q2-update/bin/
GNU_VERSION := 7.3.1
GNU_PREFIX := arm-none-eabi

To get the version you can use your terminal or CMD to navigate to the "bin" folder of your GNU ARM tools install, and type: arm-none-eabi-gcc --version.

checking arm gcc version

Having a hard time seeing? Click the image for a closer look.

In the picture above, for example, our version is 7.3.1.

Adding Boards File

The Nordic SDK includes support for a variety of Nordic development boards, but not the SparkFun nRF52840 Mini Breakout. To add support, we'll need to begin by adding a board definition file to the components/boards directory of the nRF5 SDK.

The board-support files include definitions for buttons, LEDs, and common serial pins (UART, SPI, and I2C). Our board definition file can be found in the nRF52840 GitHub Repository. Download that file (click the "Raw" link, and Save As..), or click the link below:

Place that file in the components/boards directory.

You'll also need to modify components/boards/boards.h, adding the following before #elif defined(BOARD_CUSTOM):

language:c
#elif defined (BOARD_SPARKFUN_NRF52840_MINI)
  #include "sparkfun_nrf52840_mini.h"

nRF5 SDK board mod

Having a hard time seeing? Click the image for a closer look.

This will allow us to add a BOARD_SPARKFUN_NRF52840_MINI define when compiling example code to pull in sparkfun_nrf52840_mini.h when compiling.

Setting Up a VS Code Environment

Before you get too deep into building examples and applications for the nRF52840, it may be a good idea to set up a development environment around the nRF5 SDK. An IDE can help you navigate the codebase and easily interact with it via a console.

This tutorial will explain how to set up a Visual Studio (VS) Code environment. A lot of this content should be portable to setting up similar environments in IDE's or editor's like Eclipse, Notepad++, or Sublime if you prefer other tools, though.

Open Folder in VS Code

Open up a new VS Code window, then navigate to File > Open Folder.... In the explorer-prompt that opens, navigate to your nRF5 SDK, then find examples/peripheral/blinky.

Open vs code in example/peripheral/blinky

The "blinky" example simply blinks an LED on your development board -- it's a great way to verify that your toolchain is correctly set up.

Set Up c_cpp_properties.json

After opening the folder, you should see a file-explorer on the left-hand side of the window. Double-click main.c to open the main source file.

open main.c file

Having a hard time seeing the code? Click the image for a closer look.

One of the most powerful features of IDE's like VS Code is this ability to trace back through #include to provide the definition of functions or constants. To help the IDE navigate these files, you should define a file named c_cpp_properties.json in a .vscode directory in your blinky example. This file can be automatically created by pressing CTRL+SHIFT+P, then beginning to type c/cpp: edit configurations.

ctrl+shift+p and typing c/cpp: edit configurations

Having a hard time seeing the code? Click the image for a closer look.

Your c_cpp_properties.json file should be automatically created. This file includes paths to include files and macro definitions that may alter the compilation of a source file. Here's an example you can base your file off. You may need to alter the path of nrfSDK, so it matches that of your nRF5 SDK's root path.

language:json
{
    "env": {
        "nrfSDK": "C:\\nRF5\\nRF5_SDK_15.2.0_9412b96"
    },

    "configurations": [
        {
            "name": "ARMGCC",
            "includePath": [
                "${workspaceFolder}/**",
                "${nrfSDK}/components/**",
                "${nrfSDK}/components",
                "${nrfSDK}/components/libraries/experimental_memobj",
                "${nrfSDK}/components/libraries/experimental_section_vars",
                "${nrfSDK}/modules/nrfx/mdk",
                "${nrfSDK}/modules/nrfx/hal",
                "${nrfSDK}/components/libraries/balloc",
                "${nrfSDK}/components/libraries/experimental_log",
                "${nrfSDK}/components/libraries/experimental_log/src",
                "${nrfSDK}/components/libraries/delay",
                "${nrfSDK}/integration/nrfx",
                "${nrfSDK}/components/libraries/bsp",
                "${nrfSDK}/components/drivers_nrf/nrf_soc_nosd",
                "${nrfSDK}/components/libraries/strerror",
                "${nrfSDK}/components/boards",
                "${nrfSDK}/components/toolchain/cmsis/include",
                "${nrfSDK}/modules/nrfx",
                "${nrfSDK}/components/libraries/util",
                "${nrfSDK}/components/libraries/fifo",
                "${nrfSDK}/components/libraries/uart",
                "${nrfSDK}/integration/nrfx/legacy",
                "${nrfSDK}/components/libraries/delay",
                "${nrfSDK}/modules/nrfx/drivers/include",
                "${workspaceFolder}/sparkfun/blank/config",
                "${workspaceFolder}/sparkfun/blank"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE",
                "BOARD_SPARKFUN_NRF52840_MINI",
                "BSP_DEFINES_ONLY",
                "CONFIG_GPIO_AS_PINRESET",
                "FLOAT_ABI_HARD",
                "NRF52840_XXAA",
                "DCONFIG_NFCT_PINS_AS_GPIOS"
            ],
            "compilerPath": "C:\\Program Files (x86)\\GNU Tools ARM Embedded\\7 2018-q2-update\\bin\\arm-none-eabi-gcc.exe",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "clang-x64"
        }
    ],
    "version": 4
}

If everything parses correctly, you should be able to right-click on any #include, function, or constant definition and select "Go to Definition (F12)" to navigate to the source file that declares that bit of code.

GIF: Go to Definition

Having a hard time seeing the code? Click the image for a closer look.

This is a super-powerful tool, especially if you're just beginning to familiarize yourself with the nRF5 SDK and the API's it provides.

Building Blinky for the SparkFun nRF52840 Mini Breakout

There's one last bit of setup to do before you can compile and upload the Blinky sketch to your SparkFun nRF52840 Mini Breakout. We need to modify the Makefile so it builds for our board and we need to ensure that, when the code is linked, it doesn't try to overwrite the SoftDevice or the bootloader's home in memory.

Get Used to It

These modifications will need to be made for every example you test out in the nRF5. Don't worry! You'll fall into a rhythm as you continue test out examples in the SDK.

Add a SparkFun_nRF52840_Mini Target

The standard "blinky" example includes build options for the Nordic nRF52832 (pca10040) and nRF52840 (pca10056) development kits. We want to add a build option for the SparkFun nRF52840 Mini Breakout. The easiest way to do this is to copy and duplicate the pca10056 folder and rename it to something like sparkfun_nrf52840_mini.

Copying pca10056 to sparkfun_nrf52840_mini

Once you've copied pca10056 to sparkfun_nrf52840_mini, click into your new folder, then click into the blank and armgcc folders. Then open Makefile.

The most obvious change we need to make to this file is the board definition. Look for CFLAGS and ASMFLAGS definitions for -DBOARD_PCA10056, and modify them to -DBOARD_SPARKFUN_NRF52840_MINI:

language:c
CFLAGS += -DBOARD_SPARKFUN_NRF52840_MINI

and

language:c
ASMFLAGS += -DBOARD_SPARKFUN_NRF52840_MINI

(Make sure to delete any ... += -DBOARD_PCA1056, or similar lines, to ensure the Nordic nRF52840 development kit is not enabled.)

Adding DFU-Programming Targets

We can also use this makefile to call adafruit-nrfutil and load code via serial DFU. To do that, add the following targets to the end of your Makefile:

language:c
dfu-package: $(OUTPUT_DIRECTORY)/nrf52840_xxaa.hex
    @echo Packaging $<
    adafruit-nrfutil dfu genpkg --sd-req 0xFFFE --dev-type 0x0052 --application $<  _build/dfu-package.zip

bootload: $(OUTPUT_DIRECTORY)/nrf52840_xxaa.hex dfu-package
    @echo Flashing: $<
    adafruit-nrfutil --verbose dfu serial --package _build/dfu-package.zip -p $(SERIAL_PORT) -b 115200 --singlebank --touch 1200
Tab and Space Pains

Copying the above text will probably paste four spaces instead of tabs. If you get a compilation error like:

Makefile:305: *** missing separator. Stop.

replace the four spaces before each of the four indented lines with an actual tab.

These two targets provide easy, command-line access to nrfutil's DFU-packaging and uploading features.

Revising the Linker Script's Memory Organization

One last big change you'll need to make is to the memory organization. This example doesn't know we have a bootloader living at address 0x00 in flash.

Open blinky_gcc_nrf52.ld. Then replace the MEMORY section of the linker script with the below memory origin/length combinations:

language:c
MEMORY
{
  FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xce000
  RAM (rwx) :  ORIGIN = 0x20000000, LENGTH = 0x40000
}

That will move the flash origin from 0x0 to 0x26000 and limit the available space accordingly. (Still nearly 1MB -- 892kB -- to play with!)

SparkFun Pro nRF52840 Mini Flash Memory Map

As you delve further into nRF52840 development, it may help to know a bit about how the flash memory is mapped. Here's a quick rundown:

DescriptionStart AddressEnd AddressSize (KB)
Bootloader Settings0x000FF0000x000FFFFF4
Master Boot Record Params0x000FE0000x000FEFFF4
Bootloader0x000F40000x000FDFFF40
Application Code0x000260000x000F3FFF824
SoftDevice0x000010000x00025FFF148
Master Boot Record0x000000000x00000FFF4

For more information on the nRF52840's memory map with the S140 programmed, check out Nordic's S140 Memory resource map and usage documentation.

Build the Example

Whew! That's it. To compile the code simply type:

language:makefile
make

Or, if you want to build and program in one swift command:

language:makefile
make bootload SERIAL_PORT=COM7

Make sure your nRF52840 board is in bootloader mode (double-tap reset). (Also make sure to replace "COM7" with your nRF52840's DFU serial port.)

gif showing added build packages and compile

Having a hard time seeing the code? Click the image for a closer look.

BLE Blinky Example

Blinking an LED's fine and all, but it barely scratches the surface of the nRF52840's capabilities. How about we add some Bluetooth to the mix?

Navigate back into your nRF5 SDK's examples folder and find ble_peripheral/ble_app_blinky. Open it up just as you did the previous blinky example.

Once open, just as with the last example, duplicate the pca10056 folder and rename the new folder sparkfun_nrf52840_mini.

And, you guessed it, you'll need to modify the "Makefile" and "blinky_gcc_nrf52.ld" files in "sparkfun_nrf52840_mini/s140/armgcc" to add a "bootload" target, and to modify the Flash origin/size. In the linker, "ld," file only modify the FLASH size, leave the RAM alone.

One last modification I'd recommend making is to the main.c file at the top of the directory. This is the main source file that manages all of the configuration and initialization. Towards the top of the file -- beginning around line 72 -- are LED pin definitions. I recommend setting each of the LED definitions to BSP_BOARD_LED_0. You can also set the BLE device name if you'd like. That section would end up looking something like this:

language:c
#define ADVERTISING_LED                 BSP_BOARD_LED_0                         /**< Is on when device is advertising. */
#define CONNECTED_LED                   BSP_BOARD_LED_0                         /**< Is on when device has connected. */
#define LEDBUTTON_LED                   BSP_BOARD_LED_0                         /**< LED to be toggled with the help of the LED Button Service. */
#define LEDBUTTON_BUTTON                BSP_BUTTON_0                            /**< Button that will trigger the notification event with the LED Button Service */

#define DEVICE_NAME                     "SparkFun_nRF52840"                         /**< Name of device. Will be included in the advertising data. */

Setting each LED to BSP_BOARD_LED_0 kind of abuses the code, but we do what we can with a single on-board LED. It allows us to both tell if the board is connected, and toggle the LED from the app.

Speaking of the app...

Using nRF Connect to Control the Board

To test this example sketch, you'll need another device -- either computer or smartphone -- connected to your nRF52840. Nordic provides a free, handy test tool, nRF Connect for Mobile, that's available for both Android or iOS. We'll demonstrate how to control your nRF52840's LED and monitor the button using this sketch. So go download the app!

Once downloaded, open the app and begin searching for nearby Bluetooth devices. Among those with a strong signal strength, you should see SparkFun_nRF52840. Click the "CONNECT" button next to that.

Connect to the sparkfun_nrf52840 board

Once connected, the Blue LED should turn off indicating a connection.

Click on the LED Button Service, where you'll be presented with a pair of characteristics: "Nordic Blinky Button" and "Nordic Blinky LED."

To set the LED, click on the up-arrow in the Nordic Blinky LED characteristic. To turn the LED on, type 01 into the value text box. To turn it off, type 00 (note the double-zero).

nRFConnect example -- turning on LED

To read the status of the button on pin 13, either tap single-down-arrow for a single read, or subscribe to notifications by tapping the triple-down-arrow. When you press the button down, the value should change to 0x01.

SDK Tips and Tricks

Configuring sdk_config

From the last example, I dare you to peek at the contents of sparkfun_nrf52840_mini/config/sdk_config.h. You'll be greeted with a 13k-line header full of #define macros.

Each of the #defines in sdk_config.h either slightly or drastically alter the performance of your application. It's a powerful file, but it is overwhelming. If you have Java installed, you can type:

language:makefile
make sdk_config

To open a CMSIS Config Wizard. This window allows you to navigate through a list of sorted config macros, and make changes graphically.

CMSIS Config Wizard

If you don't have Java, check out this next section on Backend-Logging...

Backend-Logging

Most application examples, unfortunately, don't log anything to a USB serial port. But! Most examples do support UART-based logging, but you have to enable it.

Search your config file for NRF_LOG_BACKEND_UART_ENABLED. Set it to 1, then set NRF_LOG_BACKEND_UART_TX_PIN to 17 (or any other pin you'd like to use as TX).

Now, if you hook up a Serial Basic or similar USB-to-serial board from RX on the basic to pin 17 on the nRF52840, you should begin to get debug prints.

Backend logging via UART

USB CDC (serial over USB) logging is, unfortunately, not currently supported.

Enabling pins 9 and 10 as GPIO

By default pins 9 and 10, towards the bottom of the board, are only to be used as NFC pins. You can change this by adding the following defines to your Makefile:

language:makefile
ASMFLAGS += -DCONFIG_NFCT_PINS_AS_GPIOS
CFLAGS += -DCONFIG_NFCT_PINS_AS_GPIOS

Linker Script's Memory Organization

We have been made aware of a possible issue in the configuration of the memory region for the BLE blinky example. For more details on the issue, users can check out the forum post. If other users also run into issues, they can try this configuration instead (has not been tested or verified):

Note: Unfortunately, with the COVID-19 restrictions this has not been tested or verified by our team.
MEMORY
{
  FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xda000
  RAM (rwx) :  ORIGIN = 0x200022e0, LENGTH = 0x3dd20
}

System Paths

Users may run into issues with their file paths when trying to run make. Make sure you are in the same directory as the Makefile, illustrated in the image below. Additionally, users may need to add system paths for Windows 10.

gif showing added build packages and compile
Notice the file path in the left hand column and terminal.

Resources and Going Further

Tutorial Resources:

Looking for more nRF52840 nRF5 SDK development tips? Here are some of our favorite resources around the web:

For more tutorials, more to learn, and more project-inspiration, check out these related tutorials:

Interactive Hanging LED Array

Learn how we converted 72 lightbulbs into an interactive LED array for our conference room.

nRF52832 Breakout Board Hookup Guide

How to hookup and program (in Arduino!) the nRF52832 Breakout -- a development board for Nordic's BLE/ANT/2.4GHz system on chip.

SparkFun Pro nRF52840 Mini Hookup Guide

A hardware overview and hookup guide for the SparkFun Pro nRF52840 Mini -- a breakout for Nordic's impossibly cool Bluetooth/ARM Cortex M4 SoC.

nRF52840 Development with Arduino and CircuitPython

How to use Arduino or CircuitPython to develop applications for the nRF52840 Cortex-M4 Bluetooth SoC.