Since your development environment is almost completely setup, we can now start to develop a simple led blinking application for our demo board. The purpose of this application is extremely simple but allows to setup the nRF51’s SDK and see how to manipulate nRF51’s registers. First you need to download the SDK v9.0 at the following address: http://developer.nordicsemi.com/nRF51_SDK/

  • Uncompress and place the nRF51’s SDK in sdk folder:
        $ cd nrf51/
        $ mkdir sdk
        $ unzip nRF51_SDK_9.0.0_2e23562.zip -d nRF51_SDK_9.0.0_2e23562
        $ mv nRF51_SDK_9.0.0_2e23562 YOUR_PROJECT_PATH/nrf51/sdk

To be able to use the SDK from our application we need to include SDK’s source files in our Makefile. To do that we will develop a new Makefile which will be included from our project Makefile. The reason to split the Makefile is to manage dependencies in a better way. For example in a case we use only one Makefile located in led_blinking project directory, it would mean that for each new project we would create, we would need to copy the Makefile part corresponding to the SDK sources. Thanks to this new dedicated Makefile we will not have to copy it to each of our future projects we will develop.

  • Create new Makefile in sdk/ folder
        $ cd sdk/
        $ touch sdk.mk
  • Add the following code in sdk.mk
SDK_DIRECTORY = ../sdk/nRF51_SDK_9.0.0_2e23562/components

# System
INCLUDES  += $(SDK_DIRECTORY)/toolchain
INCLUDES  += $(SDK_DIRECTORY)/toolchain/arm
INCLUDES  += $(SDK_DIRECTORY)/toolchain/gcc
INCLUDES  += $(SDK_DIRECTORY)/device

SRC       += $(SDK_DIRECTORY)/toolchain/system_nrf51.c

# Drivers
ifeq ($(USE_DRV_BLE_FLASH), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/ble_flash
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/ble_flash -name *.c)
CPFLAGS   += -DUSE_DRV_BLE_FLASH
endif
ifeq ($(USE_DRV_CLK), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/clock
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/clock -name *.c)
CPFLAGS   += -DUSE_DRV_CLK
endif
ifeq ($(USE_DRV_GPIOTE), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/gpiote
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/gpiote -name *.c)
CPFLAGS   += -DUSE_DRV_GPIOTE
endif
ifeq ($(USE_DRV_LPCOMP), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/lpcomp
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/lpcomp -name *.c)
CPFLAGS   += -DUSE_DRV_LPCOMP
endif
ifeq ($(USE_DRV_NOSD), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/nrf_soc_nosd
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/nrf_soc_nosd -name *.c)
CPFLAGS   += -DUSE_DRV_NOSD
endif
ifeq ($(USE_DRV_PPI), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/ppi
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/ppi -name *.c)
CPFLAGS   += -DUSE_DRV_PPI
endif
ifeq ($(USE_DRV_PSTORAGE), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/pstorage
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/pstorage -name *.c)
CPFLAGS   += -DUSE_DRV_PSTORAGE
endif
ifeq ($(USE_DRV_QDEC), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/qdec
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/qdec -name *.c)
CPFLAGS   += -DUSE_DRV_QDEC
endif
ifeq ($(USE_DRV_RADIO_CFG), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/radio_config
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/radio_config -name *.c)
CPFLAGS   += -DUSE_DRV_RADIO_CFG
endif
ifeq ($(USE_DRV_RNG), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/rng
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/rng -name *.c)
CPFLAGS   += -DUSE_DRV_RNG
endif
ifeq ($(USE_DRV_RTC), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/rtc
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/rtc -name *.c)
CPFLAGS   += -DUSE_DRV_RTC
endif
ifeq ($(USE_DRV_SDIO), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/sdio
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/sdio -name *.c)
CPFLAGS   += -DUSE_DRV_SDIO
endif
ifeq ($(USE_DRV_SPI_SLV), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/spi_slave
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/spi_slave -name *.c)
CPFLAGS   += -DUSE_DRV_SPI_SLV
endif
ifeq ($(USE_DRV_SPI_MSTR), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/spi_master
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/spi_master -name *.c)
CPFLAGS   += -DUSE_DRV_SPI_MSTR
endif
ifeq ($(USE_DRV_SWI), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/swi
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/swi -name *.c)
CPFLAGS   += -DUSE_DRV_SWI
endif
ifeq ($(USE_DRV_TIMER), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/timer
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/timer -name *.c)
CPFLAGS   += -DUSE_DRV_TIMER
endif
ifeq ($(USE_DRV_TWI_MSTR), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/twi_master
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/twi_master -name *.c)
CPFLAGS   += -DUSE_DRV_TWI_MSTR
endif
ifeq ($(USE_DRV_UART), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/uart
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/uart -name *.c)
CPFLAGS   += -DUSE_DRV_UART
endif
ifeq ($(USE_DRV_WDT), y)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/wdt
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/wdt -name *.c)
CPFLAGS   += -DUSE_DRV_WDT
endif

INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/hal
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/hal -name *.c)
INCLUDES  += $(SDK_DIRECTORY)/drivers_nrf/common
SRC 	  += $(shell find $(SDK_DIRECTORY)/drivers_nrf/common -name *.c)


# Libraries
ifeq ($(USE_LIB_DFU), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/bootloader_dfu
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/bootloader_dfu -name *.c)
CPFLAGS   += -DUSE_LIB_DFU
endif
ifeq ($(USE_LIB_BUTTON), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/button
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/button -name *.c)
CPFLAGS   += -DUSE_LIB_BUTTON
endif
ifeq ($(USE_LIB_CONSOLE), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/console
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/console -name *.c)
CPFLAGS   += -DUSE_LIB_CONSOLE
endif
ifeq ($(USE_LIB_CRC16), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/crc16
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/crc16 -name *.c)
CPFLAGS   += -DUSE_LIB_CRC16
endif
ifeq ($(USE_LIB_FIFO), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/fifo
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/fifo -name *.c)
CPFLAGS   += -DUSE_LIB_FIFO
endif
ifeq ($(USE_LIB_GPIOTE), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/gpiote
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/gpiote -name *.c)
CPFLAGS   += -DUSE_LIB_GPIOTE
endif
ifeq ($(USE_LIB_HCI), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/hci
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/hci -name *.c)
CPFLAGS   += -DUSE_LIB_HCI
endif
ifeq ($(USE_LIB_IC_INFO), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/ic_info
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/ic_info -name *.c)
CPFLAGS   += -DUSE_LIB_IC_INFO
endif
ifeq ($(USE_LIB_MEM_MNG), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/mem_manager
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/mem_manager -name *.c)
CPFLAGS   += -DUSE_LIB_MEM_MNG
endif
ifeq ($(USE_LIB_PWM), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/pwm
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/pwm -name *.c)
CPFLAGS   += -DUSE_LIB_PWM
endif
ifeq ($(USE_LIB_SCHEDULER), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/scheduler
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/scheduler -name *.c)
CPFLAGS   += -DUSE_LIB_SCHEDULER
endif
ifeq ($(USE_LIB_SENSORSIM), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/sensorsim
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/sensorsim -name *.c)
CPFLAGS   += -DUSE_LIB_SENSORSIM
endif
ifeq ($(USE_LIB_SH256), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/sha256
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/sha256 -name *.c)
CPFLAGS   += -DUSE_LIB_SH256
endif
ifeq ($(USE_LIB_SIMPLE_TIMER), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/simple_timer
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/simple_timer -name *.c)
CPFLAGS   += -DUSE_LIB_SIMPLE_TIMER
endif
ifeq ($(USE_LIB_TIMER), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/timer
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/timer -name *.c)
CPFLAGS   += -DUSE_LIB_TIMER
endif
ifeq ($(USE_LIB_TRACE), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/trace
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/trace -name *.c)
CPFLAGS   += -DUSE_LIB_TRACE
endif
ifeq ($(USE_LIB_UART), y)
INCLUDES  += $(SDK_DIRECTORY)/libraries/uart
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/uart -name *.c)
CPFLAGS   += -DUSE_LIB_UART
endif

INCLUDES  += $(SDK_DIRECTORY)/libraries/util
SRC 	  += $(shell find $(SDK_DIRECTORY)/libraries/util -name *.c)

ifeq ($(USE_SD110), y)
# SD110, BLE stack
INCLUDES  += $(SDK_DIRECTORY)/softdevice/common
INCLUDES  += $(SDK_DIRECTORY)/softdevice/s110
endif

Because each project doesn’t necessarily need to use same drivers and/or libraries, drivers and libraries sources are compiled only if a dedicated flag is defined. Fo example if we want to use a uart driver in our project we will need to add USE_DRV_UART := y in the project’ Makefile.
We also need to specify our processor family to the SDK since it has been developed to support several processors. If you have a look to nrf.h file placed in sdk/nRF51_SDK_9.0.0_2e23562/components/device/nrf.h you will see the following code:

/* Family selection for family includes. */
#if defined (NRF51)
    #include "nrf51.h"
    #include "nrf51_bitfields.h"
    #include "nrf51_deprecated.h"
#elif defined (NRF52)
    #include "nrf52.h"
    #include "nrf52_bitfields.h"
    #include "nrf51_to_nrf52.h"
#else
    #error "Device family must be defined. See nrf.h."
#endif /* NRF51, NRF52 */

As you can see if we don’t specify the processor family it will raise an exception during build. So we need to add a flag in our project Makefile as following:

...
# Define target processor
TARGET 	:= 	NRF51
CPFLAGS += -D$(TARGET) 

# Generate dependency information
CPFLAGS += -MD -MP -MF .dep\$(@F).d
...

If you look in sdk/nRF51_SDK_9.0.0_2e23562/components/drivers_nrf/config directory you’ll find the nrf_drv_config.h file. This file allows you to setup some specific drivers, since it’s project dependent we will copy this file directly in our project directory:

        $ cd nrf51/led_blinking/
        $ mkdir cfg
        $ cp sdk/nRF51_SDK_9.0.0_2e23562/components/drivers_nrf/config/nrf_drv_config.h nrf51/led_blinking/cfg/

To include this header in our project we will add “INCLUDES += cfg/ ” in the project’ makefile:

...
# include directory
INCLUDES = include/   
INCLUDES += cfg/    
...

Since our first project won’t use the bluetooth low energy stack we have to use nrf_soc_nosd driver. To use it we just have to add “USE_DRV_NOSD := y” flag in the project’s makefile, as below:

...
# Drivers
USE_DRV_NOSD := y

include ../sdk/sdk.mk
...

We can now code the led blinking feature in our project. In a first time We’re gonna use the green led as blinking led. As you can see in PCA20006_Schematic_And_PCB.pdf document the green led is connected to gpio P0.12 (you can download this document from Nordic semiconductor website). The goal of the code below is to set gpio P0.12 to 1 wait 300ms then set gpio P0.12 to 0 and wait again 300ms. This code will be place in an infinite loop.

#include "nrf51.h"
#include "nrf51_bitfields.h"  // nrf51 register declaration
#include "nrf_delay.h"

#define GREEN_LED_PIN	12  // Pin P0.12

int main(void){
	// setup led gpio in output mode
	NRF_GPIO->PIN_CNF[GREEN_LED_PIN] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
                                            | (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
                                            | (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
                                            | (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos)
                                            | (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);

	do{
		// set to 1 led gpio (bit 12 corresponding to pin 12)
		NRF_GPIO->OUTSET = (1UL << GREEN_LED_PIN);
		// wait 300ms
		nrf_delay_ms(300);
		// set to 0 led gpio (bit 12 corresponding to pin 12)
		NRF_GPIO->OUTCLR = (1UL << GREEN_LED_PIN);
		// wait 300ms
		nrf_delay_ms(300);
	}while(1); //infinite loop

    return 0;
}

As explained in nrf51 reference manual to setup a pin you need to access to PIN_CNF register and set different parameters as the pin direction, input buffer activation or not, pull configuration, drive configuration or pin sensing mechanism. In our example we just need to setup the led pin in output mode, we don’t need to use a pull up or a pull down resistor neither an input buffer and since we are in output mode we don’t need the sensing mechanism. So to set up pin 12 in output mode, accordingly to the reference manual, we need to set bit 0 to 1. Since we don’t use other features the register’s bit remain to 0.

Screen Shot 2015-11-22 at 6.41.56 PM
Screen Shot 2015-11-22 at 6.48.55 PM

It’s extremely simple to put a gpio to a high level, thanks to OUTSET register, we only need to set to 1 the bit corresponding to the pin number. To set a gpio to a low level, we use the same mechanism than the one we use for setting a high level except that we use OUTCLR register instead.

You can now make your project, in the next post I will explain how to flash the binary file into nrf51 flash memory.

        $ cd nrf51/
        $ make

If every thing is og you should be able to read this line on you terminal:

../toolchain/arm_cm0/bin/arm-none-eabi-objcopy     -O binary -S       led_blinking.elf led_blinking.bin

3 thoughts on “BLE application with nRF51822: Blinking a led

  1. Hello,

    I am following your tutorial but when I try to compile it seems not picking up the INCLUDE_DIR in the gcc command:

    arm-none-eabi-gcc -c -o src/main.o src/main.c

    And so it can not find the header files:
    #include “nrf51.h”

    I have trouble to see what is going wrong because I followed the guide step by step.

    Do you have any pointers?

    Kind regards,
    Faris

    Like

      1. Hi Jocelyn,

        Thank you for your reply, I managed to fix it in the meanwhile.
        In the Makefile I changed %o: %c to %.o : %.c
        After that it worked fine.

        Kind regards,
        Faris

        Like

Leave a comment