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.
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
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
LikeLike
Hi Faris, could you send me the output you got when you run the make command?
LikeLike
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
LikeLike