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

How do I interface low-latency pin change interrupts with the app_gpiote service?

Hello, before you say anything: "yes I have searched!" :D I found that this question was similar to mine How to write GPIOTE Interrupt

Now I am fairly certain that I can handle that part just fine, however here is when things get tricky. After stealing "GPIOTE_IRQHandler" from app_gpiote (removing app_gpiote and implementing the IRQHandler in my own module), the BLE softdevice would no longer start up. The error code provided was 0x0000001. Yes I looked it up, but wasn't able to glean any information from it.

When I subscribe using the standard

#define HAL_AFE_ADC_RDY_PIN 13
app_gpiote_user_id_t hGPIOTE = 0;
...
{
	err_code = app_gpiote_user_register(&hGPIOTE, (1 << HAL_AFE_ADC_RDY_PIN), 0x00000000, app_gpiote_event_handler);
	APP_ERROR_CHECK(err_code);
	err_code = app_gpiote_user_enable(hGPIOTE);
	APP_ERROR_CHECK(err_code);
}
...

I get latencies of up to 500 milliseconds in receiving the callback from this handler. The signal I need to detect is a logic level high pulse with 250 nanosecond width, active high. It is on pin 13.

My question is this: How do I use pin change interrupts correctly while still being able to use your bluetooth low energy softdevice?

Code dump below:


#include "nrf_gpio.h"
#include "app_gpiote.h"

#define HAL_AFE_ADC_RDY_PIN  13

app_gpiote_user_id_t hGPIOTE = 0;

void app_gpiote_event_handler(uint32_t event_pins_low_to_high, uint32_t event_pins_high_to_low)
{
	uint8_t i = 2;
	uint32_t k[] = {0, 0, 0, 0, 0, 0};
	
	event_pins_low_to_high++;
	event_pins_high_to_low++;
	i++;
	HalAfe_HW_ReadADC(k);
}

static void halAfe_ConfigIO(void)
{
  uint32_t err_code = 0;
  /* GPIO configuration */
	AFE_CONFIGURE_RESET();
	AFE_RELEASE_RESET();
	
  nrf_gpio_cfg_input(HAL_AFE_ADC_RDY_PIN, NRF_GPIO_PIN_NOPULL);
	err_code = app_gpiote_user_register(&hGPIOTE, (1 << HAL_AFE_ADC_RDY_PIN), 0x00000000, app_gpiote_event_handler);
	APP_ERROR_CHECK(err_code);
	err_code = app_gpiote_user_enable(hGPIOTE);
	APP_ERROR_CHECK(err_code);
}

  • The error codes returned by the softdevice is defined in nrf_error.h. As you can see there, 0x0001 means NRF_ERROR_SVC_HANDLER_MISSING. Are you sure that you have compiled your application with the correct header files, and that the softdevice is flashed correctly before trying to run the application?

    This isn't an error we see very often though, and it should not normally be returned by the enable function, so are you sure that the value isn't actually 0x1001, NRF_ERROR_SDM_INCORRECT_INTERRUPT_CONFIGURATION, as defined in nrf_error_sdm.h? If so, that would mean that your GPIOTE interrupt handler is configured with a too high priority. In the appendix of the nRF51 Reference Manual, a description of how the different priorities are used is given, so this can be useful to have a look at. The summary is that you must make sure that you don't set any of your own interrupts to have priority 0 or 2, but only use 1 and 3. The priority of an interrupt can be set with sd_nvic_SetPriority()/NVIC_SetPriority(), depending on whether the softdevice is enabled or not.

    With regard to timing, you write that you must detect a narrow pulse, which you should be able to easily do, but do you also have a requirement on how long time it takes before the handler runs? This could be more problematic, as this will be dependent on a number of factors. As you can see in the S110 SoftDevice Specification, the maximum interrupt latency is in the order of milliseconds when the softdevice is enabled and there is an ongoing connection with data transfer, and there isn't much you can do about this.

    Do you see acceptable latencies when you implement your own GPIOTE_IRQHandler instead of using app_gpiote?

  • Yes, your response led me to the solution as always. I had quoted you the wrong error code. I was only looking at nrf_error.h so I disregarded the NRF_ERROR_SDM_BASE_NUM portion.

    I'll add my notes to this post so I may be able to help others who encounter this issue. I ended up removing the app_gpiote service/driver/module from my project since we're working with very tight timing constraints and only have one external interrupt to process.

    With that out of the way, I found that I needed to re-order my code in order to 'play nicely' with the softdevice. That meant placing my configuration code after ble_stack_init() in the main() function since it contained calls prefixed with sd_.

    Code dump below:

    
    void GPIOTE_IRQHandler(void)
    {
        // Event causing the interrupt must be cleared.
        if ((NRF_GPIOTE->EVENTS_IN[0] == 1) && 
            (NRF_GPIOTE->INTENSET & GPIOTE_INTENSET_IN0_Msk))
        {
            NRF_GPIOTE->EVENTS_IN[0] = 0;
    			
    				uint8_t i = 2;
    				uint32_t k[] = {0, 0, 0, 0, 0, 0};
    				
    				i++;
    				HalAfe_HW_ReadADC(k);
        }
    }
    
    static void halAfe_ConfigIO(void)
    {
      uint32_t err_code = 0;
    	AFE_CONFIGURE_RESET();
    	AFE_RELEASE_RESET();
    	
      nrf_gpio_cfg_input(HAL_AFE_ADC_RDY_PIN, NRF_GPIO_PIN_NOPULL);
    	
    	
    	NRF_GPIOTE->INTENSET  = GPIOTE_INTENSET_IN0_Set << GPIOTE_INTENSET_IN0_Pos;
    	nrf_gpiote_unconfig(0);
    	nrf_gpiote_event_config(0, HAL_AFE_ADC_RDY_PIN, NRF_GPIOTE_POLARITY_LOTOHI);
    	err_code = sd_nvic_SetPriority(GPIOTE_IRQn, 1);
      err_code = sd_nvic_EnableIRQ(GPIOTE_IRQn);
    }
    

    I found that either enabling the IRQ last was necessary but it may not be since I was erroneously trying to configure channel 1. I do not know if the NRF_GPIOTE->INTENSET operation is necessary.

    Thanks Ole!

  • Thanks, this saved my day. I had two almost identical codes, one was working, one was hardfaulting with err_code 0x1001. My problem was the UART interrupt priority which was not set, I needed to add NVIC_SetPriority(UART0_IRQn, APP_IRQ_PRIORITY_LOW);

Related