Feedback
Feedback

If you are having issues with the exercises, please create a ticket on DevZone: devzone.nordicsemi.com
Click or drag files to this area to upload. You can upload up to 2 files.

Dissecting the blinky sample

v2.x.x

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 found at this location: <install_path>\zephyr\samples\basic\blinky.

1. Include modules

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

#include <zephyr/kernel.h>
#include <zephyr/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. Retrieve the device pointer, pin number, and configuration flags.

The macro call GPIO_DT_SPEC_GET() returns the structure gpio_dt_spec led, which contains the device pointer for node led_0 as well as the pin number and associated configuration flags. The node identifier LED0_NODE, defined in the previous step, has this information embedded inside its gpios property. Note the second parameter gpios, the name of the property containing all this information.

static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);

Now let’s examine main().

4. Verify that the device is ready for use

As we mentioned before, we must pass the device pointer of the gpio_dt_spec , in this case led, to gpio_is_ready_dt(), to verify that the device is ready for use.

	if (!gpio_is_ready_dt(&led)) {
		return 0;
	}

5. Configure the GPIO pin

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

int ret;

ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
    return;
}

6. Continuously toggle the GPIO pin

Finally, the blinky main function will enter an infinite loop where we continuously toggle the GPIO pin using gpio_pin_toggle_dt(). Note that in every iteration, 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) {
    ret = gpio_pin_toggle_dt(&led);
    if (ret < 0) {
        return;
    }
    k_msleep(SLEEP_TIME_MS);
}
v1.6.0 – v1.9.1

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 (!). 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);
}
Register an account
Already have an account? Log in
(All fields are required unless specified optional)

  • 8 or more characters
  • Upper and lower case letters
  • At least one number or special character

Forgot your password?
Enter the email associated with your account, and we will send you a link to reset your password.