Skip to main content

LEDs and Button Sample

Introduction

The LEDs and Button sample cycles through multiple LED colors on each press of the button using the on-board RGB LED. The LEDs and button are initialized using information from the board's device tree. This sample is compatible with the following boards:

Download

LEDs and ButtonNCS v3.2.1

Project configuration

The application is configured in the prj.conf file. For this sample only the GPIO driver is needed:

CONFIG_GPIO=y

CONFIG_GPIO enables the GPIO driver, which is used to read the button input and control the LED outputs.

The sysbuild.conf file enables MCUBoot so the sample can be flashed over USB:

SB_CONFIG_BOOTLOADER_MCUBOOT=y

Code explanation

Device tree references

The sample uses Zephyr's device tree API to access the hardware. A reference to the GPIO controller is obtained using its node label. The LEDs and button are accessed through their aliases:

#define GPIO_NODE	DT_NODELABEL(gpio0)
#define BUTTON_NODE DT_ALIAS(sw0)

#define RED_LED_NODE DT_ALIAS(led0)
#define GREEN_LED_NODE DT_ALIAS(led1)
#define BLUE_LED_NODE DT_ALIAS(led2)

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

static struct gpio_dt_spec red_led = GPIO_DT_SPEC_GET(RED_LED_NODE, gpios);
static struct gpio_dt_spec green_led = GPIO_DT_SPEC_GET(GREEN_LED_NODE, gpios);
static struct gpio_dt_spec blue_led = GPIO_DT_SPEC_GET(BLUE_LED_NODE, gpios);

The gpios parameter corresponds to the property name in the device tree that contains the pin information.

Button configuration

The button is configured as an input with an interrupt callback in the init_button function. This allows the application to respond to button presses immediately:

bool init_button(void)
{
int 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 false;
}

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 false;
}

gpio_init_callback(&gpio_cb, button_pressed_callback, BIT(button.pin));
gpio_add_callback(button.port, &gpio_cb);

return true;
}

The callback sets a flag that the main loop checks:

void button_pressed_callback(const struct device *gpiob, struct gpio_callback *cb,
gpio_port_pins_t pins)
{
button_pressed = true;
}

LED configuration

Each LED pin is configured as an output, initialized to inactive (off):

void init_leds(void)
{
gpio_pin_configure_dt(&red_led, GPIO_OUTPUT_INACTIVE);
gpio_pin_configure_dt(&green_led, GPIO_OUTPUT_INACTIVE);
gpio_pin_configure_dt(&blue_led, GPIO_OUTPUT_INACTIVE);
}

The LEDs are defined as active low in the device tree, meaning a logic 0 turns them off (hardware pin high) and a logic 1 turns them on (hardware pin low). The gpio_pin_configure_dt and gpio_pin_set_dt functions handle this inversion automatically.

Color cycling

The sample defines six colors using an enum. Single colors use one LED; compound colors (magenta, cyan, yellow) are created by turning on two LEDs at the same time:

typedef enum {
RED,
GREEN,
BLUE,
MAGENTA,
CYAN,
YELLOW
} led_color_t;

For example, yellow is created by turning on both the red and green LEDs:

case YELLOW:
gpio_pin_set_dt(&red_led, LED_ON);
gpio_pin_set_dt(&green_led, LED_ON);
break;

Main loop

The main loop checks the button_pressed flag, advances the color, and updates the LEDs. It starts with green and cycles through red, green, blue, magenta, cyan, and yellow:

led_color_t color = GREEN;
turn_leds_on_with_color(color);

while (1) {
if (button_pressed) {
color++;
if (color > YELLOW) {
color = RED;
}

turn_leds_off();
turn_leds_on_with_color(color);

button_pressed = false;
}

k_sleep(K_MSEC(100));
}
Was this page helpful?