nRF51822 Begins – nRF-DK, GCC, ADC, UART/BLE




 

nRF51822 Begins – nRF-DK, GCC, ADC, UART/BLE


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:


  1. 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.
  2. 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.
  3. 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.
  4. 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:


  1. Nordic nRF51 SDK. As of this writing the latest version is 8.1.0.
  2. Segger J-Link command utility for your platform.
  3. ARM GCC compiler.

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\4.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...\n");

    // 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\nr\ng\nexit\n" > 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\nr\ng\nexit\n" > 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:


 


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.


References


  1. Getting started with nRF51 development on Mac OS X by Eirik Midttun.
  2. nRF51 SDK Documentation for S110 SoftDevice.