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.

  • Single press most likely is not firing the interrupt twice, it is the way you are printing the logs.

    Can you add another log just after entereing button_pressed() but before the if condition and show me the logs again.

  • OK here is log, first I press and release button, it goes to sleep. Then i press to wake up, it wakes up. Then I press and release again and you can see call back is fired twice. I added a time stamp inside callback before if statement printk("Button pressed at %" PRIu32 "\n", k_cycle_get_32());

    *** Booting Zephyr OS build v3.0.99-ncs1 ***
    Set up button at GPIO_0 pin 11
    Button pressed at 176252
    Button pressed...
    Button pressed at 210136
    Button released...
    Preparing wake up
    �*** Booting Zephyr OS build v3.0.99-ncs1 ***
    Set up button at GPIO_0 pin 11
    Button pressed at 246061
    Button pressed...
    Button pressed at 246189
    Button pressed...
    Button pressed at 283049
    Button released...
    Preparing wake up
    Power down
    Button pressed at 283276
    Button released...
    Preparing wake up
    �ower down

Reply
  • OK here is log, first I press and release button, it goes to sleep. Then i press to wake up, it wakes up. Then I press and release again and you can see call back is fired twice. I added a time stamp inside callback before if statement printk("Button pressed at %" PRIu32 "\n", k_cycle_get_32());

    *** Booting Zephyr OS build v3.0.99-ncs1 ***
    Set up button at GPIO_0 pin 11
    Button pressed at 176252
    Button pressed...
    Button pressed at 210136
    Button released...
    Preparing wake up
    �*** Booting Zephyr OS build v3.0.99-ncs1 ***
    Set up button at GPIO_0 pin 11
    Button pressed at 246061
    Button pressed...
    Button pressed at 246189
    Button pressed...
    Button pressed at 283049
    Button released...
    Preparing wake up
    Power down
    Button pressed at 283276
    Button released...
    Preparing wake up
    �ower down

Children
Related