You are currently not logged in and your progress will not be saved. Register or Log in

Dissecting the blinky sample

Now that we have examined the devicetree, device driver model and the GPIO generic API, let’s dissect the blinky sample program to understand how it works.

In the following paragraphs, we will examine the blinky sample line by line to understand how this program is working. The blinky sample comes with the nRF Connect SDK and can be downloaded below:

blinky sample source code

1. Include modules

The blinky sample uses the following modules in the nRF Connect SDK:

The header files of these modules are included in main.c through the following include lines.

#include <zephyr.h> 
#include <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>

2. Define the node identifier

The line below uses the devicetree macro DT_ALIAS() to get the node identifier symbol LED0_NODE, which will represent LED1 (node led_0). Remember from the Devicetree section that led_0 node is defined in the devicetree of the DK. LED0_NODE is now the source code symbol that represents the hardware for LED1.

The DT_ALIAS() macro gets the node identifier from the node’s alias, which as we saw in the Devicetree section, is led0.

#define LED0_NODE DT_ALIAS(led0) // LED0_NODE = led0 defined in the .dts file

Note

There are many ways to retrieve the node identifier. The macros DT_PATH()DT_NODELABEL()DT_ALIAS(), and DT_INST() all return the node identifier, based on various parameters.

3. Extract information from the node identifier

The symbol LED0_NODE contains information about the GPIO port, PIN, flags, and a label embedded inside its gpios property. The macro calls below extract this information from the LED0_NODE. Note the second parameter for these macros gpios. This is the name of the property containing all this information.

//get the port - in the case of the nRF52833 DK, LED0 = "GPIO_0" 
#define LED0    DT_GPIO_LABEL(LED0_NODE, gpios) 
//get the pin - in the case of the nRF52833 DK, PIN = 13
#define PIN DT_GPIO_PIN(LED0_NODE, gpios)  
//get the flags - in the case of the nRF52833 DK, FLAGS = GPIO_ACTIVE_LOW
#define FLAGS   DT_GPIO_FLAGS(LED0_NODE, gpios) 

4. Catch statement for error handling

The following lines will only be executed if the devicetree file of the selected board does not contain an alias node named led0, which is not the case for the nRF52833 DK or any other Nordic Semiconductor official development kit, so they are simply skipped.

#error "Unsupported board: led0 devicetree alias is not defined"
#define LED0    ""
#define PIN 0
#define FLAGS   0

Now let’s examine main().

5. Define a pointer to the device structure

As we mentioned before, for each hardware instance (i.e peripheral or system block) you plan to access in your application, you need to have a device driver associated with it. In the blinky example, we only have one peripheral to interact with, which is the GPIO peripheral. Therefore, we need to declare one pointer of type const struct device to point to its driver implementation as shown below:

const struct device *dev;

6. Extract the device driver implementation

We will use the function device_get_binding() to get the device driver implementation associated with the GPIO peripheral on the board.

dev = device_get_binding(LED0);
if (dev == NULL) {
    return;
}

The device_get_binding() function expects a string as a parameter to look up the driver implementation associated with the hardware, as explained in the Device Driver Model section.  

7. Configure the GPIO pin

The generic GPIO API function gpio_pin_configure() is used to configure GPIO pin 13 as an output (active low) and initializes it to a logic 1, as explained in the GPIO Generic API section.

ret = gpio_pin_configure(dev, PIN, GPIO_OUTPUT_ACTIVE | FLAGS);
if (ret < 0) {
    return;
}

8. Continuously set GPIO pin value to variable led_is_on

Finally, the blinky main function will enter an infinite loop where we continuously set GPIO pin 13 to the value of the boolean variable led_is_on. Note that in every iteration, we are inverting this variable through the logical negation operator (!) and we are also calling the kernel service function k_msleep(), which puts the main function to sleep for 1 second, resulting in the blinking behavior at 1-second intervals.

while (1) {
    gpio_pin_set(dev, PIN, (int)led_is_on);
    led_is_on = !led_is_on;
    k_msleep(SLEEP_TIME_MS);
}