nRF51822 and Zephyr: Use nrfx gpiote for GPIO interrupts

Hello,

We are using Zephyr v2.6 and nRF51822 SoC in our project.

Because we are porting the code from the SDK environment into the Zephyr environment, we decided to use nrfx gpiote drivers instead of Zephyr GPIO drivers. Consequently, our prj.conf file contains the following lines:

CONFIG_GPIO=n
CONFIG_NRFX_GPIOTE=y

There are two GPIOs that should be configured to interrupt CPU. To achieve that, the following lines of code are used:

// GPIO 1
nrf_gpio_cfg_input(PIN_ACCEL_INT1, NRF_GPIO_PIN_NOPULL);
nrfx_gpiote_in_config_t in_config_1 = NRFX_GPIOTE_CONFIG_IN_SENSE_LOTOHI(false);
in_config_1.hi_accuracy = false;
in_config_1.pull = NRF_GPIO_PIN_PULLDOWN;
err_code = nrfx_gpiote_in_init(PIN_ACCEL_INT1, &in_config_1, gpiote_event_handler_1);

// GPIO 2
nrf_gpio_cfg_input(PIN_ACCEL_INT2, NRF_GPIO_PIN_NOPULL);
nrfx_gpiote_in_config_t in_config_2 = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(false);
in_config_2.hi_accuracy = false;
in_config_2.pull = NRF_GPIO_PIN_PULLUP;
err_code = nrfx_gpiote_in_init(PIN_ACCEL_INT2, &in_config_2, gpiote_event_handler_2);

//Enable interrupts
nrfx_gpiote_in_event_enable(PIN_ACCEL_INT1, true); 
nrfx_gpiote_in_event_enable(PIN_ACCEL_INT2, true); 

However, when I call the nrfx_gpiote_in_init() function for the second GPIO, this function returns 0xBAD0002 (NRFX_ERROR_NO_MEM).

Is there anything that I am missing here?

I've been told in some previous threads that nRF51822 contains 4 GPIOTE channels. Consequently, configuring two GPIOs to interrupt CPU should be feasible.

Thanks in advance for your time and efforts.

Sincerely,

Bojan.

Parents Reply Children
  • Hello, .

    Thanks for the reply. I was reading the example about using nrfx drivers in Zephyr envorionment. Consequently, I coonnected GPIOTE_0 IRQ to nrfx_gpiote_irq_handler with:

    IRQ_CONNECT(DT_IRQN(DT_NODELABEL(gpiote)),
        DT_IRQ(DT_NODELABEL(gpiote), priority),
        nrfx_isr, nrfx_gpiote_irq_handler, 0);

    I was also reading the GPIO and GPIOTE chapters from the reference manual. I also tried to manually set up inputs and two GPIOTE channels and use GPIOTE_IRQHandler() with the following piece of code:

    static void init_gpio_interrupts(void){
    
        nrf_gpio_cfg_input(PIN_ACCEL_INT1, NRF_GPIO_PIN_PULLDOWN);
        nrf_gpio_cfg_input(PIN_ACCEL_INT2, NRF_GPIO_PIN_PULLUP);
    
        NVIC_DisableIRQ(GPIOTE_IRQn);
        NVIC_ClearPendingIRQ(GPIOTE_IRQn);
    // GPIOTE Channel 0
        NRF_GPIOTE->CONFIG[0] =  (GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos)
                     | (PIN_ACCEL_INT1 << GPIOTE_CONFIG_PSEL_Pos)
                     | (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos);
        NRF_GPIOTE->INTENSET  = GPIOTE_INTENSET_IN0_Set << GPIOTE_INTENSET_IN0_Pos;
    
    // GPIOTE Channel 1
        NRF_GPIOTE->CONFIG[1] =  (GPIOTE_CONFIG_POLARITY_HiToLo <<
                      GPIOTE_CONFIG_POLARITY_Pos)
                      | (PIN_ACCEL_INT2 << GPIOTE_CONFIG_PSEL_Pos)
                      | (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos);
        NRF_GPIOTE->INTENSET  = GPIOTE_INTENSET_IN1_Set << GPIOTE_INTENSET_IN1_Pos;
    
        __NOP();
        __NOP();
        __NOP();
    
        /* Clear the event that appears in some cases */
        NRF_GPIOTE->EVENTS_IN[0] = 0;
        NRF_GPIOTE->EVENTS_IN[1] = 0;
    
        NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN0_Enabled << GPIOTE_INTENSET_IN0_Pos;
        NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN1_Enabled << GPIOTE_INTENSET_IN1_Pos;
        NVIC_EnableIRQ(GPIOTE_IRQn);
    }
    
    /**@brief Function for handling the GPIOTE interrupt.
     */
    void GPIOTE_IRQHandler(void)
    {
        uint32_t pins_state   = NRF_GPIO->IN;
    
        // Event causing the interrupt must be cleared.
        if ((NRF_GPIOTE->EVENTS_IN[0] == 1))
        {
            LOG_INF("Accel interrupt!");
            NVIC_DisableIRQ(GPIOTE_IRQn);
            NVIC_ClearPendingIRQ(GPIOTE_IRQn);
    
            NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN0_Enabled << GPIOTE_INTENSET_IN0_Pos;
    
            NVIC_EnableIRQ(GPIOTE_IRQn);
    
            NRF_GPIOTE->EVENTS_IN[0] = 0;
        }
    
        // Event causing the interrupt must be cleared.
        if ((NRF_GPIOTE->EVENTS_IN[1] == 1))
        {
            LOG_INF(".");
            NVIC_DisableIRQ(GPIOTE_IRQn);
            NVIC_ClearPendingIRQ(GPIOTE_IRQn);
    
            NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN1_Enabled << GPIOTE_INTENSET_IN1_Pos;
    
            NVIC_EnableIRQ(GPIOTE_IRQn);
    
            NRF_GPIOTE->EVENTS_IN[1] = 0;
        }
    
     }

    There were no compilation errors but I was unable to detect any interrupt with GPIOTE_IRQHandler() function.

    We are currently using nRF51 because we were unable to buy nRF52 SoCs at the moment. We certainly have a plan to transition to nRF52 but before that happens, we need to temporarily keep using nRF51 SoCs.

    So, the question of how to set up at least two GPIOs of nRF51822 to interrupt the CPU still remains an issue for us.

    When using Zephyr GPIO drivers, I was able to set up two GPIOs (but not more) to interrupt CPU as you can see in this thread.

    If the transaction manual says that there are four GPIOTE channels, how can I properly benefit from them and specify GPIOs connected/used in the channel?

    Thanks once again.

    Sincerely,

    Bojan.

Related