Table of Contents
- Introduction
- Creating a New Project in VS Code
- Adding Button Input Code
- Code Breakdown
- Building and Flashing the Project
- Conclusion
Introduction
If you're working with Nordic’s nRF7002 DK, you’re likely using it alongside a host like the nRF5340 SoC. While the nRF7002 handles Wi-Fi, GPIOs (including buttons) are handled by the host. In this post, we’ll show you how to handle button inputs using Zephyr RTOS, the nRF Connect SDK, and the nRF Connect extension in VS Code.
Creating a New Project in VS Code
Inside VS Code:
- Click the nRF Connect icon in the sidebar.
- Select “Create a new application”
- Fill out:
- Application Name (e.g.,
button_example
) - Template: choose
empty
- SDK Version: the version you installed
- Application Name (e.g.,
Once created, you’ll see your project structure in the Explorer.
Adding Button Input Code
Edit prj.conf
:
CONFIG_GPIO=y
Replace main.c
with:
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#define BUTTON_NODE DT_ALIAS(sw0)
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(BUTTON_NODE, gpios, {0});
static struct gpio_callback button_cb_data;
void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
printk("Button pressed at pin %d\n", button.pin);
}
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);
}
Code Breakdown
Let’s go through the code step by step to understand how it works.
1. Include Necessary Headers
The necessary Zephyr headers are included at the top of the file:
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
kernel.h
is for Zephyr’s kernel functionality.device.h
gives access to the device model, allowing interaction with hardware like GPIOs.gpio.h
provides functions specific to GPIO pins, which is crucial for interacting with buttons.
2. Device Tree Alias for Button
We define the device tree alias for the button using Zephyr’s device tree macros:
#define BUTTON_NODE DT_ALIAS(sw0)
- The button
sw0
is referenced usingDT_ALIAS(sw0)
, which connects to the device tree definition for the button, usually defined in the board's.dts
file.
3. Declare GPIO Spec and Callback
We declare the GPIO device specification (gpio_dt_spec
) and a GPIO callback structure:
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(BUTTON_NODE, gpios, {0});
static struct gpio_callback button_cb_data;
button
specifies the GPIO pin configuration forsw0
.button_cb_data
will hold the callback function triggered by the button press event.
4. Button Press Callback Function
The callback function button_pressed()
gets triggered whenever the button is pressed:
void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
printk("Button pressed at pin %d\n", button.pin);
}
- This function prints a message when the button is pressed, using
printk()
, which is Zephyr’s logging function.
5. Main Function – GPIO Setup
The main function does several important things:
a. Check if Device is Ready
if (!device_is_ready(button.port)) {
printk("Error: button device %s is not ready\n", button.port->name);
return;
}
- This checks if the button device (
button.port
) is ready for use. If not, it logs an error and returns.
b. Configure GPIO Pin as Input
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;
}
- This sets up the button GPIO pin as an input pin. The function
gpio_pin_configure_dt()
is used to configure the pin based on the device tree specification.
c. Configure Interrupt for Button Press
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;
}
- This configures the button to generate an interrupt on a rising edge (button press). The interrupt will trigger the callback function defined earlier.
d. Initialize the GPIO Callback
gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
gpio_add_callback(button.port, &button_cb_data);
gpio_init_callback()
initializes the callback function for the button press.gpio_add_callback()
registers the callback function with the GPIO device, so it gets invoked on interrupt.
6. Logging Information
At the end of the main function, a log message is printed to confirm that the button is set up correctly:
printk("Set up button at %s pin %d\n", button.port->name, button.pin);
Building and Flashing the Project
To Build:
- Create a new build configuration, and choose nRF7002dk board
- Click the "Generate and Build" button
To Flash:
- Plug in the board via USB
- Click the "Flash" button in the sidebar
- You’ll see log output from the board via the "Terminal" or "Serial Monitor"
Press the sw0 button and watch your log say:
Button pressed at pin 8
Conclusion
Handling GPIO button inputs on the nRF7002 DK is simple once your development environment is properly set up. Using the nRF Connect SDK with VS Code provides a smooth workflow, and Zephyr’s powerful APIs make hardware interaction clean and readable.
Now that you’ve mastered basic input, you can expand into LEDs, sensors, and eventually Wi-Fi-enabled logic with the nRF7002’s companion features.
Happy building!