Introduction

The nRF51822 is a very popular SoC (System on a Chip) which integrates BLE (Bluetooth Low Energy) with an ARM Cortex M0 CPU. For folks like myself trudging along in 8-bit AVR country, 32-bit ARM development is unfamiliar territory. The chips are powerful, complex beasts, and the hardware and software infrastucture required to program them are tortuous and often absurdly priced. For nRF51822, you have the burden of understanding the BLE jargon as well.

To clear the fog in my head, I wanted to create a simple project using nRF51822 that reads light levels from an LDR and sends the data over BLE. In the process, I set up the development environment on Windows and OS X. Hopefully this will serve as a useful guide for you to get started on this chip.

Hardware

The best hardware you can buy to get started with the nRF51822 is the official nRF51-DK kit from Nordic which costs about USD 70. It might be tempting get a cheap nRF51822 breakout board and hack with it. But the official kit has some major advantages:

nRF51-DK comes with a Segger J-Link OB – built-in JTAG that is. This is an opportunity for learning the industry-standard way of programming, debugging and testing these kind of chips. nRF51-DK has 4 leds, 4 buttons, a coin cell holder, and female headers broken out for all the nRF51422 pins. A great platform for prototyping. The built-in JTAG adapter can be used for external programming. So once you are done with prototyping and want to program your nRF51822 custom board, you don’t need any other hardware. Being the official board, it has vast support and documentation from Nordic and the developer community.

When you get the board, it might unsettle you (like it did me) that the chip seen on the board is the nRF51422 and not nRF51822. Fear not, as the former chip is a superset of the latter, and unless you are messing with the ANT protocol, anything you write on this board will work fine on an nRF51822.

By the way, this board is known as PCA10028 – you’ll encounter this name all over the place.

Software

To start development, get the following:

You notice above that I picked GCC for development above. Unfortunately the ARM world is filled with proprietory, closed source, horribly expensive software. Fortunately Nordic has done a fabulous job supporting the Open Source ARM GCC toolchain, which is what we will be taking advantage of.

It’s a good idea to take a look at the contents of the nRF51 SDK folder. Nordic seems to have changed this at least once, and I have found that many articles on toolchain setup refer to a now-obsolete directory structure.

klaatu:nRF51_SDK_8.1.0_b6ed55f mahesh$ pwd /Users/mahesh/Documents/code/nRF51/nRF51_SDK_8.1.0_b6ed55f klaatu:nRF51_SDK_8.1.0_b6ed55f mahesh$ tree -d -L 2 . ├── SVD ├── components │ ├── ble │ ├── device │ ├── drivers_ext │ ├── drivers_nrf │ ├── libraries │ ├── properitary_rf │ ├── serialization │ ├── softdevice │ └── toolchain ├── documentation │ ├── s110 │ ├── s120 │ ├── s130 │ └── s210 ├── examples │ ├── ant │ ├── ble_central │ ├── ble_central_and_peripheral │ ├── ble_peripheral │ ├── bsp │ ├── dfu │ ├── dtm │ ├── multiprotocol │ ├── peripheral │ └── properitary_rf └── external └── rtx

We’ll need to modify the Makefiles in components/toolchain/gcc. The Nordic samples are in examples which is our fundamental reference. The SoftDevice we are going to use is in the softdevice/s110 directory.

Setting up on Windows

Nordic assumes that you will most likely use ARM Keil tools for development on Windows, and hence the documentation is skewed towards it. ARM graciously provides a 32k code limited version free of charge. And when you want to upgrade to their commercial non-restricted version, you can just fork over something in the neighborhood of USD 4900 for a license. Thankfully, there is an alternative: GCC.

Developing with GCC requires the Unix make and associated tools. Although you can get make by itself, I recommend that instead you install GNU MSYS which gives you a Unix-like environment within Windows. (This is in fact the first piece of sotware that I install on any new Windows machine.)

The next thing you need to do is to modify components/toolchain/gcc/Makefile.windows. Here’s what mine looks like:

ifeq ($(findstring 86, $(ProgramFiles)), ) PROGFILES := C:/Program Files else PROGFILES := C:/Program Files (x86) endif GNU_INSTALL_ROOT := C:\Program Files (x86)\GNU Tools ARM Embedded.9 2015q1 GNU_VERSION := 4.9.3 GNU_PREFIX := arm-none-eabi

Next, put the Segger J-Link into your PATH environement variable. On my system, the path is C:\Program Files (x86)\SEGGER\JLink_V498c.

Setting up on OS X

OS X has Unix underneath, so you already have make and other goodies. You need to modify components/toolchain/gcc/Makefile.posix as follows:

GNU_INSTALL_ROOT := /usr/local/gcc-arm-none-eabi-4_9-2015q1 GNU_VERSION := 4.9.3 GNU_PREFIX := arm-none-eabi

Make sure the paths and version match above, for your system.

The Code

It’s common for people to put their project into the toolchain example directory, in parallel with existing projects. But I find this to be messy. What happens when you get the next version of the SDK? Besides, the Makefile scheme easily lets you put your code wherever you want. In my case, I am putting the project’s code in a directory parallel to the SDK directory.

klaatu:nRF51 mahesh$ pwd /Users/mahesh/Documents/code/nRF51 klaatu:nRF51 mahesh$ ls -1 nRF51-adc-test nRF51_SDK_8.1.0_b6ed55f

So correspondingly, in the project Makefile, you will see:

SDK_ROOT = ../../../../nRF51_SDK_8.1.0_b6ed55f TEMPLATE_PATH = $(SDK_ROOT)/components/toolchain/gcc SOFTDEVICE_HEX = $(SDK_ROOT)/components/softdevice/s110/hex/s110_softdevice.hex

You can modify the above as you see fit.

Software Layers

ARM Cortex M Programming is complicated, and so is BLE. To get going, you need to familiarize yourselves with the different layers of software used, and how they interact with each other. You will have to refer to the extensive nRF51 SDK documentation to make sense of all this.

The first thing to understand with nRF51 is the SoftDevice concept. According to Nordic, “The SoftDevice is a precompiled and linked binary software implementing a Bluetooth 4.1 low energy protocol stack for the nRF51 series of chips.” You can see below how it sits in relation to the application code.

In general, your programming flow will consist of flashing the SoftDevice on to the chip (need to do this only once), and then flashing your application code on top. (We’re using S110 here, but Nordic has other types of SoftDevices too.)

As you saw in the graphic above, just above the hardware is CMSIS, which stands for Cortex Microcontroller Software Interface Standard – this is a vendor-independent hardware abstraction layer from ARM that simplifies programming and reduces development time for these chips.

On top of CMSIS, Nordic has provided a huge bunch of libraries. BLE, peripherals, board support, softdevice, bootloader – just to name a few. You just have to read the documentation – no way around this!

Program Structure

As with other microcontrollers, the program for our project consists of a main loop:

// Application main function. int main(void) { uint32_t err_code; // set up timer APP_TIMER_INIT(0, (2 + BSP_APP_TIMERS_NUMBER), 4, false); // initlialize BLE ble_stack_init(); gap_params_init(); services_init(); advertising_init(); conn_params_init(); err_code = ble_advertising_start(BLE_ADV_MODE_FAST); APP_ERROR_CHECK(err_code); // intialize UART uart_init(); // prints to serial port printf("starting...

"); // set up ADC adc_config(); nrf_adc_start(); // set LED1 connected to P0.22 as output uint32_t pinNum = 22; //nrf_gpio_cfg_output(pinNum); nrf_gpio_pin_dir_set(pinNum, NRF_GPIO_PIN_DIR_OUTPUT); // Enter main loop. while(1) { // flash LED2 once nrf_gpio_pin_set(pinNum); nrf_delay_ms(100); nrf_gpio_pin_clear(pinNum); nrf_delay_ms(100); // send ADC value via NUS (Nordic UART service) uint8_t str[4]; sprintf((char*)str, "%d", (int)adc_sample); ble_nus_string_send(&m_nus, str, strlen((char*)str)); } }

In the code above, we start by intializing BLE – GAP, advertizing, etc. The services_init() call initializes the BLE NUS (Nordic UART Service) which is what we will use to send data over BLE. Next, we initialize UART which can be used for printing debug messages. The next step is to initialize ADC. Then we come to the loop, where in each iteration we (a) flash LED #2 and (b) Send the ADC data over NUS. You can look at the full source to see how the above functions are implemented.

Building and Uploading the Code

First you need to upload the SoftDevice on to the chip. (You need to do this only once.)

klaatu:armgcc mahesh$ pwd /Users/mahesh/Documents/code/nRF51/nRF51-adc-test/pca10028/s110/armgcc klaatu:armgcc mahesh$ make flash_softdevice printf "loadbin ../../../../nRF51_SDK_8.1.0_b6ed55f/components/softdevice/s110/hex/s110_softdevice.hex 0

r

g

exit

" > flashsd.jlink JLinkExe -device nrf51422_xxaa -if swd -speed 4000 flashsd.jlink SEGGER J-Link Commander V5.00c ('?' for help) Compiled Jun 11 2015 11:45:08 Script file read successfully. Info: Device "NRF51422_XXAA" selected. DLL version V5.00c, compiled Jun 11 2015 11:44:58 Firmware: J-Link OB-SAM3U128-V2-NordicSemi compiled Jun 8 2015 10:56:52 Hardware: V1.00 S/N: XXXX VTarget = 3.300V Info: Found SWD-DP with ID 0x0BB11477 Info: Found Cortex-M0 r0p0, Little endian. Info: FPUnit: 4 code (BP) slots and 0 literal slots Info: CoreSight components: Info: ROMTbl 0 @ F0000000 Info: ROMTbl 0 [0]: F00FF000, CID: B105100D, PID: 000BB471 ROM Table Info: ROMTbl 1 @ E00FF000 Info: ROMTbl 1 [0]: FFF0F000, CID: B105E00D, PID: 000BB008 SCS Info: ROMTbl 1 [1]: FFF02000, CID: B105E00D, PID: 000BB00A DWT Info: ROMTbl 1 [2]: FFF03000, CID: B105E00D, PID: 000BB00B FPB Info: ROMTbl 0 [1]: 00002000, CID: B105900D, PID: 000BB9A3 ??? Cortex-M0 identified. Target interface speed: 1000 kHz Processing script file... Downloading file [../../../../nRF51_SDK_8.1.0_b6ed55f/components/softdevice/s110/hex/s110_softdevice.hex]...Info: J-Link: Flash download: Flash programming performed for 2 ranges (91136 bytes) Info: J-Link: Flash download: Total time needed: 1.532s (Prepare: 0.085s, Compare: 0.031s, Erase: 0.000s, Program: 1.400s, Verify: 0.008s, Restore: 0.006s) O.K. Reset delay: 0 ms Reset type NORMAL: Resets core & peripherals via SYSRESETREQ & VECTRESET bit. Script processing completed.

Next, generate the hex file for the project as follows:

klaatu:armgcc mahesh$ pwd /Users/mahesh/Documents/code/nRF51/nRF51-adc-test/pca10028/s110/armgcc klaatu:armgcc mahesh$ make rm -rf _build *.jlink echo Makefile Makefile mkdir _build Compiling file: app_button.c Compiling file: app_error.c Compiling file: app_fifo.c Compiling file: app_timer.c Compiling file: app_trace.c Compiling file: nrf_assert.c Compiling file: retarget.c Compiling file: app_uart_fifo.c Compiling file: nrf_delay.c Compiling file: nrf_adc.c Compiling file: nrf_drv_common.c Compiling file: nrf_drv_gpiote.c Compiling file: pstorage.c Compiling file: bsp.c Compiling file: bsp_btn_ble.c Compiling file: main.c Compiling file: ble_advdata.c Compiling file: ble_advertising.c Compiling file: ble_conn_params.c Compiling file: ble_nus.c Compiling file: ble_srv_common.c Compiling file: system_nrf51.c Compiling file: softdevice_handler.c Compiling file: gcc_startup_nrf51.s Linking target: nrf51422_xxac_s110.out Preparing: nrf51422_xxac_s110.bin Preparing: nrf51422_xxac_s110.hex text data bss dec hex filename 18940 108 2072 21120 5280 _build/nrf51422_xxac_s110.out

Now you need to upload the code onto the chip:

klaatu:armgcc mahesh$ make flash printf "loadbin _build/nrf51422_xxac_s110.bin 00018000

r

g

exit

" > flash.jlink JLinkExe -device nrf51422_xxaa -if swd -speed 4000 flash.jlink SEGGER J-Link Commander V5.00c ('?' for help) Compiled Jun 11 2015 11:45:08 Script file read successfully. Info: Device "NRF51422_XXAA" selected. DLL version V5.00c, compiled Jun 11 2015 11:44:58 Firmware: J-Link OB-SAM3U128-V2-NordicSemi compiled Jun 8 2015 10:56:52 Hardware: V1.00 S/N: XXXX VTarget = 3.300V Info: Found SWD-DP with ID 0x0BB11477 Info: Found Cortex-M0 r0p0, Little endian. Info: FPUnit: 4 code (BP) slots and 0 literal slots Info: CoreSight components: Info: ROMTbl 0 @ F0000000 Info: ROMTbl 0 [0]: F00FF000, CID: B105100D, PID: 000BB471 ROM Table Info: ROMTbl 1 @ E00FF000 Info: ROMTbl 1 [0]: FFF0F000, CID: B105E00D, PID: 000BB008 SCS Info: ROMTbl 1 [1]: FFF02000, CID: B105E00D, PID: 000BB00A DWT Info: ROMTbl 1 [2]: FFF03000, CID: B105E00D, PID: 000BB00B FPB Info: ROMTbl 0 [1]: 00002000, CID: B105900D, PID: 000BB9A3 ??? Cortex-M0 identified. Target interface speed: 1000 kHz Processing script file... Halting CPU for downloading file. Downloading file [_build/nrf51422_xxac_s110.bin]...Info: J-Link: Flash download: Flash programming performed for 1 range (19456 bytes) Info: J-Link: Flash download: Total time needed: 0.423s (Prepare: 0.087s, Compare: 0.004s, Erase: 0.000s, Program: 0.323s, Verify: 0.001s, Restore: 0.006s) O.K. Reset delay: 0 ms Reset type NORMAL: Resets core & peripherals via SYSRESETREQ & VECTRESET bit. Script processing completed.

Testing

Here is how you hook up your LDR resistor divider:

To test the BLE device you just created, you can use the Nordic nRFToolbox App. You can see it in action in the video below:

For privacy reasons YouTube needs your permission to be loaded. I Accept

Downloads

You can find source code for this project here:

https://github.com/electronut/nRF51-adc-test

Conclusion

This project was an introduction to nRF51822 programming using the Nordic nRF-DK and GCC. In the process, we’ve built a device that transmits light levels over BLE.