This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Thread with callback

Hello,

I have just started in the world of Nordic products and quite recently in the embedded world. I'm trying to learn and understand the use of zephyr in basic projects.

In this one I use two threads, one that makes the led blink and the other one with a callback on button pressed that sends a console message. However, my led doesn't blink and when I press the button nothing happens. I don't really understand why.

/*
 * Copyright (c) 2017 Linaro Limited
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr.h>
#include <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <sys/printk.h>
#include <inttypes.h>
#include <drivers/pwm.h>
#include <logging/log.h>
#include <sys/__assert.h>
#include <string.h>

/* size of stack area used by each thread */
#define STACKSIZE 1024

/* scheduling priority used by each thread */
#define PRIORITY 7

#define SLEEP_MS 500
/*alias*/
#define LED0_NODE DT_ALIAS(led0)
#define SW0_NODE	DT_ALIAS(sw0)

/*FIFO*/
struct printk_data_t {
	void *fifo_reserved; /* 1st word reserved for use by fifo */
	uint32_t conp;
	uint32_t cnt;
};

K_FIFO_DEFINE(printk_fifo);

struct component {
	const char *gpio_dev_name;
	const char *gpio_pin_name;
	unsigned int gpio_pin;
	unsigned int gpio_flags;
};

static struct gpio_callback button_cb;

void print(uint32_t id, int cnt){
    struct printk_data_t tx_data = { .conp = id, .cnt = cnt };
    size_t size = sizeof(struct printk_data_t);
    char *mem_ptr = k_malloc(size);
    __ASSERT_NO_MSG(mem_ptr != 0);

    memcpy(mem_ptr, &tx_data, size);

    k_fifo_put(&printk_fifo, mem_ptr);

    k_msleep(SLEEP_MS);
    
}

void blink(const struct component *led, uint32_t id)
{
	const struct device *gpio_dev;
	int cnt = 0;
	int ret;

	gpio_dev = device_get_binding(led->gpio_dev_name);
	if (gpio_dev == NULL) {
		printk("Error: didn't find %s device\n",
		       led->gpio_dev_name);
		return;
	}

	ret = gpio_pin_configure(gpio_dev, led->gpio_pin, led->gpio_flags);
	if (ret != 0) {
		printk("Error %d: failed to configure pin %d '%s'\n",
			ret, led->gpio_pin, led->gpio_pin_name);
		return;
	}

	while (1) {
		gpio_pin_set(gpio_dev, led->gpio_pin, cnt % 2);
                print(id,cnt);
                cnt++;              
	}
}

void button_pressed(const struct device *gpio_dev, struct gpio_callback *cb,
		    uint32_t pins){
        print(1,1);
}

void ring(const struct component *bt)
{
        const struct device *gpio_dev;
	int cnt = 0;
	int ret;

	gpio_dev = device_get_binding(bt->gpio_dev_name);
	if (gpio_dev == NULL) {
		printk("Error: didn't find %s device\n",
		       bt->gpio_dev_name);
		return;
	}

	ret = gpio_pin_configure(gpio_dev, bt->gpio_pin, bt->gpio_flags);
	if (ret != 0) {
		printk("Error %d: failed to configure pin %d '%s'\n",
			ret, bt->gpio_pin, bt->gpio_pin_name);
		return;
	}

        gpio_init_callback(&button_cb, button_pressed, bt->gpio_pin);
	gpio_add_callback(gpio_dev, &button_cb);
          
        bool val;
        while(1){
                
        }
}


void led_blink(void)
{
	const struct component led0 = {
#if DT_NODE_HAS_STATUS(LED0_NODE, okay)
		.gpio_dev_name = DT_GPIO_LABEL(LED0_NODE, gpios),
		.gpio_pin_name = DT_LABEL(LED0_NODE),
		.gpio_pin = DT_GPIO_PIN(LED0_NODE, gpios),
		.gpio_flags = GPIO_OUTPUT_ACTIVE | DT_GPIO_FLAGS(LED0_NODE, gpios),
#else
#error "Unsupported board: led0 devicetree alias is not defined"
#endif
	};

	blink(&led0, 0);
}

void button(void)
{
	const struct component button0 = {
#if DT_NODE_HAS_STATUS(SW0_NODE, okay)
		.gpio_dev_name = DT_GPIO_LABEL(SW0_NODE, gpios),
		.gpio_pin_name = DT_LABEL(SW0_NODE),
		.gpio_pin = DT_GPIO_PIN(SW0_NODE, gpios),
		.gpio_flags = GPIO_INPUT | DT_GPIO_FLAGS(SW0_NODE, gpios),
#else
#error "Unsupported board: sw0 devicetree alias is not defined"
#endif
	};
	ring(&button0);
}


void uart_out(void)
{
	while (1) {
		struct printk_data_t *rx_data = k_fifo_get(&printk_fifo,
							   K_FOREVER);
		printk("[Info] conponent %d , value %d \n",
		       rx_data->conp, rx_data->cnt);
		k_free(rx_data);
	}
}

K_THREAD_DEFINE(led_id, STACKSIZE, led_blink, NULL, NULL, NULL,
		PRIORITY, 0, 0);
K_THREAD_DEFINE(button_id, STACKSIZE, button, NULL, NULL, NULL,
		PRIORITY, 0, 0);
K_THREAD_DEFINE(uart_out_id, STACKSIZE, uart_out, NULL, NULL, NULL,
		PRIORITY, 0, 0);

If you know an idea or if you can explain to me what I'm doing wrong I would be delighted.

Thanks

Parents
  • Hi!

    Thanks for reaching out. I suggest that you look into how buttons are handled in the DK Buttons and LEDs library. Note especially that:
    1. Buttons don't need a thread per say, as they work through interrupts and callbacks.
    2. When a button is pressed the interrupt should be disabled while the callback is handled. Otherwise you will get an assert like this: ASSERTION FAIL [!arch_is_in_isr()] @ WEST_TOPDIR/zephyr/kernel/sched.c:1306

    As for the LED thread not working the eternal while loop at the end of your button thread holds the CPU hostage from running the other threads. The button and callback is initialized anyway, so you don't need to have the "while" there.

    Hope this helps you on your way!

    Best regards,
    Carl Richard

Reply
  • Hi!

    Thanks for reaching out. I suggest that you look into how buttons are handled in the DK Buttons and LEDs library. Note especially that:
    1. Buttons don't need a thread per say, as they work through interrupts and callbacks.
    2. When a button is pressed the interrupt should be disabled while the callback is handled. Otherwise you will get an assert like this: ASSERTION FAIL [!arch_is_in_isr()] @ WEST_TOPDIR/zephyr/kernel/sched.c:1306

    As for the LED thread not working the eternal while loop at the end of your button thread holds the CPU hostage from running the other threads. The button and callback is initialized anyway, so you don't need to have the "while" there.

    Hope this helps you on your way!

    Best regards,
    Carl Richard

Children
  • Thank you for your response. I followed your first advice to put the button in the same thread as the led. But for your 2. I don't see how to do it, would you have some explanation and an example please ? 

  • Great. I will try to come up with an example for you tomorrow. In the meantime I believe you will find the relevant information in the source code of the library I spoke of earlier. Here the buttons are tied to a callback called button_pressed, when that callback is triggered it does two things:
    1. Calls k_spin_lock() to prevent further interrupts
    2. Disables further callbacks calling the callback_ctrl function

    After this it handles the actual button press before unlocking the spinlock and enables the callbacks again. If you do this for your single button it should work properly. I suggest that you define the button device globally so it can be accessed from the callback and callback_ctrl.

    Best regards,
    Carl Richard

  • Hello Mr Richard,

    I've tried it by myself but I must admit that I have trouble understanding the structure to pass for the button to work. Do you have any advice to help me please? 

    /*
     * Copyright (c) 2017 Linaro Limited
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr.h>
    #include <device.h>
    #include <devicetree.h>
    #include <drivers/gpio.h>
    #include <sys/util.h>
    #include <sys/printk.h>
    #include <inttypes.h>
    #include <drivers/pwm.h>
    #include <sys/__assert.h>
    #include <string.h>
    #include <dk_buttons_and_leds.h>
    
    #include "buzzer.h"
    /* size of stack area used by each thread */
    #define STACKSIZE 1024
    
    /* scheduling priority used by each thread */
    #define PRIORITY 7
    
    #define SLEEP_MS 500
    /*alias*/
    #define LED0_NODE DT_ALIAS(led2)
    #define SW0_NODE DT_ALIAS(sw0)
    
    /*FIFO*/
    struct printk_data_t {
    	void *fifo_reserved; /* 1st word reserved for use by fifo */
    	uint32_t conp;
    };
    
    K_FIFO_DEFINE(printk_fifo);
    
    static struct gpio_callback button_cb;
    const struct device *gpio_led;
    const struct device *gpio_bt;
    
    void print(uint32_t id){
        struct printk_data_t tx_data = { .conp = id };
        size_t size = sizeof(struct printk_data_t);
        char *mem_ptr = k_malloc(size);
        __ASSERT_NO_MSG(mem_ptr != 0);
    
        memcpy(mem_ptr, &tx_data, size);
    
        k_fifo_put(&printk_fifo, mem_ptr);
    
        k_msleep(SLEEP_MS);
        
    }
    
    
    void button_pressed(const struct device *gpio_dev, struct gpio_callback *cb,
    		    uint32_t pins){
    
            print(1);
            ui_buzzer_set_frequency(100, 20);
            k_msleep(SLEEP_MS);
            ui_buzzer_set_frequency(0, 0);
            
            
    }
    
    void blink()
    {
    	int cnt = 0;
    	int ret;
            
            /*led*/
    	int err;
    
    	err = dk_leds_init();
    	if (err) {
    		printk("Could not initialize leds, err code: %d\n", err);
    	}
    
    	err = dk_set_leds_state(1, DK_ALL_LEDS_MSK);
    	if (err) {
    		printk("Could not set leds state, err code: %d\n", err);
    	}
    
            /*button*/
            struct button_handler *b_cb;
            dk_button_handler_add(&b_cb);
    	err = dk_buttons_init(b_cb->cb);
    	if (err) {
    		printk("Could not initialize leds, err code: %d\n", err);
    	}
            
            /*buzzer*/
            err = ui_buzzer_init();
    	if (err) {
    		printk("Could not enable buzzer, err code: %d\n", err);
    		return err;
    	}
    
    	while (1) {
    		err = dk_set_leds_state(cnt % 2, DK_ALL_LEDS_MSK);
                    if (err) {
                            printk("Could not set leds state, err code: %d\n", err);
                    }
                    if (cnt % 2) {
                             print(0);
                    }
                    k_msleep(SLEEP_MS);
                    cnt++;              
    	}
    }
    
    
    void uart_out(void)
    {
    	while (1) {
    		struct printk_data_t *rx_data = k_fifo_get(&printk_fifo,
    							   K_FOREVER);
    		printk("[Info] conponent %d turn on \n",
    		       rx_data->conp);
    		k_free(rx_data);
    	}
    }
    
    
    
    K_THREAD_DEFINE(blink_id, STACKSIZE, blink, NULL, NULL, NULL,
    		PRIORITY, 0, 0);
    K_THREAD_DEFINE(uart_out_id, STACKSIZE, uart_out, NULL, NULL, NULL,
    		PRIORITY, 0, 0);
    

  • Hi again. 

    When using the DK buttons and LEDs library you must use the correct callback definition. The handles all the GPIO interaction so you don't need to address that directly.  See the attached code, which is a modified version of what you posted earlier.

    /*
     * Copyright (c) 2017 Linaro Limited
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr.h>
    #include <device.h>
    #include <devicetree.h>
    #include <drivers/gpio.h>
    #include <sys/util.h>
    #include <sys/printk.h>
    #include <inttypes.h>
    #include <drivers/pwm.h>
    #include <sys/__assert.h>
    #include <string.h>
    #include <dk_buttons_and_leds.h>
    
    #include "buzzer.h"
    /* size of stack area used by each thread */
    #define STACKSIZE 1024
    
    /* scheduling priority used by each thread */
    #define PRIORITY 7
    
    #define SLEEP_MS 500
    /*alias*/
    #define LED0_NODE DT_ALIAS(led2)
    #define SW0_NODE DT_ALIAS(sw0)
    
    /*FIFO*/
    struct printk_data_t {
    	void *fifo_reserved; /* 1st word reserved for use by fifo */
    	uint32_t conp;
    };
    
    K_FIFO_DEFINE(printk_fifo);
    
    void print(uint32_t id){
        struct printk_data_t tx_data = { .conp = id };
        size_t size = sizeof(struct printk_data_t);
        char *mem_ptr = k_malloc(size);
        __ASSERT_NO_MSG(mem_ptr != 0);
    
        memcpy(mem_ptr, &tx_data, size);
    
        k_fifo_put(&printk_fifo, mem_ptr);
    
        k_msleep(SLEEP_MS);
        
    }
    
    
    /**@brief Callback for button events from the DK buttons and LEDs library. */
    static void button_handler(uint32_t button_states, uint32_t has_changed) {
            print(1);
            ui_buzzer_set_frequency(100, 20);
            k_msleep(SLEEP_MS);
            ui_buzzer_set_frequency(0, 0);
            
    	if (has_changed & button_states & DK_BTN1_MSK) {
    		printk("DEV_DBG: button 1 pressed\n");
    	}
    
    	else if (has_changed & button_states & DK_BTN2_MSK) {
    		printk("DEV_DBG: button 2 pressed\n");
    	}        
    }
    
    void blink()
    {
    	int cnt = 0;
    	int ret;
            
        /*led*/
    	int err;
    
    	err = dk_leds_init();
    	if (err) {
    		printk("Could not initialize leds, err code: %d\n", err);
    	}
    
    	err = dk_set_leds_state(1, DK_ALL_LEDS_MSK);
    	if (err) {
    		printk("Could not set leds state, err code: %d\n", err);
    	}
    
    	err = dk_buttons_init(button_handler);
    	if (err) {
    		printk("Could not initialize leds, err code: %d\n", err);
    	}
    
        /*buzzer*/
        err = ui_buzzer_init();
    	if (err) {
    		printk("Could not enable buzzer, err code: %d\n", err);
    		return err;
    	}
    
    	while (1) {
    		err = dk_set_leds_state(cnt % 2, DK_ALL_LEDS_MSK);
                    if (err) {
                            printk("Could not set leds state, err code: %d\n", err);
                    }
                    if (cnt % 2) {
                             print(0);
                    }
                    k_msleep(SLEEP_MS);
                    cnt++;              
    	}
    }
    
    
    void uart_out(void)
    {
    	while (1) {
    		struct printk_data_t *rx_data = k_fifo_get(&printk_fifo,
    							   K_FOREVER);
    		printk("[Info] conponent %d turn on \n",
    		       rx_data->conp);
    		k_free(rx_data);
    	}
    }
    
    
    
    K_THREAD_DEFINE(blink_id, STACKSIZE, blink, NULL, NULL, NULL,
    		PRIORITY, 0, 0);
    K_THREAD_DEFINE(uart_out_id, STACKSIZE, uart_out, NULL, NULL, NULL,
    		PRIORITY, 0, 0);


    Notice two important changes in the code. 

    1. The button handler callback is changed to the following:
    /**@brief Callback for button events from the DK buttons and LEDs library. */
    static void button_handler(uint32_t button_states, uint32_t has_changed) {
            print(1);
            ui_buzzer_set_frequency(100, 20);
            k_msleep(SLEEP_MS);
            ui_buzzer_set_frequency(0, 0);
            
    	if (has_changed & button_states & DK_BTN1_MSK) {
    		printk("DEV_DBG: button 1 pressed\n");
    	}
    
    	else if (has_changed & button_states & DK_BTN2_MSK) {
    		printk("DEV_DBG: button 2 pressed\n");
    	}        
    }


    2. The buttons are initialized directly with the callback function as a parameter:
    err = dk_buttons_init(button_handler);
    if (err) {
        printk("Could not initialize leds, err code: %d\n", err);
    }

    I hope this is more clear to you now.

    Best regards,
    Carl Richard

  • Oh ok thanks it's clearer! despite everything nothing happens when I press the button :x

Related