Proper configuration of GPIO IRQ on nRF52

Hi,

I'm trying  to setup a "proper" configuration of IRQ from external GPIOs on nRF52832, using the Zephyr GPIO API. Ambition is to keep the SoC in "System ON" mode consuming a few uAs, but I'm not able to find any good examples of this configurations. Below is a modified version of the "Blinky" sample showing my current understanding how to use the API for this task, this works but results in high current consumption of the SoC.

After a lot of trial and error I've concluded that the missing piece seems to be a "sense-edge-mask" property on the gpio0 node in the DT, running the sample code below on the nRF52 DK just shows a difference of ~10uA when adding this property but on the real target hardware with a more complex setup in the DT the difference is huge (hundreds of uA). The IRQs I'm trying to configure should be edge sensitive, it seems that using level sensitive IRQs does not require this "sense-edge-mask" property?

I find documentation lacking in this area, how is "sense-edge-mask" related with gpio and gpiote and what is the expected setup in this case? Some DTs seems to enable the gpiote peripheral as well, but it is not clear when and why this should be done?

Maybe related question, on this nRF52832 SoC it doesn't seem possible to enable PM via "CONFIG_PM=y" since the SoC does not set the "HAS_PM" flag. Is PM and/or PM_DEVICE supported on the nRF SoCs, can drivers add hooks here for additional power savings?

#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)

/*
 * 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 struct gpio_callback irq_cb;

static void irqHandler(const struct device *port, struct gpio_callback *cb, gpio_port_pins_t pins)
{
  // Do something
}

int main(void)
{
	int ret;

	if (!gpio_is_ready_dt(&led)) {
		return 0;
	}

	ret = gpio_pin_configure_dt(&led, (GPIO_INPUT | GPIO_PULL_UP));
	if (ret < 0) {
		return 0;
	}

	gpio_init_callback(&irq_cb, irqHandler, BIT(led.pin));

	ret = gpio_add_callback_dt(&led, &irq_cb);
	if (ret < 0) {
		return 0;
	}

	ret = gpio_pin_interrupt_configure_dt(&led, GPIO_INT_EDGE_FALLING);
	if (ret < 0) {
		return 0;
	}

	while (1) {
		k_msleep(SLEEP_TIME_MS);
	}
	return 0;
}
 

Parents
  • Hi Daniel,

    this works but results in high current consumption of the SoC.

    How do you measure the current?

    on the real target hardware

    Has anyone at Nordic reviewed your custom hardware? We can review the Nordic related part of the design as a free service.

  • Current is measured using an Agilent 34401A DMM on both the custom hardware and on the nRF52 DK eval board, I get the expected current after enabling the "sense-edge-mask" property to the issue is not with the actual measurement. Custom hardware also gives expected current consumption after enabling "sense-edge-mask", but in this case I need to enable it on more pins since the custom hardware has a more complex setup with several external sensors configured to send IRQs to nrf52.

    What I'm trying to understand is the correct setup on the Zephyr API-level for nRF52 and external IRQs. This bug seems relevant:

    https://github.com/zephyrproject-rtos/zephyr/issues/28499

    Seems that at the bottom of this is this anomaly:

    https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52832_Rev2%2FERR%2FnRF52832%2FRev2%2Flatest%2Fanomaly_832_97.html

    Zephyr GPIO API seems to be using GPIOTE channels which are affected by anomaly 97. "sense-edge-mask" appears to use GPIO sense instead of GPIOTE, is that correct? Somewhere here is also GPIO PORT events mentioned, how are those related? Many hours googling and testing various old and new bits of information here...

    What I'd like to know is what is the current (as of nRF Connect SDK 2.5.x) supported and recommended way to setup edge triggered IRQs on external pins on the nRF52 SoC using the Zephyr API? 

    Please also comment on my questions regaring CONFIG_PM?

Reply
  • Current is measured using an Agilent 34401A DMM on both the custom hardware and on the nRF52 DK eval board, I get the expected current after enabling the "sense-edge-mask" property to the issue is not with the actual measurement. Custom hardware also gives expected current consumption after enabling "sense-edge-mask", but in this case I need to enable it on more pins since the custom hardware has a more complex setup with several external sensors configured to send IRQs to nrf52.

    What I'm trying to understand is the correct setup on the Zephyr API-level for nRF52 and external IRQs. This bug seems relevant:

    https://github.com/zephyrproject-rtos/zephyr/issues/28499

    Seems that at the bottom of this is this anomaly:

    https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52832_Rev2%2FERR%2FnRF52832%2FRev2%2Flatest%2Fanomaly_832_97.html

    Zephyr GPIO API seems to be using GPIOTE channels which are affected by anomaly 97. "sense-edge-mask" appears to use GPIO sense instead of GPIOTE, is that correct? Somewhere here is also GPIO PORT events mentioned, how are those related? Many hours googling and testing various old and new bits of information here...

    What I'd like to know is what is the current (as of nRF Connect SDK 2.5.x) supported and recommended way to setup edge triggered IRQs on external pins on the nRF52 SoC using the Zephyr API? 

    Please also comment on my questions regaring CONFIG_PM?

Children
  • Hi Daniel

    Håkon is currently unavailable, and I will help you out in the mean time. 

    Essentially the problem is the errata you point to, yes. By default the GPIO driver will use GPIOTE IN events for handling interrupts (callbacks), and because of this errata issue the sleep current in this case will not be in the single digit microamp range anymore. The solution is to add the sense-edge-mask property, which will make the GPIO driver use the GPIOTE PORT event rather than dedicated GPIOTE IN channels. 

    You can read more about the PORT event in the documentation here. Essentially this event can be enabled on any pin and is not limited by the number of GPIOTE channels, but whenever the event is triggered the GPIO driver needs to check the state of the GPIO registers to verify which of the enabled pins actually changed (since you no longer get a dedicated event for each pin). Using the PORT event bypasses the GPIOTE IN channel issue and means you will get significantly lower current consumption in sleep. 

    dnil-daniel said:
    What I'd like to know is what is the current (as of nRF Connect SDK 2.5.x) supported and recommended way to setup edge triggered IRQs on external pins on the nRF52 SoC using the Zephyr API? 

    You can look at standard Zephyr samples, such as the basic button sample, and add the sense-edge-mask property.  

    Maybe related question, on this nRF52832 SoC it doesn't seem possible to enable PM via "CONFIG_PM=y" since the SoC does not set the "HAS_PM" flag. Is PM and/or PM_DEVICE supported on the nRF SoCs, can drivers add hooks here for additional power savings?

    PM is not supported in the nRF devices for the simple reason that the power management system in most nRF devices is quite simple: Whenever you are not doing anything (ie the idle thread is running) the system will automatically put you in system on idle sleep mode, and this mode will not prevent any other peripherals in the system from running, so there is no need to use the PM module to set up complex policies or other rules to handle power management. 

    The critical aspect of this system though is that the sleep currents depend on whichever peripherals are running, so while the system is simple in use it does put some responsibility on the application developer to not leave peripherals running if they are not used. 

    The PM_DEVICE and PM_DEVICE_RUNTIME are supported by some of the peripheral drivers and can be used to put peripherals in sleep when they are not needed, which is critical to achieve low average currents when peripherals are in use. 

    PM_DEVICE allows you to disable device drivers in a standardized way, and can as an example be used to disable the UART when it is not used, since keeping the UART driver running will lead to very high sleep currents. 

    PM_DEVICE_RUNTIME is used by some drivers to handle power management in an automatic way, and if you use I2C device for instance it is highly recommended to enable this. Then the I2C peripheral will be automatically put to sleep whenever no I2C transactions are running. 

    Best regards
    Torbjørn

  • Thanks for providing a clear and comprehensive answer to this question, appreciated!

  • Glad I could help Daniel, the best of luck with your project Slight smile

Related