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

PORT event interupt while sensed pin is active

Hello!

I am using the PORT event interupt sense handler to wake up the device from various pin interupts. We have in total 6 pins that need to do that.

This works fine, except for when a current pin generated an event that stays in this detected state for a while. For example, see these scope images:

The green line is a 1 hz signal from an external RTC. This sometimes needs to wakeup the device once a second (hence the interupt sense method choosen is HI to LO).

The yellow line is an interupt that needs to wakeup the device as well, but needs only to trigger an interupt when something is near (IR detection).

The problem i am facing is that during the RTC interupt from HI to LO, this IR detection interupt gets ignored, because the PORT sense event only can generate an interupt if the current pin that generated the interupt goes back to the non triggered state (AFAIK and is during debugging very apparant).

We also have a pin that is pulled LO and stays low as long as a certain cable is connected to our device, which needs to prevent the device to sleep during this event. In this case, ALL interupts from the other PORT event pins get ingnored for the set interupt event.

I tried the IN EVENT mode. That works fine for what we need, but uses way too much current during the sleep period of the device.

I tried to switch from the PORT event to the IN EVENT during wake times, but thats not ideal and gave me errors during switching.

Is there a way I can "reset" the current pin state, that generated the interupt in the PORT event, so that I can receive other interupts?

It is very important that we always have every 6 of these pins generate the interupt and that we know what pin did it.

(part of) my code:

void PIN_WAKE_ON_FTDI_IRQHandler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) // FTDI connect
{	if(pin == INTERFACE_CABLE_DETECT_PIN)
	{	PM_WakedBy |= WAKE_ON_FTDI;
	}
}

void PIN_WAKE_ON_CARDDT_IRQHandler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) //EM = Toetsen of IR
{	if(pin == EM_IRQ_PIN)
	{	PM_WakedBy |= WAKE_ON_CARDDT;
	}
}

void PIN_WAKE_ON_RTC_IRQHandler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) // WAKE_ON_RTC
{	if(pin == RTC_PCF2123_CLKOUT_PIN)
	{	PM_WakedBy |= WAKE_ON_RTC;
	}
}


void PM_Init(void)
{	ret_code_t err_code;
	nrfx_gpiote_in_config_t nrfx_gpiote_in_config;
	err_code = nrfx_gpiote_init(); // init de GPIOTE module
	APP_ERROR_CHECK(err_code);

	nrfx_gpiote_in_config.hi_accuracy = false; // Set hi_accu to false for PORT event
	
	// WAKE_ON_FTDI this is a pin that stays low during placement of the cable and needs to prevent the device from sleep
	nrfx_gpiote_in_uninit(INTERFACE_CABLE_DETECT_PIN);
	nrfx_gpiote_in_config.sense = NRF_GPIOTE_POLARITY_HITOLO;
	nrfx_gpiote_in_config.pull = (nrf_gpio_pin_pull_t)INTERFACE_CABLE_DETECT_PIN_PULL_CFG;
	err_code = nrfx_gpiote_in_init(INTERFACE_CABLE_DETECT_PIN, &nrfx_gpiote_in_config, PIN_WAKE_ON_FTDI_IRQHandler);  
	APP_ERROR_CHECK(err_code);
	nrfx_gpiote_in_event_enable(INTERFACE_CABLE_DETECT_PIN, true);
	
	// WAKE_ON_CARDDT this is a pin that needs to detect a near detection at all times
	nrfx_gpiote_in_uninit(EM_IRQ_PIN);
	nrfx_gpiote_in_config.sense = NRF_GPIOTE_POLARITY_HITOLO;
	nrfx_gpiote_in_config.pull = (nrf_gpio_pin_pull_t)EM_IRQ_PIN_PULL_CFG;
	err_code = nrfx_gpiote_in_init(EM_IRQ_PIN, &nrfx_gpiote_in_config, PIN_WAKE_ON_CARDDT_IRQHandler);
	APP_ERROR_CHECK(err_code);
	nrfx_gpiote_in_event_enable(EM_IRQ_PIN, true);	
	
	// WAKE_ON_RTC this is a 1 hz interupt pin that sometimes needs to wake the device every second for a few seconds
	nrfx_gpiote_in_uninit(RTC_PCF2123_CLKOUT_PIN);
	nrfx_gpiote_in_config.sense = NRF_GPIOTE_POLARITY_HITOLO;
	nrfx_gpiote_in_config.pull = (nrf_gpio_pin_pull_t)RTC_PCF2123_CLKOUT_PIN_PULL_CFG;
	err_code = nrfx_gpiote_in_init(RTC_PCF2123_CLKOUT_PIN, &nrfx_gpiote_in_config, PIN_WAKE_ON_RTC_IRQHandler);
	APP_ERROR_CHECK(err_code);
	nrfx_gpiote_in_event_enable(RTC_PCF2123_CLKOUT_PIN, true);
}

	

  • Hi,

    You cannot get multiple port events (interrupts) unless the firs pin(s) is deasserted before the next is asserted. The reason is that the common DETECT signal, which generates the event is a simple logical OR of each pins detect signal (PINx.DETECT). You can see this from Figure 2. DETECT signal behavior.

    The only other option I can think of is to poll the LATCH register to see if more pins have been asserted. From GPIO chapter in PS:

    The CPU can read the LATCH register at any time to check if a SENSE condition has been met on one or more of the the GPIO pins even if that condition is no longer met at the time the CPU queries the LATCH register. This mechanism will work even if the LDETECT signal is not used as the DETECT signal.

  • Thank you.

    Polling the LATCH register is not realy an option.

    I tried however to use the TOGGLE sense option for the 1 hz pin and the cable detect pin. This seems to work a bit, as the other interupt pins can be handled now while a toggled sense pin is HI or LO. This seems to indicate that it should be possible to sort of "reset" the PORT event, cause using TOGGLE seems to reset the triggered state of these pins.

    I cant however find what the TOGGLE mode does that makes the pin not blocking the other interupts while it is in the state when it generated the interupt...

  • So basicly I "fixed" it myself by changing the interupt handler of GPIOTE:

                        if (handler || (polarity == NRF_GPIOTE_POLARITY_TOGGLE))
                        {
                            if (polarity == NRF_GPIOTE_POLARITY_TOGGLE)
                            {
                                nrf_bitmask_bit_set(pin, toggle_mask);
                            }
                            nrf_gpio_pin_sense_t sense     = nrf_gpio_pin_sense_get(pin);
                            uint32_t             pin_state = nrf_bitmask_bit_is_set(pin, input);
                            if ((pin_state && (sense == NRF_GPIO_PIN_SENSE_HIGH)) ||
                                (!pin_state && (sense == NRF_GPIO_PIN_SENSE_LOW))  )
                            {
                                NRFX_LOG_DEBUG("PORT event for pin: %d, polarity: %d.", pin, polarity);
                                //if (polarity == NRF_GPIOTE_POLARITY_TOGGLE)
                                //{
                                    nrf_gpio_pin_sense_t next_sense =
                                        (sense == NRF_GPIO_PIN_SENSE_HIGH) ?
                                        NRF_GPIO_PIN_SENSE_LOW :
                                        NRF_GPIO_PIN_SENSE_HIGH;
                                    nrf_gpio_cfg_sense_set(pin, next_sense);
                                    ++repeat;
    
                                //}
                                if (handler)
                                {
                                    handler(pin, polarity);
                                }
                            }
                        }

    By above changes (commenting out the //if (polarity == NRF_GPIOTE_POLARITY_TOGGLE) line), the other interupts now work when one of the pins is set from HI to LO event trigger and stays LO.

  • Stupid me, that basicly made it a toggling sense mode for all the PORT interupt pins, wheter they are set to HI to LO or LO to HI.

  • Hi,

    Yes, that looks like a neat hack. It seems like you will have a race condition, though. If the second pin is inserted before you change the sense polarity of the first pin, then you would not get an interrupt. That might not be something that would happen on your particular HW, but it is worth noting.

Related