This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

GPIOTE handler module or GPIOTE driver for PORT interrupt

On dev kit nRF52 PCA10040, rev 0.9.0, Keil uVision 5.21.1.0, SDK nRF5_SDK_12.1.0_0d23e2a - I need to monitor 9 input signals for a transition (hi to lo) on any of the pins. This would generate an interrupt and then I need to capture the state of these pins at high speed for a period of time, and once that time period is expired, return to sleep and wait for another transition on any of the 9 pins. Questions:

  1. In the "low power" mode monitoring the pins using the DETECT output and the PORT event, what is the latency between the GPIO pin changing state and the invocation of the ISR?
  2. Should I be using the GPIOTE handler module (documented here - nRF5 SDK > nRF5 SDK v12.1.0 > API Reference > SDK common libraries) or not? I've seen conflicting comments regarding its obsolescence.
  3. If I should be using the GPIOTE driver, it isn't clear if the nrf_drv_gpiote_in_init will do the job pursuant to the following comment in the infocenter: "In this case only one active pin can be detected at a time. It can be worked around by setting all of the used low accuracy pins to toggle mode." This is about as clear as mud. This in_init method only works on one pin, so is configuring each of the 9 pins using the "GPIOTE_CONFIG_IN_SENSE_HITOLO" macro sufficient to ensure that a change in state of any one of the 9 causes the DETECT signal and the PORT interrupt?
  4. The GPIOTE handler module seems more geared to what I'm trying to do, but in that module there are a number of ways to register actions: ...end_irq_event_handler_register, ...input_event_handler_register, app_gpiote_user_register. what is the salient difference between these methods given the context of my desired functionality?

Please see the code block for my stab at initializing the GPIOTE using the driver for this scenario. Will this produce the expected result? It doesn't seem correct to specify (using in_init) the same function pointer (for the handler) for every one of the 9 pins - with the SENSE config does it just magically know to connect these all to the PORT event?

	// Disable interrupts
NRF_GPIOTE->INTENCLR = 0xFFFFFFFF;

// Use the built-in macro to define the input pin config struct
// Elements of this struct are defined in nrf_drv_gpiote.h
nrf_drv_gpiote_in_config_t config = GPIOTE_CONFIG_IN_SENSE_HITOLO(false);
// Macro does not set the pullup or pulldown
config.pull = NRF_GPIO_PIN_PULLUP;
// Initialize the specific pin (P0.xx) #defines are in pca10040.h
// This also allocates a channel (hardware resource) that needs to be returned to the pool when finished
err_code = nrf_drv_gpiote_in_init(HESA0_TOP_PIN_NUMBER, &config, hesa_pin_event_handler);
    err_code = nrf_drv_gpiote_in_init(HESA1_TOP_PIN_NUMBER, &config, hesa_pin_event_handler);
    // ...do this for 7 more pins
APP_ERROR_CHECK(err_code);
// Enable event for this pin, no interrupt
nrf_drv_gpiote_in_event_enable(HESA0_TOP_PIN_NUMBER, false);
    nrf_drv_gpiote_in_event_enable(HESA1_TOP_PIN_NUMBER, false);
    // ...do this for 7 more pins
// Enable PORT interrupt
NRF_GPIOTE->INTENSET = 0x80000000;
  • 1.The interrupt latency of the nRF52 when waking up from System ON mode has been measured to 12us , please see this answer for a more detailed explanation.

    2.Yes, I would recommend using the nrf_drv_gpiote driver over the app_gpiote library as it allows you to have greater control.

    3.What kind of inputs are the 9 signals? Will any of the signals transition from high to low simultaneously? Is this a battery-powered device? Depending on the answers to the questions above you have the following options:

    If power-consumption is not an issue and the signals may tranition at the same time, then I would intialize the pins with nrf_drv_gpiote_in_init with a nrf_drv_gpiote_in_config_t struct where hi_accuracy is set to true and register a separate event handler for each of the pins. (Note: There are only 8 GPIOTE channels so you might have to re-configure one of the pins once you enter the GPIOTE IRQ handler).

    If none of the signals transition at the same time, then you can configure the pins to use the PORT event at all times. This is given that the signals are kept low long enough so that you can check which pin generated the PORT event when you enter the GPIOTE IRQ handler by checking which pin that is high.

    If power consumption is an issue, then you can configure the pins to use the PORT event (i.e. hi_accuracy set to false). The first transition will then wake the chip from low power mode and you can reconfigure the pins to use the IN Event (i.e. hi_accuracy set to true) in the event handler. Once the high speed sampling is over you can reconfigure the pins to again use the PORT event and wait for the next transition while in low power mode.

    4.The event handler registered with app_gpiote_end_irq_event_handler_register will be called everytime the end of the GPIOTE IRQ handler is reached, regardless of which pin that generated the input. The event handler registered with app_gpiote_input_event_handler_register will only be called when an IN Event is generated by the pin specified. The app_gpiote_user_registerfunction allows you to register multiple users so that the GPIOTE interrupts/events are send to both users separate event handler function.(Note: Two users cannot register on the same pins).

  • Thank you for that information, but now I'm getting an out of memory error when running in_init on the 5th pin. Registering an event handler like you suggest creates a conflict between the GPIOTE_IRQHandler called by the (obsolete) GPIOTE handler and the one called by the GPIOTE driver. What am I missing here?

    // Disable interrupts
    NRF_GPIOTE->INTENCLR = 0xFFFFFFFF;
    // Clear the latch register
    NRF_P0->LATCH = 0xFFFFFFFF;
    
    //NRF_LOG_INFO("setting up hesa\r\n");
    
    // Use the built-in macro to define the input pin config struct
    // Elements of this struct are defined in nrf_drv_gpiote.h
    nrf_drv_gpiote_in_config_t config = GPIOTE_CONFIG_IN_SENSE_HITOLO(false);
    // Macro does not set the pullup or pulldown
    config.pull = NRF_GPIO_PIN_PULLUP;
    // Initialize the specific pin (P0.xx) #defines are in pca10040.h
    // This also allocates a channel (hardware resource) that needs to be returned to the pool when finished
    err_code = nrf_drv_gpiote_in_init(HESA_TOP_PIN_NUMBER, &config, hesa_pin_event_handler);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_gpiote_in_init(HESA_BOT_PIN_NUMBER, &config, hesa_pin_event_handler);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_gpiote_in_init(HESA_LFT_PIN_NUMBER, &config, hesa_pin_event_handler);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_gpiote_in_init(HESA_RHT_PIN_NUMBER, &config, hesa_pin_event_handler);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_gpiote_in_init(HESA_MID_PIN_NUMBER, &config, hesa_pin_event_handler);
    APP_ERROR_CHECK(err_code); // memory error (code = 4)
    
  • If you're getting NRF_ERROR_NO_MEM when calling nrf_drv_gpiote_in_init then you have used up all your GPIOTE channels, see the driver documentation here. If you're using the driver then you have to remove the files and codes that used the GPIOTE handler.

  • Have you set the increased the #define GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS in sdk_config.h from 4 to 8?

Related