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

Confusion with NRF_GPIOTE->EVENTS_PORT

I have my own code for configuring digital IO pins. I'm using the PCA 10040 devKit board. When running in the debugger, on startup, the interrupts on the input pins seemed to worked 95% of the time. Every once in a while, it seemed that interrupts just weren't being triggered. It wasn't just my own digital input pins; it was also the buttons on the board. On the other hand, when not running in the debugger, I frequently had problems with the interrupts when I first powered the board. After cycling the power a bunch of times, it suddenly started working and everything was fine, so clearly there was some problem with the configuration code. It looks like I have fixed it, but I don't understand why, so I don't trust it.

I'm using the EVENTS_PORT event to capture the input pin changes. I'm not creating GPIOTE channels.

Here's my configuration routine.

void CpuPinInit(void)
{
	CpuPinEnum	i;

	//Disable the port event as an interrupt source during configuration.
	NRF_GPIOTE->INTENCLR = GPIOTE_INTENSET_PORT_Msk;

	for (i = CpuPinFIRST; i < CpuPinCOUNT; i++)
	{
		if (cpuPinConfigOnInit[i])
			CpuPinConfigure(i);
	}

	//Clear any pending event port interrupt source.
	NRF_P0->LATCH = 0xFFFFFFFF;
	NRF_GPIOTE->EVENTS_PORT = 1;

	//Configure and enable the interrupt.
	NVIC_SetPriority(GPIOTE_IRQn, CPU_PIN_GPIO_IRQ_PRIORITY);
	NVIC_ClearPendingIRQ(GPIOTE_IRQn);
	NVIC_EnableIRQ(GPIOTE_IRQn);

	//Enable the port event as an interrupt source.
	NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_PORT_Msk;
}

I'm attempting to ensure there are no spurious interrupts on startup by clearing the latches and the EVENTS_PORT event register. As you can see in the code, I'm setting EVENTS_PORT to 1. That looks wrong to me. I should be setting it to 0, but setting it to 1 seemed to solve my problem. Can anyone shed some light on this?

Parents
  • Hi John, 

     

    Setting  NRF_GPIOTE->EVENTS_PORT = 1; doesn't seem the correct way to do it. 

    Could you post your full code, how did you configure the pins ? If you make a very simple application to just enable one pin with sense and enable GPIOTE Port event do you have any issue ? 

    Note that, if you have multiple pins that can trigger GPIOTE port, if one pin is active, other pins can't trigger another GPIOTE Port (the DETECT signal held active)

  • OK, so I'm using the default DETECTMODE, not using LDETECT. If I understand correctly, that  means if I clear the LATCH register, but an individual pin detect is still high then I'm going to get stuck in the situation where the interrupt won't get triggered because the DETECT signal will never trigger EVENTS_PORT.  By that reasoning I just took out the clearing of the LATCH register and the clearing of the EVENTS_PORT register in my pin configuration routine and everything seems to work fine now. I think I was being too defensive trying to set those registers unnecessarily. 

    Does that make sense?

Reply
  • OK, so I'm using the default DETECTMODE, not using LDETECT. If I understand correctly, that  means if I clear the LATCH register, but an individual pin detect is still high then I'm going to get stuck in the situation where the interrupt won't get triggered because the DETECT signal will never trigger EVENTS_PORT.  By that reasoning I just took out the clearing of the LATCH register and the clearing of the EVENTS_PORT register in my pin configuration routine and everything seems to work fine now. I think I was being too defensive trying to set those registers unnecessarily. 

    Does that make sense?

Children
  • Hi John, 

    If you are running in the default DETECTMODE, the LATCH register should not affect the DETECT signal (see Figure 21 in GPIO chapter). But what you described here is correct: "an individual pin detect is still high then I'm going to get stuck in the situation where the interrupt won't get triggered because the DETECT signal will never trigger EVENTS_PORT". Please see Figure 22. 

    Clearing EVENTS_PORT before you enable the event is suggested. 

     

  • OK, so based on your feedback, I was determined to clear EVENTS_PORT as part of my configuration. I walked through the gpiote init code and discovered several differences from my implementation. I decided to make the changes to my code one at a time to get a sense of what the key ingredient was. Here's what I did in the order I did it.

    1. I noticed that the example code does not clear the LATCH register, so I added back my line of code that sets NRF_GPIOTE->EVENTS_PORT, but I assigned it 0, instead of 1. I left the LATCH assignment deleted. That did not work. I had the same failure. Interestingly, the problem occurred more reliably in the debugger.

    2. I noticed the example code clear EVENTS_PORT after enabling the after enabling the GPIOTE IRQ, but before enabling the EVENTS_PORT source, so I moved the instruction in my code to match the example order of operations. That did not solve the problem.

    3. I noticed that the example code configures the individual pins after enabling the GPIOTE IRQ and configuring it with the EVENTS_PORT source, so I moved my pin configuration for loop to occur after the interrupt configuration. That seems to solve the problem. I am now successfully setting EVENTS_PORT to 0 as part of my init code.

    Here's the modified code:

    void CpuPinInit(void)
    {
        CpuPinEnum i;
        
        //Disable the port event as an interrupt source during configuration.
        NRF_GPIOTE->INTENCLR = GPIOTE_INTENSET_PORT_Msk;
    
        //Configure and enable the GPIOTE interrupt.
        NVIC_SetPriority(GPIOTE_IRQn, CPU_PIN_GPIO_IRQ_PRIORITY);
        NVIC_ClearPendingIRQ(GPIOTE_IRQn);
        NVIC_EnableIRQ(GPIOTE_IRQn);
        
        //Enable the port event as an interrupt source.
        NRF_GPIOTE->EVENTS_PORT = 0;
        NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_PORT_Msk;
        
        //Configure each pin.
        for (i = CpuPinFIRST; i < CpuPinCOUNT; i++)
        {
            if (cpuPinConfigOnInit[i])
                CpuPinConfigure(i);
        }
    }
    
    

    Now it matches the order of operations in the example code and it seems to work. I guess it all makes sense. Clearing the LATCH register here doesn't really do anything in the normal DETECT mode because the LATCH register just gets reloaded with the same bits without generating a new DETECT signal. It's essential a NOP.

    Clearly, in the time it takes to configure the pins, at least one of the pins has its DETECT signal triggered, which triggers the EVENTS_PORT, so clearing it after configuring the pins means that the IRQ source is cleared, but the DETECT condition is not. By doing the pin configuration last, the interrupt is enabled should the configuration cause the DETECT condition to be met. I suspect that setting EVENTS_PORT to 1 in my original code forced the interrupt to occur when it was then enabled, even if there was no DETECT signal to cause it.

    Hopefully, that explains things?

  • BTW, I just want to point out why my original code was written the way it was. According to the documentation for the port event in the GPIOTE section:

    "In order to prevent spurious interrupts from the PORT event while configuring the sources, the user shall first disable interrupts on the PORT event (through INTENCLR.PORT), then configure the sources (PIN_CNF[n].SENSE), clear any potential event that could have occurred during configuration (write '1' to EVENTS_PORT), and finally enable interrupts (through INTENSET.PORT)."

    So I did this and it did work, as long as I wrote a 1 to EVENTS_PORT. The documentation is correct in the sense that it does work, but I think it works because the IRQ is force triggered; not because the source is cleared. The example code does not match this documentation. I rewrote my code to match the example and it also works. They both work for different reasons, but I guess the example code is considered best practice, which is fine. I think the documentation should be changed to match.

  • Hi John, 

     

    Thanks for pointing it out. It's actually a bug in our documentation. It was reported here, but we haven't updated the spec since then. 

    You are right about the pin configuration order. I agree that the pin should be configured after the GPIOTE interrupt is configured to avoid the DETECT pin already high when the GPIOTE interrupt is enabled. 

Related