I'm trying to control the brightness of the LEDs of my dev kit with the buttons

Hi,

I’m really new to zephyr and nrf SDK (I currently use nrf connect on vscode) and I have a project which consist in remotely controling the brightness of the 4 LEDs built in my nrf52840 dk using bluetooth mesh (I have 3 kits). I apologize in advance if I’m asking stupid questions but I’m writing this post after looking on the forum for some answers.

For now I’m just trying to understand how to control the brightness with the “led_pwm” sample included and I would like in the first place to do some tests on the same board (without any bluetooth). The thing is that :

In one hand, i’ve managed to turn on and off the LEDs of my board with that code :

#include <drivers/gpio.h>
#include <settings/settings.h>
#include <random/rand32.h>
// #include <bluetooth/bluetooth.h>
// #include <bluetooth/mesh.h>
// #include <bluetooth/mesh/proxy.h>

//GPIOs for Buttons

// Button 1
#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 button1 = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios,
							      {0});
static struct gpio_callback gpio_btn1_cb;

#ifndef SW0_GPIO_FLAGS
#ifdef SW0_GPIO_PIN_PUD
#define SW0_GPIO_FLAGS SW0_GPIO_PIN_PUD
#else
#define SW0_GPIO_FLAGS 0
#endif
#endif

// Button 2
#define SW1_NODE	DT_ALIAS(sw1)
#if !DT_NODE_HAS_STATUS(SW1_NODE, okay)
#error "Unsupported board: sw1 devicetree alias is not defined"
#endif
static const struct gpio_dt_spec button2 = GPIO_DT_SPEC_GET_OR(SW1_NODE, gpios,
							      {0});
static struct gpio_callback gpio_btn2_cb;

#ifndef SW1_GPIO_FLAGS
#ifdef SW1_GPIO_PIN_PUD
#define SW1_GPIO_FLAGS SW1_GPIO_PIN_PUD
#else
#define SW1_GPIO_FLAGS 0
#endif
#endif

// Button 3
#define SW2_NODE	DT_ALIAS(sw2)
#if !DT_NODE_HAS_STATUS(SW2_NODE, okay)
#error "Unsupported board: sw2 devicetree alias is not defined"
#endif
static const struct gpio_dt_spec button3 = GPIO_DT_SPEC_GET_OR(SW2_NODE, gpios,
							      {0});
static struct gpio_callback gpio_btn3_cb;

#ifndef SW2_GPIO_FLAGS
#ifdef SW2_GPIO_PIN_PUD
#define SW2_GPIO_FLAGS SW2_GPIO_PIN_PUD
#else
#define SW2_GPIO_FLAGS 0
#endif
#endif

// Button 4
#define SW3_NODE	DT_ALIAS(sw3)
#if !DT_NODE_HAS_STATUS(SW3_NODE, okay)
#error "Unsupported board: sw3 devicetree alias is not defined"
#endif
static const struct gpio_dt_spec button4 = GPIO_DT_SPEC_GET_OR(SW3_NODE, gpios,
							      {0});
static struct gpio_callback gpio_btn4_cb;

#ifndef SW3_GPIO_FLAGS
#ifdef SW3_GPIO_PIN_PUD
#define SW3_GPIO_FLAGS SW3_GPIO_PIN_PUD
#else
#define SW3_GPIO_FLAGS 0
#endif
#endif

#define BUTTON_DEBOUNCE_DELAY_MS 250

// for debouncing the four buttons
static uint32_t btn_time[4] = { 0,0,0,0};
static uint32_t btn_last_time[4] = { 0,0,0,0};

static struct gpio_callback gpio_btn1_cb;
static struct gpio_callback gpio_btn2_cb;
static struct gpio_callback gpio_btn3_cb;
static struct gpio_callback gpio_btn4_cb;

// GPIO for LEDs
#define LED1_NODE	DT_ALIAS(led0)
static struct gpio_dt_spec ledN1 = GPIO_DT_SPEC_GET_OR(LED1_NODE, gpios,
						     {0});
#define LED2_NODE	DT_ALIAS(led1)
static struct gpio_dt_spec ledN2 = GPIO_DT_SPEC_GET_OR(LED2_NODE, gpios,
						     {0});
#define LED3_NODE	DT_ALIAS(led2)
static struct gpio_dt_spec ledN3 = GPIO_DT_SPEC_GET_OR(LED3_NODE, gpios,
						     {0});
#define LED4_NODE	DT_ALIAS(led3)
static struct gpio_dt_spec ledN4 = GPIO_DT_SPEC_GET_OR(LED4_NODE, gpios,
						     {0});

// for use with k_work_submit which we use to handle button presses in a background thread to avoid holding onto an IRQ for too long
static struct k_work button1_work;
static struct k_work button2_work;
static struct k_work button3_work;
static struct k_work button4_work;

void ledOn(struct gpio_dt_spec led_nbr) {
	gpio_pin_set(led_nbr.port, led_nbr.pin, 1);
}

void ledOff(struct gpio_dt_spec led_nbr) {
	gpio_pin_set(led_nbr.port, led_nbr.pin, 0);
}


// Buttons
// -------
void button1_work_handler(struct k_work *work)
{
	ledOn(ledN1);
	k_msleep(200);
	ledOff(ledN1);
	k_msleep(200);
	ledOn(ledN1);
	k_msleep(200);
	ledOff(ledN1);
	k_msleep(200);
}

void button2_work_handler(struct k_work *work)
{
	ledOn(ledN2);
	k_msleep(200);
	ledOff(ledN2);
	k_msleep(200);
	ledOn(ledN2);
	k_msleep(200);
	ledOff(ledN2);
	k_msleep(200);

}

void button3_work_handler(struct k_work *work)
{	

}

void button4_work_handler(struct k_work *work)
{
}

bool debounce(int btn_inx) {
	bool ignore = false;
	btn_time[btn_inx] = k_uptime_get_32();
	if (btn_time[btn_inx] < (btn_last_time[btn_inx] + BUTTON_DEBOUNCE_DELAY_MS)) {
		ignore = true;
	} else {
		ignore = false;
	}
	btn_last_time[btn_inx] = btn_time[btn_inx];
	return ignore;
}

void button_1_pressed(const struct device *gpiob, struct gpio_callback *cb,
											uint32_t pins)
{
	// printk("Button 1 pressed at %d\n", k_cycle_get_32());

    if (!debounce(0)) {
	  k_work_submit(&button1_work);
	}
}

void button_2_pressed(const struct device *gpiob, struct gpio_callback *cb,
											uint32_t pins)
{
	// printk("Button 2 pressed at %d\n", k_cycle_get_32());
    if (!debounce(1)) {
	  k_work_submit(&button2_work);
	}
}

void button_3_pressed(const struct device *gpiob, struct gpio_callback *cb,
											uint32_t pins)
{
	// printk("Button 3 pressed at %d\n", k_cycle_get_32());
    if (!debounce(2)) {
	  k_work_submit(&button3_work);
	}
}

void button_4_pressed(const struct device *gpiob, struct gpio_callback *cb,
											uint32_t pins)
{
	// printk("Button 4 pressed at %d\n", k_cycle_get_32());
    if (!debounce(3)) {
	  k_work_submit(&button4_work);
	}
}

// -------------------------------------------------------------------------------------------------------
// LED
// -------
/**
 * @brief This function sets up GPIO to allow the board’s built-in LED to be switched on or off and switches it off to
begin with.
 */
void configureLED(void)
{
	// Led 1
	printk("configureLED1\n");

	int ret1 = 0;
	int ret2 = 0;

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


	printk("configureLED1\n");

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


// -------------------------------------------------------------------------------------------------------
// Buttons
// -------

/**
 * @brief This sets up GPIO so that pressing any of the four buttons on the board results in a callback to an
	associated handler function
 */
void configureButtons(void)
{
	printk("configureButtons\n");

	int ret;

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

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

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

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

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

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

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

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

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

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

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

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

}


void main(void)
{
	printk("switch\n");

	configureButtons();

	configureLED();
	
}

And on the other hand, I run another program which increase and decrease the brightness of 1 led (without buttons) :

/*
 * Copyright (c) 2020 Seagate Technology LLC
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <device.h>
#include <errno.h>
#include <drivers/led.h>
#include <sys/util.h>
#include <zephyr.h>

#include <logging/log.h>
LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL);

#if DT_NODE_HAS_STATUS(DT_INST(0, pwm_leds), okay)
#define LED_PWM_NODE_ID		DT_INST(0, pwm_leds)
#define LED_PWM_DEV_NAME	DEVICE_DT_NAME(LED_PWM_NODE_ID)
#else
#error "No LED PWM device found"
#endif

#define LED_PWM_LABEL(led_node_id) DT_PROP_OR(led_node_id, label, NULL),

const char *led_label[] = {
	DT_FOREACH_CHILD(LED_PWM_NODE_ID, LED_PWM_LABEL)
};

const int num_leds = ARRAY_SIZE(led_label);

#define MAX_BRIGHTNESS	100

#define FADE_DELAY_MS	10
#define FADE_DELAY	K_MSEC(FADE_DELAY_MS)

/**
 * @brief Run tests on a single LED using the LED API syscalls.
 *
 * @param led_pwm LED PWM device.
 * @param led Number of the LED to test.
 */
static void run_led_test(const struct device *led_pwm, uint8_t led)
{
	int err;
	uint16_t level;

	LOG_INF("Testing LED %d - %s", led, led_label[led] ? : "no label");

	/* Increase LED brightness gradually up to the maximum level. */
	LOG_INF("%s","  Increasing brightness gradually");
	for (level = 0; level <= MAX_BRIGHTNESS; level++) {
		err = led_set_brightness(led_pwm, led, level);
		if (err < 0) {
			LOG_ERR("err=%d brightness=%d\n", err, level);
			return;
		}
		k_sleep(FADE_DELAY);
	}
	k_sleep(K_MSEC(1000));

	/* Turn LED off. */
	err = led_off(led_pwm, led);
	if (err < 0) {
		LOG_ERR("err=%d", err);
		return;
	}
	LOG_INF("%s","  Turned off, loop end");
}

void main(void)
{
	const struct device *led_pwm;
	uint8_t led;

	led_pwm = device_get_binding(LED_PWM_DEV_NAME);
	if (led_pwm) {
		LOG_INF("Found device %s", LED_PWM_DEV_NAME);
	} else {
		LOG_ERR("Device %s not found", LED_PWM_DEV_NAME);
		return;
	}

	if (!num_leds) {
		LOG_ERR("No LEDs found for %s", LED_PWM_DEV_NAME);
		return;
	}

	do {
		for (led = 0; led < num_leds; led++) {
			run_led_test(led_pwm, led);
		}
	} while (true);
}

Now I need to “merge” these two codes (if it’s possible) but I have now idea how considering that the “led_set_brightness” function doesn’t seem to use gpios like the on/off function does. And also, I don’t understand what this part means (the #defines are different from the way I used them in the first program :

LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL);

#if DT_NODE_HAS_STATUS(DT_INST(0, pwm_leds), okay)
#define LED_PWM_NODE_ID		DT_INST(0, pwm_leds)
#define LED_PWM_DEV_NAME	DEVICE_DT_NAME(LED_PWM_NODE_ID)
#else
#error "No LED PWM device found"
#endif

#define LED_PWM_LABEL(led_node_id) DT_PROP_OR(led_node_id, label, NULL),

const char *led_label[] = {
	DT_FOREACH_CHILD(LED_PWM_NODE_ID, LED_PWM_LABEL)
};

const int num_leds = ARRAY_SIZE(led_label);

So I can’t make the led_pwm sample run on the 4 LEDs of my DK since I can’t figure out how to define the other leds.

I hope that I’ve been clear and that someone will be able to help me.

Kind regards,

Bastien

Parents
  • Hello Bastien, sorry about the wait.

    And no worries about the questions, everything is allowed here.

    the “led_set_brightness” function doesn’t seem to use gpios like the on/off function does. And also, I don’t understand what this part means (the #defines are different from the way I used them in the first program :

    The led_set_brightness function uses all the available pwm-leds as a node, not a specific one. When it comes to the rest of the defines:

    LOG_MODULE_REGISTER is only about logging, and not too relevant for this. For more info on logging see here.

    #if DT_NODE_HAS_STATUS(DT_INST(0, pwm_leds), okay)
    #define LED_PWM_NODE_ID		DT_INST(0, pwm_leds)
    #define LED_PWM_DEV_NAME	DEVICE_DT_NAME(LED_PWM_NODE_ID)
    #else
    #error "No LED PWM device found"
    #endif

    The above defines adds all the pwm-led that have an "okay" status to the pwm-leds node. 

    #define LED_PWM_LABEL(led_node_id) DT_PROP_OR(led_node_id, label, NULL),
    
    const char *led_label[] = {
    	DT_FOREACH_CHILD(LED_PWM_NODE_ID, LED_PWM_LABEL)
    };

    This adds all the pwm-led nodes to the led_label list.

    Note that you got the led_pwm sample (the second code snippet you provided) to work with only one LED, but the code is set to use all pwm-leds with an "okay" status. You can confirm that led1 is the only one set by taking a look at the .dts file of the board. If you add the code below to an overlay file (ie. nrf52840dk_nrf52840.overlay) you will see that all 4 of the LEDs are being used.

    / {
        pwmleds {
            compatible = "pwm-leds";
            pwm_led0: pwm_led_0 {
                pwms = < &pwm0 13 >;
            };
            pwm_led1: pwm_led_1 {
                pwms = < &pwm1 14 >;
            };	
            pwm_led2: pwm_led_2 {
                pwms = < &pwm2 15 >;
            };	
            pwm_led3: pwm_led_3 {
                pwms = < &pwm3 16 >;
                
            };	
        };
    };
    &pwm0 {
        ch0-pin = <13>;
        ch0-inverted;
        status= "okay";
    };
    &pwm1 {
        ch0-pin = <14>;
        ch0-inverted;
        status= "okay";
    };
    &pwm2 {
        ch0-pin = <15>;
        ch0-inverted;
        status= "okay";
    };
    &pwm3 {
        ch0-pin = <16>;
        ch0-inverted;
        status= "okay";
    };
    

    I have now idea how considering that the “led_set_brightness” function

    To change the brightness I would stick with the led_set_brightness() function. For more info on how to use it see here.

    Let me know if there is anything that I haven't cleared up yet.

    Regards,

    Elfving

  • Hi, thank you very much for your help, your explanations were very clear. Now that I understand how the device tree works, everything is much easier ! It might be useful to include some informations about the device tree in the sample since only one LEDs is configured as pwm. 

    Thank you for your time,

    Kind regards,

    Bastien

  • No problem at all!

    Bastien said:
    It might be useful to include some informations about the device tree in the sample since only one LEDs is configured as pwm. 

    That is a good point, I will pass that on to the relevant team.

    Regards,

    Elfving

Reply Children
No Data
Related