Skip to content

About

USB-enabled bootloaders for the nRF52 BLE SoC chips

Resources

Code of conduct

Stars

1 star

Watchers

0 watching

Forks

 
 

Repository files navigation

Adafruit nRF52 Bootloader

Build Status

A CDC/DFU/UF2 bootloader for Nordic nRF52 microcontroller. UF2 is an easy-to-use bootloader that appears as a flash drive. You can just copy .uf2-format application images to the flash drive to load new firmware. See https://github.com/Microsoft/uf2 for more information.

DFU via serial/CDC requires the standalone Nordic nrfutil. You can download it from the official Nordic website or install it via:

# Download the latest nrfutil for Linux
curl -LO https://developer.nordicsemi.com/.pc-tools/nrfutil/x64-linux/nrfutil
chmod +x nrfutil
sudo mv nrfutil /usr/local/bin/

Supported Boards

Officially supported boards are:

In addition, there is also lots of other 3rd-party boards which are added by other makers, users and community. Check out the complete list of all boards here.

Features

  • DFU over Serial and OTA (application, Bootloader+SD)
  • Self-upgradable via Serial and OTA
  • DFU using UF2 (https://github.com/Microsoft/uf2) (application only)
  • Auto-enter DFU briefly on startup for DTR auto-reset trick (832 only)
  • Supports dual bank firmware updates (disabled by default)
  • Supports signed firmware updates (disabled by default)
  • Checks the initial stack pointer and Thumb reset vector before booting applications without a stored CRC, including the erased-settings fallback.

Note: For OTA, Packet Receipt Notification (PRN) must be 8 or less, which can be configured in nRF DFU. Otherwise, the bootloader will run out of memory.

How to use

There are two pins, DFU and FRST that bootloader will check upon reset/power:

  • Double Reset Reset twice within 500 ms will enter DFU with UF2 and CDC support (only works with nRF52840)
  • DFU = LOW and FRST = HIGH: Enter bootloader with UF2 and CDC support
  • DFU = LOW and FRST = LOW: Enter bootloader with OTA, to upgrade with a mobile application such as Nordic nrfConnect/Toolbox
  • DFU = HIGH and FRST = LOW: Factory Reset mode: erase firmware application and its data
  • DFU = HIGH and FRST = HIGH: Go to application code if it is present, otherwise enter DFU with UF2
  • The GPREGRET register can also be set to force the bootloader can enter any of above modes (plus a CDC-only mode for Arduino). GPREGRET is set by the application before performing a soft reset.
#include "nrf_nvic.h"
void reset_to_uf2(void) {
  NRF_POWER->GPREGRET = 0x57; // 0xA8 OTA, 0x4e Serial
  NVIC_SystemReset();         // or sd_nvic_SystemReset();
}

On the Nordic PCA10056 DK board, DFU is connected to Button1, and FRST is connected to Button2. So holding down Button1 while clicking RESET will put the board into USB bootloader mode, with UF2 and CDC support. Holding down Button2 while clicking RESET will put the board into OTA (over-the-air) bootloader mode.

On the Nordic PCA10059 Dongle board, DFU is connected to the white button. FRST is connected to pin 1.10. Ground it to pull FRST low, as if you had pushed an FRST button. There is an adjacent ground pad.

For other boards, please check the board definition for details.

Making your own UF2

To create your own UF2 DFU update image, simply use the Python conversion script on a .bin file or .hex file, specifying the family as 0xADA52840 (nRF52840) or 0x621E937A (nRF52833).

nRF52840
uf2conv.py firmware.hex -c -f 0xADA52840

nRF52833
uf2conv.py firmware.hex -c -f 0x621E937A

If using a .bin file with the conversion script you must specify application address with the -b switch, this address depend on the SoftDevice size/version e.g S140 v6 is 0x26000, v7 is 0x27000

nRF52840
uf2conv.py firmware.bin -c -b 0x26000 -f 0xADA52840

nRF52833
uf2conv.py firmware.bin -c -b 0x27000 -f 0x621E937A

To create a UF2 image for bootloader from a .hex file using separated family of 0xd663823c

uf2conv.py bootloader.hex -c -f 0xd663823c

Burn & Upgrade with pre-built binaries

You can burn and/or upgrade the bootloader with either a J-link or DFU (serial) to a specific pre-built binary version without the hassle of installing a toolchain and compiling the code. This is preferred if you are not developing/customizing the bootloader. Pre-builtin binaries are available on GitHub releases

Note: The bootloader can be downgraded. Since the binary release is a merged version of both bootloader and the Nordic SoftDevice, you can freely upgrade/downgrade to any version you like.

How to compile and build

You should only continue if you are looking to develop bootloader for your own. You must have have a J-Link available to "unbrick" your device.

Prerequisites

Build:

Firstly clone this repo including its submodules with following command:

git clone --recurse-submodules https://github.com/adafruit/Adafruit_nRF52_Bootloader

For git versions before 2.13.0 you have to do that manually:

git clone https://github.com/adafruit/Adafruit_nRF52_Bootloader
cd Adafruit_nRF52_Bootloader
git submodule update --init

Build using make

Then build it with make BOARD={board} all, for example:

make BOARD=feather_nrf52840_express all

Make uses python3 by default. Set PYTHON=/path/to/python3 in the environment or on the Make command line to override the interpreter, including UF2 deployment.

For the list of supported boards, run make without BOARD= :

make
You must provide a BOARD parameter with 'BOARD='
Supported boards are: feather_nrf52840_express feather_nrf52840_express pca10056
Makefile:90: *** BOARD not defined.  Stop

Build using cmake

Firstly initialize your build environment by passing your board to cmake via -DBOARD={board}:

mkdir build
cd build
cmake .. -DBOARD=feather_nrf52840_express

And then build it with:

make

You can also use the generator of your choice. For example adding the -GNinja and then you can invoke ninja build instead of make.

To list all supported targets, run:

cmake --build . --target help

To build individual targets, you can specify it directly with make or using cmake --build:

make bootloader
cmake --build . --target bootloader

One-time USB bootloader identity migration

Use this only when the physical board remains compatible and a corrected board definition changes its USB VID/UF2 PID. It is not a cross-board flashing bypass. The installed bootloader compares the incoming CF2 key 208 against its own USB VID/PID; changing the new bootloader's receive policy cannot bypass that old check.

The explicit migration-uf2 target packages the standard no-SoftDevice UF2, changing only CF2 key 208 to the installed identity. Runtime USB descriptors and all receive checks remain those of the new standard bootloader. Neither all nor the ordinary release-copy target includes migration artifacts.

Both IDs are required as 0xVVVVPPPP. SOURCE_ID is the installed bootloader's USB VID/UF2 PID (not its CDC PID or text Board-ID); TARGET_ID is the independently verified identity of the new standard board definition. The packager checks the input CF2 against the target ID before making any change.

Example: For SKPastry P10 1209:7694 to this branch's P10 1209:7693, from this repository:

make BOARD=sk_cheesecake_nrf_p10 migration-uf2 \
  BOOTLOADER_MIGRATION_SOURCE_ID=0x12097694 \
  BOOTLOADER_MIGRATION_TARGET_ID=0x12097693

Or with CMake:

cmake -S . -B cmake-build-sk_cheesecake_nrf_p10 \
  -DBOARD=sk_cheesecake_nrf_p10 \
  -DBOOTLOADER_MIGRATION_SOURCE_ID=0x12097694 \
  -DBOOTLOADER_MIGRATION_TARGET_ID=0x12097693
cmake --build cmake-build-sk_cheesecake_nrf_p10 --target migration-uf2

Migration procedure:

  1. Verify the actual installed USB identity, physical board compatibility, bootloader start and MBR parameter-page addresses. Have SWD recovery available for initial hardware validation.
  2. Copy only the dedicated migration UF2 to the old bootloader's USB drive.
  3. After it restarts, re-enter the bootloader and verify the new USB identity.
  4. Copy the standard no-SoftDevice UF2 from the same build (bootloader_mbr.uf2 with CMake; update-..._nosd.uf2 with Make). This restores CF2 to the new identity without relaxing any checks.
  5. Reinstall the application if necessary. The old bootloader handles the first transfer and can invalidate or overwrite application storage; the new bootloader's application-preservation fixes do not protect that first step.

PWM RGB LED polarity

For a common-cathode RGB LED using LED_RGB_RED_PIN, LED_RGB_GREEN_PIN, and LED_RGB_BLUE_PIN, add #define LED_RGB_COMMON_CATHODE to the board's board.h. Leave it undefined for a common-anode RGB LED; defining it as 0 still enables common-cathode mode.

This option affects only the three PWM RGB channels. Independent status LEDs retain their LED_STATE_ON polarity; NeoPixel and APA102 outputs are unchanged. Run the polarity regression with python3 tools/test_led_pwm.py (requires a host C compiler; override it with CC).

Flash

To flash the bootloader (without softdevice/mbr) using JLink:

make BOARD=feather_nrf52840_express flash

If you are using pyocd as debugger, add FLASHER=pyocd to make command:

make BOARD=feather_nrf52840_express FLASHER=pyocd flash

To upgrade the bootloader using DFU Serial via port /dev/ttyACM0

make BOARD=feather_nrf52840_express SERIAL=/dev/ttyACM0 flash-dfu

To flash SoftDevice (will also erase chip):

make BOARD=feather_nrf52840_express flash-sd

To flash MBR only

make BOARD=feather_nrf52840_express flash-mbr

Dual bank firmware support:

Dual-bank updates stage the incoming image separately from the active application, reducing the maximum application size to approximately half the available space. Enable them with DUALBANK_FW=1 for Make or -DDUALBANK_FW=ON for CMake.

The old application remains available while a replacement is received and validated. Once activation starts copying over bank 0, the old image is no longer a rollback copy. For serial dual-bank application updates, the bootloader writes and reads back an invalid bank-0 marker before erasing the active image, compares the copied image against the staging bank, and marks it valid only after that comparison succeeds. A failed invalidation leaves bank 0 untouched; a failed copy leaves it unbootable.

Serial validation or activation failures discard queued packets and pending flash cache data, reset the existing DFU session for a fresh upload, and do not show the write-complete indication. No new serial wire response is introduced. These activation checks do not change the asynchronous BLE OTA path.

Flash page erases and dirty-page writes feed an already-running watchdog's enabled reload channels. The watchdog interval must still exceed each individual blocking flash operation; feeding cannot make an arbitrarily short interval safe.

Host regressions run with python3 -m unittest discover -s tools -p 'test_*.py'. The DFU and watchdog regressions require a native C compiler (CC can override it) on Linux; modeled hardware failures and timing are not physical-device proof.

Signed firmware support:

This bootloader can validate that the uploaded firmware is digitally signed, and refuse to install unsigned or signed with the improper key firmware. Because this will make Arduino uploads stop working (because they are not digitally signed), this feature is disabled by default. But if you want to ensure noone else is allowed to upload firmware to your device, this option is probably what you want to enable.

To create a signing key, you will need the adafruit-nrfutil utility. First, generate a signing key and store at given path by using

adafruit-nrfutil keys --gen-key stored_key.pem

This command will create a signing key and store it in the file stored_key.pem. STORE THIS FILE IN A SAFE PLACE, as without it, you won't be able to sign your firmware packages, and the bootloader will refuse to flash them!!

Now, you must show the verification key (that is calculated from the signing key):

adafruit-nrfutil keys --show-vk code stored_key.pem

This command will read your signing key (from the file stored_key.pem) and display C source code with the Qx and Qy arrays: Something along the lines

static uint8_t Qx[] = { ... };
static uint8_t Qy[] = { ... };

You must save the Qx and Qy values, remove all spaces, and pass them to the make command line

make BOARD=feather_nrf52840_express SIGNED_FW=1 SIGNED_FW_QX='Qx values' SIGNED_FW_QY='Qy values'

And replace Qx values with the Qx array values, and the Qy values with the Qy array values. That will create a bootloader that ONLY accepts firmware signed with the stored_key.pem signing key. Take into account that the option SIGNED_FW=1 is what enables to build a bootloader that enforces signing, and will ALSO result in disabling UF2 support, as UF2 format does NOT support signing keys. There is an option (only for testing!) called FORCE_UF2 that will FORCE UF2 support on bootloaders that enforce firmware security. It is there to bypass security so you can test a secure bootloader and have a way to recover it something goes wrong, but should NEVER be used if you want to enforce security

Finally, to create a signed firmware application/bootloader update package, you can use, for example

adafruit-nrfutil dfu genpkg --dev-type 0x0052 --sd-req 0x0123 --application "application.hex" --key-file "stored_key.pem" "application_package.zip"

Please, use full paths for all files. The examples omit them for clarity!

And, to upload it to your device,

adafruit-nrfutil.exe --verbose dfu serial -pkg "application_package.zip" -p /dev/tty0 -b 115200

Common makefile problems

arm-none-eabi-gcc: No such file or directory

If you get the following error ...

make BOARD=feather_nrf52840_express all
Compiling file: main.c
/bin/sh: /usr/bin/arm-none-eabi-gcc: No such file or directory
make: *** [_build/main.o] Error 127

... you may need to pass the location of the GCC ARM toolchain binaries to make using the variable CROSS_COMPILE as below:

make CROSS_COMPILE=/opt/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi- BOARD=feather_nrf52832 all

For other compile errors, check the gcc version with arm-none-eabi-gcc --version to insure it is at least 9.x.

ModuleNotFoundError: No module named 'intelhex'

Install python-intelhex with

pip install intelhex

make: nrfjprog: No such file or directory

Make sure that nrfjprog is available from the command-line. This binary is part of Nordic's nRF5x Command Line Tools.

About

USB-enabled bootloaders for the nRF52 BLE SoC chips

Resources

Code of conduct

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages