NRF52840 dongle 10 button USB HID with zephyr

Goodday

 I thought this could be good learning curve to get started with Zephyr. Im having some problems with the GPIO.  If I rewrite this code for  3 buttons with  PORT1 (pin 1.10 pin 1.13 and pin 1.15) the code works. But it does not work on PORT 0 with 10 buttons. I presume it may be something with the DTS file that may be configuring the ports for other purposes (SPI/UART/I2C) etc. 

1. How do I use PORT 0 in zephyr instead of port 1.

2. is there anything I need to reconfigure in the DTS file to make it work.

Any help appreciated as always

/*
 * Copyright (c) 2016 Open-RnD Sp. z o.o.
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */



#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/printk.h>
#include <inttypes.h>



#define SLEEP_TIME_MS	1

/*
 * Get button configuration from the devicetree sw0 alias. This is mandatory.
 */
#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 button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios,
							      {0});
static struct gpio_callback button0_cb_data;
static struct gpio_callback button1_cb_data;
static struct gpio_callback button2_cb_data;
static struct gpio_callback button3_cb_data;
static struct gpio_callback button4_cb_data;
static struct gpio_callback button5_cb_data;
static struct gpio_callback button6_cb_data;
static struct gpio_callback button7_cb_data;
static struct gpio_callback button8_cb_data;
static struct gpio_callback button9_cb_data;
static struct gpio_callback button10_cb_data;


#define BUT_PIN1 13 //port 0
#define BUT_PIN2 15 //port 0
#define BUT_PIN3 17 //port 0
#define BUT_PIN4 20 //port 0
#define BUT_PIN5 24 //port 0
#define BUT_PIN6 02 //port 0
#define BUT_PIN7 9 //port 0
#define BUT_PIN8 10 //port 0
#define BUT_PIN9 29  //port 0
#define BUT_PIN10 31  //port 0

/*
 * The led0 devicetree alias is optional. If present, we'll use it
 * to turn on the LED whenever the button is pressed.
 */
static struct gpio_dt_spec led = GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios,
						     {0});

void button_pressed(const struct device *dev, struct gpio_callback *cb,
		    uint32_t pins)
{
	uint32_t bit_to_pin = 0;
	for (int i = 0; i < 32; i++) {
		if ((pins >> i) == 1) {
			bit_to_pin = i;
			break;
		}
	}

	printk("Button %d pressed at %" PRIu32 "\n", bit_to_pin, k_cycle_get_32());
	static bool val;
	val = !val;
	gpio_pin_set_dt(&led, val);	
}

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

	ret = gpio_pin_configure(button.port, BUT_PIN1, GPIO_INPUT | GPIO_PULL_UP);
	printk("ret %d\n", ret);
	ret = gpio_pin_configure(button.port, BUT_PIN2, GPIO_INPUT | GPIO_PULL_UP);
	printk("ret %d\n", ret);
	ret = gpio_pin_configure(button.port, BUT_PIN3, GPIO_INPUT | GPIO_PULL_UP);
	printk("ret %d\n", ret);
	ret = gpio_pin_configure(button.port, BUT_PIN4, GPIO_INPUT | GPIO_PULL_UP);
	printk("ret %d\n", ret);
	ret = gpio_pin_configure(button.port, BUT_PIN5, GPIO_INPUT | GPIO_PULL_UP);
	printk("ret %d\n", ret);
	ret = gpio_pin_configure(button.port, BUT_PIN6, GPIO_INPUT | GPIO_PULL_UP);
	printk("ret %d\n", ret);
	ret = gpio_pin_configure(button.port, BUT_PIN7, GPIO_INPUT | GPIO_PULL_UP);
	printk("ret %d\n", ret);
	ret = gpio_pin_configure(button.port, BUT_PIN8, GPIO_INPUT | GPIO_PULL_UP);
	printk("ret %d\n", ret);
	ret = gpio_pin_configure(button.port, BUT_PIN9, GPIO_INPUT | GPIO_PULL_UP);
	printk("ret %d\n", ret);
	ret = gpio_pin_configure(button.port, BUT_PIN10, GPIO_INPUT | GPIO_PULL_UP);
	printk("ret %d\n", ret);

	ret = gpio_pin_interrupt_configure(button.port, BUT_PIN1, GPIO_INT_EDGE_BOTH);
	printk("ret %d\n", ret);
	ret = gpio_pin_interrupt_configure(button.port, BUT_PIN2, GPIO_INT_EDGE_BOTH);
	printk("ret %d\n", ret);
	ret = gpio_pin_interrupt_configure(button.port, BUT_PIN3, GPIO_INT_EDGE_BOTH);
	printk("ret %d\n", ret);
	ret = gpio_pin_interrupt_configure(button.port, BUT_PIN4, GPIO_INT_EDGE_BOTH);
	printk("ret %d\n", ret);
	ret = gpio_pin_interrupt_configure(button.port, BUT_PIN5, GPIO_INT_EDGE_BOTH);
	printk("ret %d\n", ret);
	ret = gpio_pin_interrupt_configure(button.port, BUT_PIN6, GPIO_INT_EDGE_BOTH);
	printk("ret %d\n", ret);
	ret = gpio_pin_interrupt_configure(button.port, BUT_PIN7, GPIO_INT_EDGE_BOTH);
	printk("ret %d\n", ret);
	ret = gpio_pin_interrupt_configure(button.port, BUT_PIN8, GPIO_INT_EDGE_BOTH);
	printk("ret %d\n", ret);
	ret = gpio_pin_interrupt_configure(button.port, BUT_PIN9, GPIO_INT_EDGE_BOTH);
	printk("ret %d\n", ret);
	ret = gpio_pin_interrupt_configure(button.port, BUT_PIN10, GPIO_INT_EDGE_BOTH);
	printk("ret %d\n", ret);

	gpio_init_callback(&button1_cb_data, button_pressed, BIT(BUT_PIN1));
	ret = gpio_add_callback(button.port, &button1_cb_data);
	printk("ret %d\n", ret);
	gpio_init_callback(&button2_cb_data, button_pressed, BIT(BUT_PIN2));	
	ret = gpio_add_callback(button.port, &button2_cb_data);
	printk("ret %d\n", ret);
	gpio_init_callback(&button3_cb_data, button_pressed, BIT(BUT_PIN3));	
	ret = gpio_add_callback(button.port, &button3_cb_data);		
	printk("ret %d\n", ret);
	gpio_init_callback(&button4_cb_data, button_pressed, BIT(BUT_PIN4));
	ret = gpio_add_callback(button.port, &button4_cb_data);
	printk("ret %d\n", ret);
	gpio_init_callback(&button5_cb_data, button_pressed, BIT(BUT_PIN5));	
	ret = gpio_add_callback(button.port, &button5_cb_data);
	printk("ret %d\n", ret);
	gpio_init_callback(&button6_cb_data, button_pressed, BIT(BUT_PIN6));	
	ret = gpio_add_callback(button.port, &button6_cb_data);		
	printk("ret %d\n", ret);
	gpio_init_callback(&button7_cb_data, button_pressed, BIT(BUT_PIN7));
	ret = gpio_add_callback(button.port, &button7_cb_data);
	printk("ret %d\n", ret);
	gpio_init_callback(&button8_cb_data, button_pressed, BIT(BUT_PIN8));	
	ret = gpio_add_callback(button.port, &button8_cb_data);
	printk("ret %d\n", ret);
	gpio_init_callback(&button9_cb_data, button_pressed, BIT(BUT_PIN9));	
	ret = gpio_add_callback(button.port, &button9_cb_data);		
	printk("ret %d\n", ret);
	gpio_init_callback(&button10_cb_data, button_pressed, BIT(BUT_PIN10));
	ret = gpio_add_callback(button.port, &button10_cb_data);
	printk("ret %d\n", ret);

	

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

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

	printk("Press the button\n");
}

  • Hi,

    Have you connected a debugger and checked if the program asserts or enters the callback handler?

    If I rewrite this code for  3 buttons with  PORT1 (pin 1.10 pin 1.13 and pin 1.14) the code works. But it does not work on PORT 0 with 10 buttons.

    But does it work with 3 buttons on PORT 0 or even 1?

    Also some of the GPIOs that you're trying to use are already in use in the Dongle dts,

    regards

    Jared 

  • HI Jared

    If I only declare 3 pins namely 10,13,15 the switches do work, but only port 1. One of my questions was how do I select PORT 0 so I can test the pins currently defined in the code. Ideally, I would like to make the entire PORT 0, GPIO inputs for buttons.

    You're right some of the GPIOs are declared in the DTS that's why I asked if I need to reconfigure it somehow.

    Thanks for the help

  • Hi,

    Ideally, using PORT 0 shouldn't be much difference than using PORT 1. Contrary to nRF5SDK, In zephyr we have something that we call the device tree that is used to describe the HW that the application will run on. Additional GPIOs that you're going to use should be added in an overlay which will map the pins in the device tree.  For example, here is an example on how to set interrupt on button1 (port 0, pin11)  on the nRF52840 DK so that LED1 will flash every time it's pressed:

    /*
     * Copyright (c) 2016 Intel Corporation
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/drivers/gpio.h>
    
    /* 1000 msec = 1 sec */
    #define SLEEP_TIME_MS   1000
    
    /* The devicetree node identifier for the "led0" alias. */
    #define LED0_NODE DT_ALIAS(led0)
    #define BUTTON0_NODE DT_ALIAS(sw0)
    
    /*
     * A build error on this line means your board is unsupported.
     * See the sample documentation for information on how to fix this.
     */
    static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
    static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(BUTTON0_NODE, gpios);
    
    
    void button_cb_handler(const struct device *dev, struct gpio_callback *cb, gpio_port_pins_t pins)
    {
    		int ret = gpio_pin_toggle_dt(&led);
    		if (ret < 0) {
    		}
    }
    
    
    int main(void)
    {
    	int ret;
    
    	if (!gpio_is_ready_dt(&led)) {
    		return 0;
    	}
    
    	if (!gpio_is_ready_dt(&button))
    	{
    		return 0;
    	}
    
    
    
    
    	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return 0;
    	}
    	ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
    	if (ret < 0) {
    		return 0;
    	}
    
    
    	gpio_pin_interrupt_configure_dt(&button,GPIO_INT_EDGE_TO_ACTIVE);
    
    
    	static struct gpio_callback pin_cb_data;
    	gpio_init_callback(&pin_cb_data, button_cb_handler, BIT(button.pin));
    	gpio_add_callback(button.port,&pin_cb_data);
    
    	while (1) {
    		k_msleep(SLEEP_TIME_MS);
    	}
    	return 0;
    }
    

    You can try to build this project with your dongle, and first add 1 or 2 gpios with an overlay, and repeat the steps that I did for the button for the additional gpios that you've added. Then eventually you can try to expand it to 10 buttons,

    I also strongly suggest to go through our DevAcademy, the first lessons are very relevant for your question Slight smile,

    regards

    Jared

  • Thanks Jared. 

    I have done an overlay file as per below. I see in the overlay file some of my pins say "&GPIO already assigned to 2 other nodes"  I dont think im removing SPI,I2C and UART nodes correctl

    // To get started, press Ctrl+Space to bring up the completion menu and view the available nodes.
    
    // You can also use the buttons in the sidebar to perform actions on nodes.
    // Actions currently available include:
    
    // * Enabling / disabling the node
    // * Adding the bus to a bus
    // * Removing the node
    // * Connecting ADC channels
    
    // For more help, browse the DeviceTree documentation at https://docs.zephyrproject.org/latest/guides/dts/index.html
    // You can also visit the nRF DeviceTree extension documentation at https://nrfconnect.github.io/vscode-nrf-connect/devicetree/nrfdevicetree.html
    
    /{leds {
    
    		compatible = "gpio-leds";
    		led0_green: led_0 {
    			gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
    			label = "Green LED 0";
    		};
    		led1_red: led_1 {
    			gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
    			label = "Red LED 1";
    		};
    		led1_green: led_2 {
    			gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
    			label = "Green LED 1";
    		};
    		led1_blue: led_3 {
    			gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
    			label = "Blue LED 1";
    		};
    	};
    };
    
    /{
        buttons {
        compatible = "gpio-keys";
        button0: button_0 {
    		gpios = <&gpio1 6 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
    		label = "Push button switch 0";
    		};
        compatible = "gpio-keys";
        button1: button_1 {
            gpios = <&gpio0 13 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
            label = "Push button switch 1";
        };
        compatible = "gpio-keys";
        button2: button_2 {
            gpios = <&gpio0 15 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
            label = "Push button switch 2";
        };
        compatible = "gpio-keys";
        button3: button_3 {
            gpios = <&gpio0 17 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
            label = "Push button switch 3";
        };
        compatible = "gpio-keys";
        button4: button_4 {
            gpios = <&gpio0 20 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
            label = "Push button switch 4";
        };
    };
    };
    
    
    /* These aliases are provided for compatibility with samples */
    /{		aliases {
            sw0 = &button0;
            sw1 = &button1;
            sw2 = &button2;
            sw3 = &button3;
            sw4 = &button4;
    	
            led0 = &led0_green;
    		led1 = &led1_red;
    		led2 = &led1_green;
    		led3 = &led1_blue;
    		led0-green = &led0_green;
    		led1-red   = &led1_red;
    		led1-green = &led1_green;
    		led1-blue  = &led1_blue;
    
    	};
    };
    
    
    &uart0 {
       
        /delete-property/ some-unwanted-property;
    };
    
    &i2c0 {
        /delete-property/ some-unwanted-property;
    
    };
    
    &i2c1 {
        /delete-property/ some-unwanted-property;
    
    };
    
    &pwm0 {
        /delete-property/ some-unwanted-property;
    
    };
    
    
    &spi0 {
        /delete-property/ some-unwanted-property;
    
    };
    
    &spi1 {
        /delete-property/ some-unwanted-property;
    
    };
    
    &uart0 {
       /delete-property/ some-unwanted-property;
    };
    
    

  • Hi,

    That looks correct, did you do a pristine build?

    If you're using nRF Connect SDK VS Code extension, then there is a DTS GUI that will make it easier for you to map out the pins and replace and delete properties. Maybe you could try and see if that helps?

    regards

    Jared

Related