Understanding basic button sample code

Hello,

I'm studying the code for the button sample program in the nrf in zephyr.

/*
 * Copyright (c) 2016 Open-RnD Sp. z o.o.
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr.h>
#include <device.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <sys/printk.h>
#include <inttypes.h>

#define SLEEP_TIME_MS	1

/*
 * Get button configuration from the devicetree sw0 alias. This is mandatory.
 */
#define SW0_NODE	DT_ALIAS(sw0)
#if !DT_NODE_HAS_STATUS(SW0_NODE, okay)
#error "Unsupported board: sw0 devicetree alias is not defined"
#endif
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios,
							      {0});
static struct gpio_callback button_cb_data;

/*
 * The led0 devicetree alias is optional. If present, we'll use it
 * to turn on the LED whenever the button is pressed.
 */
static struct gpio_dt_spec led = GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios,
						     {0});

void button_pressed(const struct device *dev, struct gpio_callback *cb,
		    uint32_t pins)
{
	printk("Button pressed at %" PRIu32 "\n", k_cycle_get_32());
}

void main(void)
{
	int ret;

	if (!device_is_ready(button.port)) {
		printk("Error: button device %s is not ready\n",
		       button.port->name);
		return;
	}

	ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
	if (ret != 0) {
		printk("Error %d: failed to configure %s pin %d\n",
		       ret, button.port->name, button.pin);
		return;
	}

	ret = gpio_pin_interrupt_configure_dt(&button,
					      GPIO_INT_EDGE_TO_ACTIVE);
	if (ret != 0) {
		printk("Error %d: failed to configure interrupt on %s pin %d\n",
			ret, button.port->name, button.pin);
		return;
	}

	gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
	
	gpio_add_callback(button.port, &button_cb_data);
	printk("Set up button at %s pin %d\n", button.port->name, button.pin);

	if (led.port && !device_is_ready(led.port)) {
		printk("Error %d: LED device %s is not ready; ignoring it\n",
		       ret, led.port->name);
		led.port = NULL;
	}
	if (led.port) {
		ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT);
		if (ret != 0) {
			printk("Error %d: failed to configure LED device %s pin %d\n",
			       ret, led.port->name, led.pin);
			led.port = NULL;
		} else {
			printk("Set up LED at %s pin %d\n", led.port->name, led.pin);
		}
	}

	printk("Press the button\n");
	if (led.port) {
		while (1) {
			/* If we have an LED, match its state to the button's. */
			int val = gpio_pin_get_dt(&button);

			if (val >= 0) {
				gpio_pin_set_dt(&led, val);
			}
			k_msleep(SLEEP_TIME_MS);
		}
	}
}

I would like to ask for assistance in understanding some parts of the code.

#include <device.h>

Is there a diffence between the above code and #include <zephyr/device.h>? which should be the better choice?

#if !DT_NODE_HAS_STATUS(SW0_NODE, okay)
#error "Unsupported board: sw0 devicetree alias is not defined"
#endif

May I know if this code is relevant? I assume this is used for unit testing?

void button_pressed(const struct device *dev, struct gpio_callback *cb,
		    uint32_t pins)
{
	printk("Button pressed at %" PRIu32 "\n", k_cycle_get_32());
}

This is the first time I have encountered "PRIu32", is this a time data?

if (led.port) {
		ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT);
		if (ret != 0) {
			printk("Error %d: failed to configure LED device %s pin %d\n",
			       ret, led.port->name, led.pin);
			led.port = NULL;
		} else {
			printk("Set up LED at %s pin %d\n", led.port->name, led.pin);
		}
	}

What is "led.port->name"? why does it point to name? I tried to look for it in the led0 part of nrf52840dk_nrf52840.dts and then to the gpio0 part of nrf52840.dtsi but only found "port = <0>;"

  • Hi,

    Is there a diffence between the above code and #include <zephyr/device.h>? which should be the better choice?

    This is just form different versions of nRF Connect SDK. With recent versions, you must use #include <zephyr/device.h>.

    May I know if this code is relevant? I assume this is used for unit testing?

    You can build Zephyr project for many different boards, and as this sample needs a button it is sensible to make sure you get an error if you try to build for a board that does not have a button. You can remove this if you want to.

    This is the first time I have encountered "PRIu32", is this a time data?

    It is a way to explicitly print a 32 bit unsigned integer.

    What is "led.port->name"? why does it point to name?

    this comes from the device tree. If you are building for anRF52840 DK, the "name" will be gpio@50000000 because 0x50000000 is the base address for GPIOTE port 0. You can find it in zephyr/dts/arm/nordic/nrf52840.dtsi:

    		gpio0: gpio@50000000 {
    			compatible = "nordic,nrf-gpio";
    			gpio-controller;
    			reg = <0x50000000 0x200
    			       0x50000500 0x300>;
    			#gpio-cells = <2>;
    			status = "disabled";
    			port = <0>;
    		};

  • It is true (meaning not 0) if led0 exists in the device tree. The reason is is done like this is that a LED is optional in this sample, as you can see from this comment where led is declared:

    /*
     * The led0 devicetree alias is optional. If present, we'll use it
     * to turn on the LED whenever the button is pressed.
     */
    static struct gpio_dt_spec led = GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios,
    						     {0});

  • This is understood.

    Next question I have is for:

    static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios,
    							      {0});

    Is there a difference between GPIO_DT_SPEC_GET and GPIO_DT_SPEC_GET_OR?

    when do you use which?

  • The difference is that GPIO_DT_SPEC_GET_OR has a default/fallback value which is used if the node and property is not found in the device tree. This makes more sense in generic samples like this which is supposed to work on many different board, and here the LED is optional. In most projects it is not something you would use.

Related