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

Correct way to handle GPIOTE port event

Hello DevZone,

For my project I have a number of pins generating interrupts from different sensors.

I've read that GPIOTE IN events consume a bit more current than the port event, so I opted to use the port event in my project.

I configure each interrupt pin to have high or low sensing depending on the logic level of that interrupt.

In my GPIOTE port event I handle the interrupt like this:

if (NRF_GPIOTE->EVENTS_PORT != 0)
{
    // Clear the event to allow it to trigger again.
    NRF_GPIOTE->EVENTS_PORT = 0;
    // Loop through all pins available on port 0.
    for (uint8_t i = 0; i < 32; i++)
    {
        // Check if there is a bit set in the latch register.
        if (NRF_P0->LATCH & 1 << i)

            // Clear the latch by writing a one to it.
            NRF_P0->LATCH |= 1 << i;
            // Switch statement for calling functions that have been generating the interrupt.
            switch (i)
            {
                case GYRO_INTERRUPT:
                    p_stMask->GyroInterrupt = true;
                    break; 
                case MAG_INTERRUPT:
                    p_stMask->MagnetoInterrupt = true;
                    break; 
                case TEMP_INTERRUPT:
                    p_stMask->TemperatureInterrupt = true;
                    break;
                default:
                    break
            }
        }
    }
}

In my main loop I process all pending interrupts because I do not want my interrupt routine to be very long.

The problem I am facing at this moment is that when I use a single sensor everything works fine but as soon as I use multiple sensors I start missing interrupts.

What I could see is that if the processor is in my GPIOTE loop and another pins senses a new change it doesn't process this.

Example:

All sensors run at their native sampling speed and the temperature sensor is the first to assert its interrupt pin.

The microcontroller senses this and calls the GPIOTE interrupt handler.

Whilst the microcontroller is processing the interrupt, the gyroscope asserts its interrupt pin.

The microcontroller does sense this, writes it to the latch register but doesn't process it.

I even tried to have the GPIOTE handler as short as possible by reading the latch register into a variable and then clear itself and ending the handler there.

This still causes some interrupts to not trigger.

// Check if the  port event has triggered.
if (NRF_GPIOTE->EVENTS_PORT != 0)
{
    // Clear the event to allow it to trigger again.
    NRF_GPIOTE->EVENTS_PORT = 0;
    // Logical OR the latch into a temporary variable.
    temp |= NRF_P0->LATCH;
    //Clear the latch.
    NRF_P0->LATCH = 0xFFFFFFFF;
}

Does anyone have an idea how I could tackle this problem?

I cannot use all GPIOTE IN events as I have more interrupt sources than available GPIOTE channels.

  • Hi,

    Did you try looking at the port_event_handle function in nrfx_gpiote.c? There are two different handlers: port_event_handle(uint32_t * latch) and port_event_handle(uint32_t * input). Both show how to check and clear the event. Also notice the loop in the handler to catch new events while the previous one is being handled.

  • I think I have found a working solution.

    I am sharing this solution so I can help others with the same problem.

    Firstly, I change the detect mode of the gpio port.

    NRF_P0->DETECTMODE = GPIO_DETECTMODE_DETECTMODE_LDETECT << GPIO_DETECTMODE_DETECTMODE_Pos;

    This allows the GPIOTE->PORT event to trigger every time the SENSE criteria is met. 

    To stop the microcontroller from spamming the GPIOTE->PORT event too much, I have chosen to change the sense level of that pin. This will cause it to trigger once on the rising and falling edge.

    if (NRF_GPIOTE->EVENTS_PORT != 0)
    {
        // Clear the event to allow it to trigger again.
        NRF_GPIOTE->EVENTS_PORT = 0;
        // Loop through all pins available on port 0.
        for (uint8_t i = 0; i < 32; i++)
        {
            // Check if there is a bit set in the latch register.
            if (NRF_P0->LATCH & 1 << i)
                // Change the sense of the pin.
                InvertSense(i);
                // Clear the latch by writing a one to it.
                NRF_P0->LATCH |= 1 << i;
                // Switch statement for calling functions that have been generating the interrupt.
                switch (i)
                {
                    case GYRO_INTERRUPT:
                        // only trigger if the pin is high.
                        if(NRF_P0->IN & (1 << GYRO_INTERRUPT))
                            p_stMask->GyroInterrupt = true;
                        break; 
                    case MAG_INTERRUPT:
                        // only trigger if the pin is high.
                        if(NRF_P0->IN & (1 << MAG_INTERRUPT))
                            p_stMask->MagnetoInterrupt = true;
                        break; 
                    case TEMP_INTERRUPT:
                        // only trigger if the pin is low.
                        if(!(NRF_P0->IN & (1 << TEMP_INTERRUPT)))
                            p_stMask->TemperatureInterrupt = true;
                        break;
                    default:
                        break
                }
            }
        }
    }

    In my OP I've added a function to invert the sense on that specific pin, stopping the microcontroller from spamming the GPIOTE->PORT event.

    The funcion InvertSense looks like this.

    void InvertSense(uint8_t pin)
    {
        // Flip bit 16 in the pin_cnf register, switching sense level.
        NRF_P0->PIN_CNF[pin] ^= 1 << 16;
    }

    In the switch from the GPIOTE port event I check if the state of the input is at the logic level that interrupt has (high or low) and if it is I set my interrupt flag.

    In my main loop I process all pending interrupts.

Related