Zephyr GPIO Interrupt

I am porting my code from Nordic SDK16 to Zephyr and NRF Connect SDK 2.0. I am working on putting a chip into sleep mode and set up a wake up on a press of the button. Everything seems to be working ok on the first run but after the chip wakes up, the gpio call back gets called twice and I have no idea why. I tried removing a call back with gpio_remove_callback(button.port, &button_cb_data) but its not helping. Thanks for any help.

#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 button_cb_data;

static int disable_ds_1(const struct device *dev)
{
	ARG_UNUSED(dev);
	pm_policy_state_lock_get(PM_STATE_SOFT_OFF);
	return 0;
}

SYS_INIT(disable_ds_1, PRE_KERNEL_2, 0);

static void power_down() {
    printk("Power down\r\n");
    pm_state_force(0u, &(struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
    k_sleep(K_SECONDS(2U));
}

static void prepare_wake_up() {
    printk("Preparing wake up\r\n");
    nrf_gpio_cfg_input(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios), NRF_GPIO_PIN_PULLUP);
	nrf_gpio_cfg_sense_set(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios), NRF_GPIO_PIN_SENSE_LOW);
}

static void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
    if (gpio_pin_get(button.port, button.pin)) {
        printk("Button pressed...\r\n");
    } else {
        printk("Button released...\r\n");
        prepare_wake_up();
        power_down();
    }
}

int main() {
    if (!device_is_ready(button.port)) {
		printk("Error: button device %s is not ready\n",
		       button.port->name);
		return;
	}

	gpio_pin_configure_dt(&button, GPIO_INPUT));

	gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_BOTH);

	gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
	gpio_add_callback(button.port, &button_cb_data);
}

On the first run it will be:

Button pressed...

Button released...

Prepare wake up...

Power down...

Then after system wakes up with the press of the button, this happens:

Button pressed...

Button pressed...

Button released...

Prepare wake up...

Power down...

Button released...

Prepare wake up...

Power down...

Parents
  • The logic in your code seems to have a bug.

    static void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
    {
        if (gpio_pin_get(button.port, button.pin)) {
            printk("Button pressed...\r\n");
        } else {
            printk("Button released...\r\n");
            prepare_wake_up();
            power_down();
        }
    }

    Inside the button_pressed callback reading the gpio pin status after wakeup might read that the button is released if you pressed and released the button fast. Instead do something like below

    static void button_interrupt(const struct device *dev,
    			     struct gpio_callback *cb,
    			     uint32_t pins)
    {
    static bool pressed = false;
    
    	pressed = !pressed;
    	printk("Button %s\n", pressed ? "pressed" : "released");
    
    	if (pressed) {
            ...
    	}
        else {
            ...
        }
        ...
    }

  • This is a simplified code, where in reality the button needs to be help for half a second for power down to begin. That shouldn't really solve the problem as to why the gpio callback gets fired twice on "SINGLE" press of the button and then release.

Reply Children
Related