Exercise 1

v2.x.x

Controlling an LED through a button (polling based)

In this exercise, we will modify the blinky example so that LED1 is turned on only when button 1 is being pressed, as shown in the illustration below.

To do this, we will use the polling method, as discussed in GPIO Generic API. This is done by continuously polling the CPU to check if the button was pressed and then update the LED accordingly. In Exercise 2 that follows, we will learn how to use GPIO interrupts, which is more power-efficient than polling.

If we recall the schematic of the nRF52833 DK, there are four push-buttons connected to the same GPIO peripheral as the LEDs, GPIO0 (node label: gpio0). This can be seen in the GPIO pin mapping, for instance, notice that button 1 is connected to P0.11. The 0 in P0 symbolizes it is &gpio0.

nRF52833 DK button location
nRF52833 DK button pin mapping

This is why the same driver is needed for both the LEDs and buttons. This is also evident from the DK’s devicetree file nrf52833dk_nrf52833.dts.

Button 1 on the nRF52833 DK is given the node name button_0, which has the alias (sw0) and node label (button0) and is connected to &gpio0, pin 11. Remember, indexing in the devicetree files always starts from 0, which is why button 1 on the board is called button0.

gpios = <&gpio0 11 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;

Note

If the buttons are connected to a different GPIO (e.g. GPIO_1) than the LEDs, a new device pointer is required. Luckily, this is not the case for any official Nordic Semiconductor development kits.

Exercise steps

1. Clone the nRF Connect SDK Fundamentals GitHub repository.

Create a folder close to your root directory and clone the repository by running

git clone https://github.com/NordicDeveloperAcademy/nRF-Connect-SDK-Fundamentals

2. Open the exercise code base for this exercise in VS Code.

2.1 From the Welcome View in the nRF Connect extension, click on Add an existing application.

Select the base code for this exercise, found in nRF-Connect-SDK-Fundamentals/v2.x.x/lesson2/fund_less2_exer1, and click on Select Folder.

2.2 Open main.c from the Explorer in VS Code as shown in the screenshot below:

3. Initialize the button on the hardware.

3.1 Get the node identifier for button 1 through its alias sw0.

Recall from the Devicetree section, that the /aliases node in the DK devicetree defined the alias sw0 for node &button0. The line below uses the macro call DT_ALIAS() to access the node identifier for the node through its alias.

In main.c , search for STEP 3.1 and add (copy and paste) the following lines:

#define SW0_NODE	DT_ALIAS(sw0) 

This is done to ensure compatibility across different hardware.

3.2 Get the device pointer, pin number and pin’s configuration flags through gpio_dt_spec.

The GPIO API has the struct called gpio_dt_spec that encapsulates all this information, and can be retrieved using GPIO_DT_SPEC_GET().

In main.c , search for STEP 3.2 and add (copy and paste) the following lines:

static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(SW0_NODE, gpios);

This line uses the macro GPIO_DT_SPEC_GET( ) to populate the members of the gpio_dt_spec variable with:

  • The device pointer: const struct device *port;
  • The pin number: gpio_pin_t pin;
  • The pin’s flags: gpio_dt_flags_t dt_flags;

4. It is important to verify that the device is ready to use by calling device_is_ready() before using it.

device_is_ready()

In main.c , search for STEP 4 and add (copy and paste) the following lines:

if (!device_is_ready(button.port)) {
	return;
}

5. Configure the pin connected to the button to be an input pin and set its hardware specifications.

Inside main(), search for STEP 5 and add the following lines:

ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
if (ret < 0) {
	return;
}

6. In the main loop of our exercise, we will indefinitely poll the CPU to read the status of the button (pressed = 1, unpressed = 0) and update the LED to the status of the button.

6.1 Read the status of the button and store it.

Using gpio_pin_get_dt(), read the current status of button.pin and save it in the variable val.

Search for STEP 6.1 and add the following code inside the infinite loop:

bool val = gpio_pin_get_dt(&button);

6.2 Update the LED to the status of the button

Update the LED to reflect the current status of the button that was saved in the variable val in the previous step, using gpio_pin_set_dt().

Search for STEP 6.2 and add the following code inside the infinite loop:

 gpio_pin_set_dt(&led,val);

7. Change the sleep time from 1000 ms to 100 ms. 100 ms is about the right time to put the main thread to sleep and still be able to respond in time when the button is pressed.

8. Add a build configuration, like we did in Lesson 1 Exercise 2.

9. Build the exercise and flash it to the board as we have done in the previous lesson. Observe that when button 1 is pressed, LED1 is turned ON.

The solution for this exercise can be found in the GitHub repository, lesson2/fund_less2_exer1_solution of whichever version directory you are using (v2.x.x or v1.6.0-v1.9.1)

v1.6.0 – v1.9.1

Controlling an LED through a button (polling based)

In this exercise, we will modify the blinky example so that LED1 is turned on only when button 1 is being pressed, as shown in the illustration below.

To do this, we will use the polling method, as discussed in GPIO Generic API. This is done by continuously polling the CPU to check if the button was pressed and then update the LED accordingly. In Exercise 2 that follows, we will learn how to use GPIO interrupts, which is more power-efficient than polling.

If we recall the schematic of the nRF52833 DK, there are four push-buttons connected to the same GPIO peripheral (GPIO_0 , aka: gpio0) as the LEDs. This can be seen in the GPIO pin mapping. For example, notice that button 1 is connected to P0.11. The 0 in P0 symbolizes it is GPIO_0.

nRF52833 DK button location
nRF52833 DK button pin mapping

This is why the same driver, the GPIO_0 driver, is needed for both the LEDs and buttons. This is also evident from the DK’s devicetree file nrf52833dk_nrf52833.dts.

	buttons {
		compatible = "gpio-keys";
		button0: button_0 { 
			gpios = <&gpio0 11 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
			label = "Push button switch 0";
		};
		button1: button_1 {
			gpios = <&gpio0 12 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
			label = "Push button switch 1";
		};
/* ... */
		};
	};

/* ... */
	/* These aliases are provided for compatibility with samples */
	aliases {
/* ... */
		sw0 = &button0;
		sw1 = &button1;
		sw2 = &button2;
		sw3 = &button3;
		bootloader-led0 = &led0;
	};
};
nrf52833dk_nrf52833.dts file

Button 1 on the nRF52833 DK is given the node name button_0, which has two aliases (sw0 and button0) and is connected to GPIO_0, pin 11. Remember, indexing in the devicetree files always starts from 0, which is why button 1 on the board is called button0.

gpios = <&gpio0 11 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;

Note

If the buttons are connected to a different GPIO (ex. GPIO_1) than the LEDs, a new device driver binding is required. Luckily, this is not the case for any official Nordic Semiconductor development kits.

Exercise steps

1. Clone the nRF Connect SDK Fundamentals GitHub repository.

Create a folder close to your root directory and clone the repository by running

git clone https://github.com/NordicDeveloperAcademy/nRF-Connect-SDK-Fundamentals

2. Open the exercise code base for this exercise in VS Code.

2.1 From the Welcome View in the nRF Connect extension, click on Add an existing application.

Select the base code for this exercise, found in nRF-Connect-SDK-Fundamentals/v1.6.0-v1.9.1/lesson2/fund_less2_exer1, and click on Select Folder.

Open main.c from the Explorer in VS Code as shown in the screenshot below:

3. Define the symbol that will represent the button on the board. This is done by calling the appropriate devicetree APIs, same way as was done in the blinky example.

In main.c , search for STEP 3 and add (copy and paste) the following lines:

#define SW0_NODE    DT_ALIAS(sw0)

#if DT_NODE_HAS_STATUS(SW0_NODE, okay)
#define SW0_GPIO_LABEL  DT_GPIO_LABEL(SW0_NODE, gpios)
#define SW0_GPIO_PIN    DT_GPIO_PIN(SW0_NODE, gpios)
#define SW0_GPIO_FLAGS  DT_GPIO_FLAGS(SW0_NODE, gpios)
#else
#error "Unsupported board: sw0 devicetree alias is not defined"
#define SW0_GPIO_LABEL  ""
#define SW0_GPIO_PIN    0
#define SW0_GPIO_FLAGS  0
#endif

4. Configure the pin connected to the button to be an input pin and set its hardware specifications.

Inside main(), search for STEP 4 and add the following lines:

    ret = gpio_pin_configure(dev, SW0_GPIO_PIN, GPIO_INPUT | SW0_GPIO_FLAGS);
    if (ret < 0) {
        return;
    }

5. In the main loop of our exercise, we will indefinitely poll the CPU to read the status (pressed = 1 , unpressed = 0) of the button and update the LED to the status of the button.

Search for STEP 5, add the following code inside the infinite loop:


bool val; //A variable that stores the status of the button (pressed = 1 , unpressed = 0)
val = gpio_pin_get(dev, SW0_GPIO_PIN); //Read the status of the button
gpio_pin_set(dev, PIN, val); //Update the LED to the status of the button
k_msleep(SLEEP_TIME_MS); // Put the CPU to sleep for 100ms so it does not consume power

6. Change the sleep time from 1000 ms to 100 ms. 100 ms is about the right time to put the main thread to sleep and still be able to respond in time when the button is pressed.

7. Add a build configuration, like we did in Lesson 1 Exercise 2.

8. Build the exercise and flash it to the board as we have done in the previous lesson. Observe that when button 1 is pressed, LED1 is turned ON.

The solution for this exercise can be found in the GitHub repository, lesson2/fund_less2_exer1_solution of whichever version directory you are using (v2.x.x or v1.6.0-v1.9.1).

Register an account
Already have an account? Log in
(All fields are required unless specified optional)

Forgot your password?
Enter your email address, and we will send a link to reset your password.