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.

  • For the second GPIO, I tried to allocate GPIOTE channel before initializing it with:

        uint8_t channel;
        err_code = nrfx_gpiote_channel_alloc(&channel);
        err_code = nrfx_gpiote_in_prealloc_init(PIN_ACCEL_INT2, &in_config_2, channel, gpiote_event_handler_2);

    nrfx_gpiote_channel_alloc() function properly allocates GPIOTE channel but the nrfx_gpiote_in_prealloc_init() returns 0xBAD0004 (NRFX_ERROR_INVALID_PARAM).

    According to the nrfx_gpiote_in_prealloc_init() brief description, this return code means that the pin is configured to not be controlled by the GPIOTE task and cannot be used with preallocated channel. It is also advised to use nrfx_gpiote_in_init() instead which, as I reported previously, returns 0xBAD0002 (NRFX_ERROR_NO_MEM).

  • Hello,

    I suggest to check out:
    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/samples/boards/nrf/nrfx/README.html and
    https://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf (chapter 15 GPIOTE).

    The reference manual will describe the difference between port events (low power wakeup) and IN event (higher current, but can be connected by PPI to tasks).

    All that said, are you sure you want to use NCS with nRF51-series? I do not believe there is proper production support there, for instance I can not find NCS listed in the compatibility matrix for the nRF51822 vs nRF52832 for instance:
    https://infocenter.nordicsemi.com/topic/comp_matrix_nrf51/COMP/nrf51/nRF51422_nRF51822_ic_rev_sdk_sd_comp_matrix.html vs.
    https://infocenter.nordicsemi.com/topic/comp_matrix_nrf52832/COMP/nrf52832/ic_rev_sdk_sd_comp_matrix.html 

    If you are making an BLE application for the nRF51-series you would need to use the zephyr BLE controller with the additional cost and time that requires for qualification (since it's not qualified by Nordic). For new development I highly recommend to transition to the nRF52-series.

    Best regards,
    Kenneth

  • 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