Update: Since this blog was originally written the nrfjprog tool has been ported to OS X (now macOS). This means that the flash programming is much simpler, and it is simply a matter of following the documentation for the tool. Also, nRF Toolbox for iOS is deprecated; use the nRF Connect iOS app instead
I recently decided to try the nRF51 SDK on Mac OS X to see how it was to compile and program the example applications. It worked nicely, and it wasn't even that hard to figure out. But evidently there were things to find out, and before things were actually working there was always the fear of discovering that some of the setup was wrong and you have wasted hours on a stupid mistake.
The questions are many: Do I have the right tools? Where do get started in the SDK? Are there other changes needed? And from experience there are always things you don't think about before they hit you in your face. This blog is the guide I wish I had when I first started out. If by following the steps there is at least some confidence that things are working as they should, then I can go on exploring from there. I hope you find it helpful!
Setting up the tools
First of all, and at the risk of stating the obvious: Buy a development kit!
You will need this for the license to programming and debugging software. For this tutorial the nRF51822-EK (Evaluation Kit) was used.
Assuming nothing, here is the (minimum) software you need download (the version I used are in parenthesis):
- nRF51-SDK-zip: This is the Nordic Semiconductor SDK for nRF51 (6.0.0). See the nRF51/52 SDK download page to get it. Unpack the SDK to a location of your own choice.
- S110-SD-v7: The S110 (v7.0.0) softdevice that supports peripheral devices. Find it on the same page as above.
- GCC ARM Embedded: Compiler tools for ARM Cortex-M. Download the latest version from Launchpad (gcc-arm-none-eabi-4_8-2014q2-20140609-mac). Unpack the tarball to /usr/local.
- J-Link software for Mac: Get the latest version from Segger (V4.90b) and run the installer.
- (You might need to install make. Not sure if it is installed byt default or part of XCode.)
Compiling the Bluetooth Smart example
The examples in the SDK each have a makefile (or several) in the gcc folder that works without modification. However, there is one important change that is needed for one of the template files, which is to set the correct toolchain path. This is defined in Makefile.posix: (SDK location)\nrf51822\Source\templates\gcc\Makefile.posix
GNU_INSTALL_ROOT := /usr/local/gcc-arm-none-eabi-4_8-2014q2
GNU_VERSION := 4.8.3
GNU_PREFIX := arm-none-eabi
Make sure that GNU_INSTALL_ROOT is the correct path GCC toolchain on your computer. Also check that GNU_VERSION is the right one.
To make things interesting I'll use a Bluetooth Smart example: the Heart Rate Monitor application ((SDK location)/nrf51822/Board/pca10001/s110/ble_app_hrs). With the changes in Makefile.posix you should now be able to compile it.
~ em$ cd <SDK location>/nrf51822/Board/pca10001/s110/ble_app_hrs/gcc
gcc em$ make -f ble_app_hrs.Makefile all
This example has more than on makefile so you need to specify which one to use. The build result is found in the _build folder.
Flash Programming
For simplicity in this tutorial copy the softdevice (S110) .hex file to the _build folder of the Heart Rate Monitor example. The programming is performed from the _build folder:
_build em$ JLinkExe -device nrf51822_xxaa -if swd -speed 4000
[...]
J-Link>loadbin s110_nrf51822_7.0.0_softdevice.bin 0
J-Link>loadbin ble_app_hrs_s110_xxaa.hex 0x16000
J-Link>r
J-Link>g
J-Link>exit
Setting the correct device is crucial, and the speed setting is needed to make the programming fast enough. The softdevice is first programmed in the beginning (location 0) and the application is programmed immediately after (0x16000 for S110 v7.0). After the programming a reset is needed ('r' + 'g').
Now, try to connect the HRM to your phone: I used nRF Toolbox for iOS.
Troubleshooting tips
If you are struggling to make the app work it might be a good idea to clean the memory and reprogram. Unfortunately the only way to erase the memory is to do some magic register writes:
JLinkExe
J-Link>w4 4001e504 2
J-Link>w4 4001e50c 1
If you are wondering if things are working at all, try building and programming the blinky_example at memory location 0. Since this example doesn't require the softdevice you have removed one source of error.
Automating Tasks by Modifying the Makefiles
Working with the J-Link CLI quickly gets cumbersome, but by modifying Makefile.posix the things you need to do can be automated. Here is file I used:
GNU_INSTALL_ROOT := /usr/local/gcc-arm-none-eabi-4_8-2014q2
GNU_VERSION := 4.8.3
GNU_PREFIX := arm-none-eabi
GDB_PORT_NUMBER := 9992
FLASH_START_ADDR = $(shell $(OBJDUMP) -h $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).out -j .text | grep .text | awk '{print $$4}')
JLINK_OPTS = -device nrf51822 -if swd -speed 4000
JLINK_GDB_OPTS = -noir
JLINK = JLinkExe $(JLINK_OPTS)
JLINKD_GDB = JLinkGDBServer $(JLINK_GDB_OPTS)
flash-jlink: flash.jlink
$(JLINK) flash.jlink
flash.jlink:
printf "loadbin $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).bin $(FLASH_START_ADDR)\nr\ng\nexit\n" > flash.jlink
erase-all: erase-all.jlink
$(JLINK) erase-all.jlink
erase-all.jlink:
# Write to NVMC to enable erase, do erase all, wait for completion. reset
printf "w4 4001e504 2\nw4 4001e50c 1\nsleep 100\nr\nexit\n" > erase-all.jlink
run-debug:
$(JLINKD_GDB) $(JLINK_OPTS) $(JLINK_GDB_OPTS) -port $(GDB_PORT_NUMBER)
.PHONY: flash-jlink flash.jlink erase-all erase-all.jlink run-debug
With this modification you have makefile targets for flash programming (flash-jlink) and erasing the chip (erase-all). In addition there is a command to launch the debugger (run-debug) which we will look at next.
Tip: For more options and ideas have a look at this Github repo.
Running the Debugger
With the modified Makefile.posix it is easy to start a GDB server:
gcc em$ make -f ble_app_hrs.Makefile run-debug
This will start a GDB server in your current terminal window. Note that there is a port number in the parameter list (9992, GDB_PORT_NUMBER in Makefile.posix) which you need to connect to GDB.
In order to load debug symbols you need to compile the project in debug mode (make clean & make debug). Start the debugger with the .out file to load the symbols. In another terminal window start GDP:
gcc em$ /usr/local/gcc-arm-none-eabi-<version>/bin/arm-none-eabi-gdb _build/ble_app_hrs_s110_xxaa.out
[Loading ... check that symbols are loaded]
(gdb) target remote localhost:9992
(gdb) b main.c:621
(gdb) monitor reset
(gdb) continue
The line number in main.c might differ, so set it to the Connected case in the on_ble_evt() callback routine. Try to connect the heart rate monitor to your phone and check that the GDB breaks the execution.
Next step - IDE integration
I kept IDE integration out of this guide, but it is a natural next step. The nAN-29 Application Note explains how to set up Eclipse with GCC. Personally I'm tempted to try something else.