This is the first in a series of posts about learning to develop for the Nordic Semiconductor nRF52840 Bluetooth chip. I have a handful of Bluetooth based projects that I would like to build, so I need a platform that I can implement them with. I’m using the SparkFun Pro nRF52840 Mini – Bluetooth Development Board to get started. This board can be programmed using Arduino, Circuit Python, in C using nRF5 SDK. If I wanted to quickly knock out a single project, I’d probably choose one of those options. But I want to build up my own library of code, and I kind of like the option to work at the HAL level, so I’m using the nRF5 SDK. I got started building some of the examples in the SDK, but the low amount of documentation associated with many of the examples made them less helpful than I had hoped for, particularly with understanding the internal dependencies of the the SDK. To get a better handle on how to use the chip and SDK, I ended up creating a series of simple programs, that also use more straightforward Makefiles. In this series I’ll describe how I set up the software I’m using for development. Then I’ll go through several examples that demonstrate the basic chip operation, and can form building blocks for larger projects. I’m posting all the code on github.com, and I hope it will be useful for others.
Series Table of Contents
- Part 1: Introduction
- Part 2: Software and Setup
- Part 3: Blinky
- Part 4: Debugging
- Part 5: Timers and Interrupts
- Part 6: PWM peripheral
- Part 7: USB CDC example
- Part 8: FreeRTOS Blinky
- Part 9: FreeRTOS USB Shell
- Part 10: BLE Blinky
- Part 11: BLE Shell
*links will be added as posts are completed.
Longer Introduction
I chose to focus on the nRF52840 for a couple of reasons. First, it’s based around an ARM M4 core, which means any code not directly associated with one of the peripherals should be easy to port to/from other ARM based systems, and there are a lot of debugging tools available as well. Second, the nRF52840 has built-in hardware support for USB, which is something I want for some of my intended projects. I chose the SparkFun Pro nRF52840 Mini – Bluetooth Development Board mainly because it was the first board I found that included a tutorial on programming using the nRF5 SDK, without the need for Circuit Python or Arduino. Later I realized that it is very similar to the nRf52840 Feather board, but the feather also includes the SWD header on the board. (the Sparkfun board has thru-hole pads for the debug interface, but the header is not attached and is in a difficult location for soldering.) Although I haven’t had a chance to test it out, I pretty sure everything I present in these tutorials will also work on the Feather after some pin re-mapping.
I started out by going through the SparkFun Pro nRF52840 Mini Hookup Guide. I then moved on to the nRF52840 Advanced Development With the nRF5 SDK, and built the Blinky example. And here’s where things started to get frustrating. When you look at the main program, it’s very straightforward. It’s really just a couple lines of actual code, with three calls to functions that are implemented elsewhere.
#include <stdbool.h> #include <stdint.h> #include "nrf_delay.h" #include "boards.h" /** * @brief Function for application main entry. */ int main(void) { /* Configure board. */ bsp_board_init(BSP_INIT_LEDS); /* Toggle LEDs. */ while (true) { for (int i = 0; i < LEDS_NUMBER; i++) { bsp_board_led_invert(i); nrf_delay_ms(500); } } }
But when you look at the Makefile you find this list of source files, which get compiled as part of the project:
SRC_FILES += \ $(SDK_ROOT)/modules/nrfx/mdk/gcc_startup_nrf52840.S \ $(SDK_ROOT)/components/libraries/log/src/nrf_log_frontend.c \ $(SDK_ROOT)/components/libraries/log/src/nrf_log_str_formatter.c \ $(SDK_ROOT)/components/boards/boards.c \ $(SDK_ROOT)/components/libraries/util/app_error.c \ $(SDK_ROOT)/components/libraries/util/app_error_handler_gcc.c \ $(SDK_ROOT)/components/libraries/util/app_error_weak.c \ $(SDK_ROOT)/components/libraries/util/app_util_platform.c \ $(SDK_ROOT)/components/libraries/util/nrf_assert.c \ $(SDK_ROOT)/components/libraries/atomic/nrf_atomic.c \ $(SDK_ROOT)/components/libraries/balloc/nrf_balloc.c \ $(SDK_ROOT)/external/fprintf/nrf_fprintf.c \ $(SDK_ROOT)/external/fprintf/nrf_fprintf_format.c \ $(SDK_ROOT)/components/libraries/memobj/nrf_memobj.c \ $(SDK_ROOT)/components/libraries/ringbuf/nrf_ringbuf.c \ $(SDK_ROOT)/components/libraries/strerror/nrf_strerror.c \ $(SDK_ROOT)/modules/nrfx/soc/nrfx_atomic.c \ $(PROJ_DIR)/main.c \ $(SDK_ROOT)/modules/nrfx/mdk/system_nrf52840.c \
That’s 19 files to blink an LED! So what’s going on with all this extra stuff? As it turns out, almost nothing. You can delete most of these files from the build, and everything will still build and link. All you actually need are the following:
SRC_FILES += \ $(SDK_ROOT)/modules/nrfx/mdk/gcc_startup_nrf52840.S \ $(SDK_ROOT)/components/boards/boards.c \ $(PROJ_DIR)/main.c \ $(SDK_ROOT)/modules/nrfx/mdk/system_nrf52840.c \
I understand that there might be a reason to include all these files in a standard Makefile, even if they aren’t used everywhere. But there should be an explanation of what they’re used for. Including them without explanation fails to teach the user how to correctly use the SDK. After looking through a few more examples, I decided it made more sense to go back to basics and build up my own examples to teach myself how to use the SDK. Those examples are what I’m presenting in the rest of this series of posts. I hope you’ll find them useful.