How to generate a GPIOTE interrupt on the press of a button?

Hi, I am currently working on nRF52840-DK board and trying to implement simple programs. I want to toggle a LED(P0.13) on the press of a button(P0.11) but I dont want to use the SDK example where driver functions such as nrf_gpio_cfg_input are used but rather would like to modify registers to toggle leds on the press of a button.

This is the code I have come up with. I am not seeing the intended output. What things should I add and modify to enable interrupt?

#include "nrf52840.h"
#include "nrf52840_bitfields.h"
#include "nrf_delay.h"
#include "nrf_gpio.h"

#define LED 13
#define BUTTON 11

void GPIOTE_IRQHandler(void)
{
    if(NRF_GPIOTE->EVENTS_IN[0] != 0)
    {
        NRF_GPIOTE->EVENTS_IN[0] = 0;
        
		// toggle LED
		NRF_GPIOTE->TASKS_OUT[1] = 1;
    }

}

int blinky()
{
	/* Start LFCLK in low power mode driven by LFXO */
	NRF_CLOCK->LFRCMODE = CLOCK_LFRCMODE_MODE_ULP;
	NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_Xtal;
	NRF_CLOCK->TASKS_LFCLKSTART = 1;

   // config button as an event
    NRF_GPIOTE->CONFIG[0] = (GPIOTE_CONFIG_MODE_Event << 0) | (BUTTON << 8) |
				(GPIOTE_CONFIG_POLARITY_HiToLo << 16) ;
    // config led as a task
	NRF_GPIOTE->CONFIG[1] = (GPIOTE_CONFIG_MODE_Task << 0) | (LED << 8) |
				(GPIOTE_CONFIG_POLARITY_Toggle << 16) | (GPIOTE_CONFIG_OUTINIT_Low << 20);
	
	// enable interrupt 
	NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN0_Set;
    
    NVIC_EnableIRQ(GPIOTE_IRQn);

	return 0;

}

int main(void)
{

	blinky();

	while (1) {
		nrf_delay_ms(1000);
	}
}

  • Hi,

     

    A generic comment: I'd recommend that you use macros for shifting, like "GPIOTE_CONFIG_MODE_Pos" etc, and possibly the hal helper headers (nrf_gpio.h) so you do not need to think about which position you need to shift to etc.

    You'll need to set a pull on your input pin, in the NRF_GPIO->PIN_CNF[BUTTON] register:

    https://infocenter.nordicsemi.com/topic/ps_nrf52840/gpio.html?cp=4_0_0_5_8_1_9#register.PIN_CNF-0-31

     

    Could you try this and report back?

     

    Kind regards,

    Håkon

  • Hi,

    Thanks for the reply.I made a few changes to the above program. I am able to generate interrupt on pressing button.As you can see in the below program, I have used the gpio function nrf_gpio_cfg_input along with GPIOTE config register. Is it considered OK?

    Another thing is, I have configured LED to toggle, but the LED does not go completely off but just dims a little bit and then switches on, when i press the button again. What am i doing wrong here?

    .

    #include "nrf52840.h"
    #include "nrf52840_bitfields.h"
    #include "nrf_delay.h"
    #include "nrf_gpio.h"
    #include "printf.h"
    #include "uart.h"
    
    #include <nrf.h>
    #define PIN_LED 13
    #define PIN_BUTTON 11
    
    int main(void)
    {
      // Start LFCLK (32kHz) crystal oscillator. If you don't have crystal on your board, choose RCOSC instead.
      NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos;
      NRF_CLOCK->TASKS_LFCLKSTART = 1;
      while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0);
      NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
    
      // config button as an event
        NRF_GPIOTE->CONFIG[0] = (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) | (PIN_BUTTON << GPIOTE_CONFIG_PSEL_Pos) |
    				(GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos) ;
       
       // pull up for the button
       nrf_gpio_cfg_input(PIN_BUTTON,NRF_GPIO_PIN_PULLUP);
    
        // config led as a task
    	NRF_GPIOTE->CONFIG[1] = (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) | (PIN_LED << GPIOTE_CONFIG_PSEL_Pos) |
    				(GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos) | (GPIOTE_CONFIG_OUTINIT_Low << GPIOTE_CONFIG_OUTINIT_Pos);
    	
    	// enable interrupt 
    	NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN0_Msk;
        
        NVIC_EnableIRQ(GPIOTE_IRQn);
    
    
    	while (1)
      {
        __WFE();
    
      }
    }
    
    void GPIOTE_IRQHandler(void)
    {
        volatile uint32_t dummy;
        if(NRF_GPIOTE->EVENTS_IN[0] != 0)
        {
            NRF_GPIOTE->EVENTS_IN[0] = 0;
           
    	   NRF_GPIOTE->TASKS_OUT[1] = !(NRF_GPIOTE->TASKS_OUT[1]);
        // Read back event register so ensure we have cleared it before exiting IRQ handler.
           dummy = NRF_GPIOTE->EVENTS_IN[0];
           dummy;
        }
    
    }

  • Hi,

     

    If the LED dims/blinks, it sounds like the input to the pin is floating, causing the task to be triggered seemingly at random.

    Here's the test program that I use, which is essentially your original one with setting a pull-up on the button:

    #include "nrf52840.h"
    #include "nrf52840_bitfields.h"
    #include "nrf_delay.h"
    #include "nrf_gpio.h"
    
    #define LED 13
    #define BUTTON 11
    
    void GPIOTE_IRQHandler(void) {
      if (NRF_GPIOTE->EVENTS_IN[0] != 0) {
        NRF_GPIOTE->EVENTS_IN[0] = 0;
    
        // toggle LED
        NRF_GPIOTE->TASKS_OUT[1] = 1;
      }
    }
    
    int blinky() {
      /* Start LFCLK in low power mode driven by LFXO */
      NRF_CLOCK->LFRCMODE = CLOCK_LFRCMODE_MODE_ULP;
      NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_Xtal;
      NRF_CLOCK->TASKS_LFCLKSTART = 1;
    
      // config button as an event
      NRF_GPIOTE->CONFIG[0] = (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) | (BUTTON << 8) |
                              (GPIOTE_CONFIG_POLARITY_HiToLo << 16);
      // config led as a task
      NRF_GPIOTE->CONFIG[1] = (GPIOTE_CONFIG_MODE_Task << 0) | (LED << 8) |
                              (GPIOTE_CONFIG_POLARITY_Toggle << 16) |
                              (GPIOTE_CONFIG_OUTINIT_High << 20);
    
      // enable interrupt
      NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN0_Set;
    
      NVIC_EnableIRQ(GPIOTE_IRQn);
    
      return 0;
    }
    
    int main(void) {
    
      blinky();
      nrf_gpio_cfg_input(BUTTON, NRF_GPIO_PIN_PULLUP);
     
      while (1) {
        nrf_delay_ms(1000);
      }
    }
    

     

    Note that I set the task in the ISR each time, as the task is "toggle" - ie. no need to toggle the task register as well.

     

    Kind regards,

    Håkon

Related