How do I set up GPPI/DPPI with the new nrfx 4.0.1 drivers?

I am evaluating the nRF54L15 with the nRF54L15DK. Previously, I was developing with NCS 3.1.1 and successfully implemented a high precision frequency measurement using a timer, the gpiote and connecting them using the gppi helper functions. The relevant part of code which was doing that is this:

static void connect_edge_to_timer(uint32_t pin)
{
    nrfx_err_t err;
    uint8_t gppi_ch;

    err = nrfx_gppi_channel_alloc(&gppi_ch);
    NRFX_ASSERT(err == NRFX_SUCCESS);

    uint32_t evt_addr = nrfx_gpiote_in_event_address_get(&gpiote, pin);
    uint32_t task_addr = nrfx_timer_task_address_get(&timer_inst,
                                                     NRF_TIMER_TASK_CAPTURE0);

    nrfx_gppi_channel_endpoints_setup(gppi_ch, evt_addr, task_addr);
    nrfx_gppi_channels_enable(BIT(gppi_ch));
}

I want to update the project to NCS 3.2.1 with the new nrfx 4.0.1 drivers. The nrfx 4.0 migration notes state: "Contrary to the legacy solution, GPPI needs to be initialized prior to using the GPPI API. Ensure that nrfx_gppi_init is called before calling any GPPI API." Also "Removed nrfx_gppi_event_endpoint_setup. Action : Use nrfx_gppi_ep_attach instead."

I don't understand how to initiallize gppi with the nrfx_gppi_init and how to connect the event with the task. I found this docs page, but don't understand how to apply this to my case.

Any help is highly appreciated.

BR Moritz

Parents
  • Hi Håkon,

    thank you for the info. The example you provided shows how to connect the compare event of the timer with the toggle task of a gpio pin.

    I tried to adapt this example to my case where I want to connect the HITOLO trigger event of a gpio pin to the capture event of the timer with aditional calculations during the isr of the pin event, but fail to do so.

    I have added the two projects - one with ncs 3.1.1 which works as intended, one with ncs 3.2.1 which doesn't work. Any idea what I am doing wrong?

    Thank you very much :)

    nRF_gppi.7z

  • Hi,

     

    My apologies for the late reply. Thank you so much for sharing the project, this helped alot to debug what the issue is.

    The problem is related to this change in nrfx 4.0.x:

    Driver internal state is now kept within driver's instance object. Driver instance object must have static storage to remain valid throughout application runtime. Creating multiple driver instances associated with same hardware peripheral instance will not share peripheral state.
    Action : Declare single driver instance within the system and share it among dependent software components, e.g. by using global scope.

     

    Here is a proposed v3.2.x version of your test application:

    #include <zephyr/kernel.h>
    #include <zephyr/drivers/gpio.h>
    #include <nrfx_timer.h>
    #include <nrfx_gpiote.h>
    #include <helpers/nrfx_gppi.h>
    #include <hal/nrf_gpio.h>
    #if defined(CONFIG_GPIO)
    #include <gpiote_nrfx.h>
    #endif
    
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(Logger, LOG_LEVEL_INF);
    
    static const uint32_t quartz_frequency = 16175056;
    
    static const struct gpio_dt_spec btn0 = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios); // P1.13 on nRF54L15DK
    uint32_t btn_psel = NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios);
    static nrfx_timer_t timer_inst = NRFX_TIMER_INSTANCE(NRF_TIMER20);
    
    #define GPIOTE_NODE	DT_NODELABEL(gpiote20)
    
    #if !defined(CONFIG_GPIO)
    static nrfx_gpiote_t gpiote_node = NRFX_GPIOTE_INSTANCE(NRF_GPIOTE_INST_GET(GPIOTE_NODE));
    static nrfx_gpiote_t *gpiote_inst = &gpiote_node;
    #else
    static nrfx_gpiote_t *gpiote_inst = &GPIOTE_NRFX_INST_BY_NODE(GPIOTE_NODE);
    #endif
    
    /* frequency measurement */
    static volatile uint32_t edge_count = 0;
    static volatile uint32_t total_edges = 0;
    static volatile uint32_t total_value = 0;
    static volatile bool new_measurement = true;
    static volatile bool active = true;
    
    void gpiote_handler(nrfx_gpiote_pin_t pin,
                           nrfx_gpiote_trigger_t trigger,
                           void *p_context)
    {
        ARG_UNUSED(p_context);
        ARG_UNUSED(trigger);
    
        if (pin == btn_psel) {
            if (new_measurement){
                new_measurement = false;
                total_edges = 0;
                total_value = 0;
                active = true;
                nrfx_timer_clear(&timer_inst);
                nrfx_timer_resume(&timer_inst);
            } else if (active){
                edge_count++;
                uint32_t value = nrfx_timer_capture_get(&timer_inst, NRF_TIMER_CC_CHANNEL0);
                if (value > 10000000){
                    nrfx_timer_pause(&timer_inst);
                    total_value = value;
                    total_edges = edge_count;
                    edge_count = 0;
                    active = false;
                }
            }
        }
    }
    
    static void timer_init_timer_mode(void)
    {
        nrfx_err_t status;
        uint32_t base_frequency = NRF_TIMER_BASE_FREQUENCY_GET(timer_inst.p_reg);
        nrfx_timer_config_t cfg = NRFX_TIMER_DEFAULT_CONFIG(base_frequency);
        cfg.mode      = NRF_TIMER_MODE_TIMER;       // timer mode
        cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;     // large range
        cfg.p_context = NULL;
    
        status = nrfx_timer_init(&timer_inst, &cfg, NULL);
        NRFX_ASSERT(status == NRFX_SUCCESS);
    
        nrfx_timer_clear(&timer_inst);
    }
    
    static void gpiote_init_edge_input(uint32_t pin)
    {
        nrfx_err_t status;
        uint8_t in_channel;
    
        // init driver instance
        #if !defined(CONFIG_GPIO)
        status = nrfx_gpiote_init(gpiote_inst, NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
        if (status != NRFX_SUCCESS && status != NRFX_ERROR_ALREADY) {
            // handle error
        }
        #endif
        LOG_INF("GPIOTE status: %s",
                      nrfx_gpiote_init_check(gpiote_inst) ? "initialized" : "not initialized");
    
        status = nrfx_gpiote_channel_alloc(gpiote_inst, &in_channel);
        if (status != NRFX_SUCCESS) {
            // handle error
        }
    
        static const nrf_gpio_pin_pull_t pull_cfg = NRF_GPIO_PIN_PULLUP;
        nrfx_gpiote_trigger_config_t trig_cfg = {
            .trigger      = NRFX_GPIOTE_TRIGGER_HITOLO,
            .p_in_channel = &in_channel,
        };
        static const nrfx_gpiote_handler_config_t handler_cfg = {
            .handler  = gpiote_handler,
            .p_context = NULL,
        };
        nrfx_gpiote_input_pin_config_t input_cfg = {
            .p_pull_config    = &pull_cfg,
            .p_trigger_config = &trig_cfg,
            .p_handler_config = &handler_cfg,
        };
    
        status = nrfx_gpiote_input_configure(gpiote_inst, pin, &input_cfg);
        if (status != NRFX_SUCCESS) {
            // handle error
        }
    
        nrfx_gpiote_trigger_enable(gpiote_inst, pin, true);
    }
    
    static void connect_edge_to_timer(uint32_t pin)
    {
        int status;
        nrfx_gppi_handle_t gppi_handle;
    
        // Get the event and task addresses
        uint32_t evt_addr = nrfx_gpiote_in_event_address_get(gpiote_inst, pin);
        uint32_t task_addr = nrfx_timer_task_address_get(&timer_inst, NRF_TIMER_TASK_CAPTURE0);
        // uint32_t task_addr = nrfx_timer_capture_task_address_get(&timer_inst, NRF_TIMER_CC_CHANNEL0);
    
        status = nrfx_gppi_conn_alloc(evt_addr, task_addr, &gppi_handle);
        NRFX_ASSERT(status == NRFX_SUCCESS);
    
        // Enable the connection using the handle
        nrfx_gppi_conn_enable(gppi_handle);
    
        // Enable timer after nrfx_gppi_conn_enable as seen in the example.
        nrfx_timer_enable(&timer_inst);
        LOG_INF("Timer status: %s", nrfx_timer_is_enabled(&timer_inst) ? "enabled" : "disabled");
    }
    
    static void sample(void)
    {
        if (total_value > 0){
            float freq = 1.0f * quartz_frequency*total_edges/total_value;
            LOG_INF("Edges: %d\tValue: %d\t Freq: %.2f Hz", total_edges, total_value, freq);
        }
        new_measurement = true;
    }
    
    int main(void)
    {
        if (!gpio_is_ready_dt(&btn0)) {
            return 0;
        }
        // gpio_pin_configure_dt(&btn0, GPIO_INPUT);
    
    #if defined(__ZEPHYR__) && !defined(CONFIG_GPIO)
        IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_TIMER20), IRQ_PRIO_LOWEST,
                    nrfx_timer_irq_handler, &timer_inst, 0);
        IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_GPIOTE20), IRQ_PRIO_LOWEST,
                    nrfx_gpiote_irq_handler, &gpiote_node, 0);
    #endif
    
        timer_init_timer_mode();
        gpiote_init_edge_input(btn_psel);
        connect_edge_to_timer(btn_psel);
        
        new_measurement = true;
    
        while (1) {
            k_msleep(1000);
            sample();
        }
    
        return 0;
    }
    

     

    Kind regards,

    Håkon

  • Please note:

    "Expressions like NRFX_ASSERT(err == NRFX_SUCCESS) will no longer work as expected."

    from the migration wiki (https://github.com/NordicSemiconductor/nrfx/wiki/nrfx-3.14.0-to-4.0.0)

Reply Children
No Data
Related