Help figuring out why my GPIO interrupt handler not being called - Zephyr 3.2

I'm pulling my hair out here; I have a custom board with an nrf52840 on it, and my code is using Zephyr 3.2 (I'm not specifically using any of the nrf SDKs, just stock Zephyr).

I have verified that the hardware works - I have built the Zephyr "button" sample (`zephyr/samples/basic/button`) and verified that the GPIO in question is the correct one, and that it works in the sample.  (GPIO 0.17, in case it matters)

However, in my larger application (you know, where I actually want to USE it), I'm trying to build a driver which needs to detect edge-triggered interrupts on this GPIO, and for the life of me, I can't get the interrupt handler to trigger.  As far as I can tell, I'm setting everything up the same way:

- call `gpio_pin_interrupt_configure_dt()` on the pin in question'

- call `gpio_init_callback()` to initialize the callback function

- call `gpio_add_callback()` to call the callback function to the pin

... but the callback is never called, even though the GPIO state is changing.

I have a bunch of `printk` debugging and I'm also using Segger Ozone - what else should I be looking for here?  I'd appreciate suggestions of specific things to look at (maybe interrupts are not being enabled?  where do I check that?) in the debugger or source code.

This is the init function (where I'm trying to set up interrupts) and some associated support code - any pointers of what to look at would be greatly appreciated:

struct hakkei_power_config {
const struct gpio_dt_spec signal;
};

struct hakkei_power_data {
struct gpio_callback pwr_cb;
const struct device *dev;
struct k_work work;
const char *str;
};
void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins) {
printk("Button pressed at %" PRIu32 "\n", k_cycle_get_32());
}

static int hakkei_power_init(const struct device *dev) {
struct hakkei_power_data *data = dev->data;
const struct hakkei_power_config *config = dev->config;

data->dev = dev;

if (!device_is_ready(config->signal.port)) {
LOG_ERR("switch gpio not ready!");
}

if (gpio_pin_configure_dt(&config->signal, GPIO_INPUT)) {
LOG_ERR("Failed to configure signal pin");
return -EIO;
}
int ret = gpio_pin_interrupt_configure_dt(&config->signal, GPIO_INT_EDGE_BOTH);
if (ret != 0) {
printk("Error %d: failed to configure interrupt on %s pin %d\n", ret,
config->signal.port->name, config->signal.pin);
return;
}
gpio_init_callback(&data->pwr_cb, button_pressed, BIT(config->signal.pin));
gpio_add_callback(config->signal.port, &data->pwr_cb);
printk("callback set? str is %s\n", data->str);
printk("Set up button at %s pin %d\n", config->signal.port->name, config->signal.pin);
switch (gpio_pin_get_dt(&config->signal)) {
case 0:
LOG_WRN("power is off");
printk("off\n");
break;
case 1:
LOG_WRN("power is on");
printk("on\n");
break;
default:
LOG_ERR("power is unknown");
break;
};

printk("here2\n");
// k_work_init(&data->work, hakkei_power_work_cb);
// k_work_submit(&data->work);
printk("here3\n");
LOG_ERR("This is where the power switch initialization will go");

LOG_WRN("LOOP");
while (1) {
k_msleep(100);
switch (gpio_pin_get_dt(&config->signal)) {
case 0:
printk("OFF ");
break;
case 1:
printk("ON ");
break;
default:
LOG_ERR("power is unknown");
break;
};
}

return 0;
}

static const struct hakkei_power_config config = {.signal = GPIO_DT_SPEC_INST_GET(0, signal_gpios)};
static const struct hakkei_power_data data = {.str = "BLARGH"};
DEVICE_DT_INST_DEFINE(0, hakkei_power_init, NULL, &data, &config, APPLICATION, 1, NULL);
Parents
  • Hello,

    Please use the peripheral viewer in Ozone to check if the GPIO (P0) and GPIOTE configurations are the same when you run the basic button sample and when you run your application. I'm not able to spot any errors in the code you posted, so I'm wondering if the input pin configuration may be overwritten by something else in your app.

    Relevant registers fields:

    P0->PIN_CNF[17] 

    GPIOTE->INTENSET

    GPIOTE->CONFIG[n] (check all channels)

    Thanks,

    Vidar

  • Sorry for the response delay - took me a bit to figure out I hadn't loaded the nrf52 SVD in Ozone :)

    Anyway, no luck - pin configurations for the registers in question all seem to be the same in both apps:

    P0->PIN_CNF[17] is 0000 0000

    GPIOTE->INTENSET is 8000 0080

    GPIOTE->CONFIG[7] is 0003 1101

    (all other GPIOTE->CONFIG[0..6] are 0000 0000)

    I'm really scratching my head at this point

  • Good, you found the SVD file. However, it's strange that the same register configuration is not working in your project. Could you please check if the EVENTS_IN[6] register is being set when you toggle the input? You may also want to try placing a breakpoint in the nrfx_gpiote.c::nrfx_gpiote_irq_handler() function to see if it is invoked when the interrupt is triggered.

    Besides not receiving the button callback, is your application performing other tasks as expected?

  • OK, adding a breakpoint in nrfx_gpiote.c::nrfx_gpiote_irq_handler() is going to help - that's definitely triggering, and I step through the function.  EVENTS_IN[7] shows "Generated" at that point.

    I think at this point I just need to step through nrfx_gpiote.c::nrfx_gpiote_irq_handler() to figure out why the callback isn't being called - hopefully this gives me enough of a clue.  I probably won't have time to get to it for a few days, unfortuately, but I'll reply here when I figure it out.

  • I found the time to step through somewhat; I haven't figured it out yet, but it looks like the handler isn't actually getting set.  I have no idea why just yet - Ozone is telling me some stuff is "out of scope" when I step through on the debugger for reasons I don't entirely understand, but clearly, when I step through here:

    gpio_init_callback(&data->pwr_cb, button_pressed, BIT(config->signal.pin));
    gpio_add_callback(config->signal.port, &data->pwr_cb);
    ... I don't see the "handler" field of &data->pwr_cb (which is `struct gpio_callback`) getting set (it remains 0)
    So... I clearly am missing something simple.  I bet it's staring me right in the face.  Slight smile
    The fact that those are inline functions is making the debugging a bit hard.
Reply
  • I found the time to step through somewhat; I haven't figured it out yet, but it looks like the handler isn't actually getting set.  I have no idea why just yet - Ozone is telling me some stuff is "out of scope" when I step through on the debugger for reasons I don't entirely understand, but clearly, when I step through here:

    gpio_init_callback(&data->pwr_cb, button_pressed, BIT(config->signal.pin));
    gpio_add_callback(config->signal.port, &data->pwr_cb);
    ... I don't see the "handler" field of &data->pwr_cb (which is `struct gpio_callback`) getting set (it remains 0)
    So... I clearly am missing something simple.  I bet it's staring me right in the face.  Slight smile
    The fact that those are inline functions is making the debugging a bit hard.
Children
  • As I expected, the problem was in fact simple.

    Setting up the device:

    static const struct hakkei_power_config config = {.signal = GPIO_DT_SPEC_INST_GET(0, signal_gpios)};
    static const struct hakkei_power_data data = {.str = "BLARGH"};
    DEVICE_DT_INST_DEFINE(0hakkei_power_initNULL&data&config, APPLICATION, 1NULL);
    ... the callback is in the "data" struct - which is marked `const`.  This didn't trigger a compiler error, but apparently did get it stored in flash instead of RAM.  Face palm
    Changing to this:
    static struct hakkei_power_data data = {.str = "BLARGH"};
    made everything work!  Thanks for your help.
Related