Is there a simple example of how to enable two GPIOTE interrupts for fast timing?

I would like to monitor the states of switches sw0 and sw1 on my nrf7002dk board using GPIOTE interrupts.

Initially this is just for example purposes.

I eventually need to assign two other GPIO pins where I need to have the ability to time ~30 microsecond accuracy and I am finding that the standard Zephyr PORT interrupts not fast enough for my timing purposes.

The nrfx example that I looked at assigns just one GPIOTE pin and I am having difficulty assigning another pin. Specifically when it comes time to IRQ_CONNECT and nrfx_gpiote_in_init the second GPIO pin.

Thank you.

  • Vidar,

    I am still having trouble finding a simple timer example that uses TIMER mode rather than COUNTER mode.

    I need to capture the time difference between edges with microsecond accuracy.

    I understand how to detect GPIO edge interrupts and how to start the timer (in this case set to a frequency of 1MHz). Is there a way that I can read the timer value from the interrupt handler every time that I get a GPIO edge?

    I do not think I need the added complexity of using PPI and would like to keep it very simple for now. I am using GPIO rather than GPIOTE to keep things simple.

    Thanks.

    Adam

  • Hi Adam,

    Yes, you can use the nrfx_timer_capture() to read the current counter value for your timer.

    Best regards,

    Vidar

  • Thanks Vidar,

    I keep getting unexpected values when I use nrfx_timer_capture().

    I am not certain that I am setting things up correctly. I would just like to know how to run a 1MHz timer and then be able to read the number of ticks when I get an edge interrupt.

    Without an example, I don't seem to be able to accomplish this.

    Adam K.

  • Hi Adam,

    Here is an updated version of the sample I first uploaded, which enables a timer. It doesn't attempt to capture the signal periods, but it shows how you can capture the counter value from the ISR.

    main.c

    /*
     * Copyright (c) 2012-2014 Wind River Systems, Inc.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <stdio.h>
    #include <zephyr/kernel.h>
    
    #include <zephyr/drivers/clock_control.h>
    #include <zephyr/drivers/clock_control/nrf_clock_control.h>
    
    #include <nrfx_gpiote.h>
    #include <nrfx_timer.h>
    #include <hal/nrf_gpio.h>
    #include <helpers/nrfx_gppi.h>
    #if defined(DPPI_PRESENT)
    #include <nrfx_dppi.h>
    #else
    #include <nrfx_ppi.h>
    #endif
    
    #include <zephyr/logging/log.h>
    #include <zephyr/irq.h>
    #include <zephyr/drivers/gpio.h>
    LOG_MODULE_REGISTER(nrfx_sample, LOG_LEVEL_INF);
    
    #define GPIOTE_INST	NRF_DT_GPIOTE_INST(DT_NODELABEL(signal_input_pins), gpios)
    
    #define INPUT_SIG_1 NRF_DT_GPIOS_TO_PSEL_BY_IDX(DT_NODELABEL(signal_input_pins), gpios, 0)
    
    const nrfx_gpiote_t gpiote = NRFX_GPIOTE_INSTANCE(GPIOTE_INST);
    
    nrfx_timer_t timer_inst = NRFX_TIMER_INSTANCE(0);
    
    static void pin_evt_handler(nrfx_gpiote_pin_t pin,
    			   nrfx_gpiote_trigger_t trigger,
    			   void *context)
    {
    	uint32_t ticks_now;
    
    	ticks_now = nrfx_timer_capture(&timer_inst, NRF_TIMER_CC_CHANNEL0);
    
    	LOG_INF("GPIO input event callback for P%d.%d. Timestamp: %d", 
    		NRF_PIN_NUMBER_TO_PORT(pin), NRF_PIN_NUMBER_TO_PIN(pin), ticks_now);
    	
    }
    
    /* Request HF crystal oscillator for better timer accuracy */
    static void clock_init(void)
    {
    	int err;
    	int res;
    	struct onoff_manager *clk_mgr;
    	struct onoff_client clk_cli;
    
    	clk_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
    	if (!clk_mgr) {
    		LOG_WRN("Unable to get the Clock manager\n");
    		return;
    	}
    
    	sys_notify_init_spinwait(&clk_cli.notify);
    
    	err = onoff_request(clk_mgr, &clk_cli);
    	if (err < 0) {
    		LOG_WRN("Clock request failed: %d\n", err);
    		return;
    	}
    
    	do {
    		err = sys_notify_fetch_result(&clk_cli.notify, &res);
    		if (!err && res) {
    			LOG_WRN("Clock could not be started: %d\n", res);
    			return;
    		}
    	} while (err);
    
    	LOG_INF("Clock has started\n");
    }
    
    static void timer_init(void)
    {
    	nrfx_err_t err;
    	nrfx_timer_config_t config = NRFX_TIMER_DEFAULT_CONFIG(NRFX_MHZ_TO_HZ(1));
    	
    	config.bit_width = NRF_TIMER_BIT_WIDTH_32;
    
    	err = nrfx_timer_init(&timer_inst, &config, NULL);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_timer_init error: 0x%08X", err);
    	}
    
    	nrfx_timer_clear(&timer_inst);
    
    	nrfx_timer_enable(&timer_inst);
    }
    
    static void gpiote_init(void) 
    {
    	nrfx_err_t err; 
    	static const nrf_gpio_pin_pull_t pull_config = NRF_GPIO_PIN_PULLUP;
    	uint8_t in_sig1_channel;
    
    	static const nrfx_gpiote_handler_config_t handler_config = {
        	.handler = pin_evt_handler,
    	};
    
    	err = nrfx_gpiote_init(&gpiote, 0);
    	if (err != NRFX_SUCCESS && err != NRFX_ERROR_ALREADY) {
    		LOG_ERR("nrfx_gpiote_init error: 0x%08X", err);
    		return;
    	}
    
    	// Configure input for SIGNAL 1
    	nrfx_gpiote_trigger_config_t trigger_config_sig1 = {
    	    .trigger = NRFX_GPIOTE_TRIGGER_HITOLO,
    	    .p_in_channel = &in_sig1_channel,
    	};
    	nrfx_gpiote_input_pin_config_t input_config = {
    	    .p_pull_config = &pull_config,
    	    .p_trigger_config = &trigger_config_sig1,
    	    .p_handler_config = &handler_config
    	};	
    
    	err = nrfx_gpiote_channel_alloc(&gpiote, &in_sig1_channel);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("Failed to allocate in_channel, error: 0x%08X", err);
    		return;
    	}
    
    	err = nrfx_gpiote_input_configure(&gpiote, INPUT_SIG_1, &input_config);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_gpiote_input_configure error: 0x%08X", err);
    		return;
    	}
    	
    	nrfx_gpiote_trigger_enable(&gpiote, INPUT_SIG_1, true);
    }
    
    
    int main(void)
    {
    	LOG_INF("Hello World! %s\n", CONFIG_BOARD);
    	
    	clock_init();
    	timer_init();
    	gpiote_init();
    
    	return 0;
    }
    

    Symbol to be added to prj.conf

    CONFIG_NRFX_TIMER0=y

    Best regards,

    Vidar

  • Thanks Vidar,

    The clock_init function was missing from my code. I was not aware that had to be done. Thank you.

    I am able to produce a 135KHz reference frequency from the device that I am trying to monitor with my particular GPIO pin. That should give me a consistent high edge to low edge time of about 3.7 microseconds. I am capturing the first 100 cycles and printing them out in microseconds when my capturing has stopped.

    017 021 020 016 016 016 016 020 020 016
    016 020 020 016 016 016 021 020 016 016
    016 021 020 016 016 016 021 020 016 016
    016 021 020 016 016 016 021 020 016 016
    016 021 016 016 016 021 020 016 016 016
    021 020 016 016 016 021 020 016 016 016
    021 020 016 016 016 021 020 016 016 016
    021 020 016 016 016 021 020 016 016 016
    021 020 016 016 016 021 020 016 016 016
    021 020 016 016 016 021 020 016 016 016

    With the clock/timer example code that you gave me I am measuring much longer times and they are inconsistent. They are 4 to 5 times longer than I expected.

    I can change the timer config frequency and see the values increase by 2, 4, 8, etc. so I know the timer is working fine.

    For reference, I am NOT using the GPIOTE functions but rather the simpler GPIO setup functions (using GPIO_INT_EDGE_TO_ACTIVE and GPIO_INT_EDGE_TO_INACTIVE) and then capturing the high/low edge values of NRF_TIMER_CC_CHANNEL0 in my callback function and then storing the difference between the low/high edge times (the values I am printing out).

    Is using GPIO setup with callback not fast enough for this application? Is it possible that I am missing edges with this approach and the values I am measuring are actually 4 or 5 edges apart rather than every edge? Is there a way to prioritize the GPIO callback interrupt to get better performance?

    Another thing that makes me think I am missing edges is the number of edges that I am counting when I start and stop the measuring.

    uart:~$ start
    started: 1970/01/01 00:25:22 UTC
    uart:~$ stop
    stopped: 1970/01/01 00:25:24 UTC
    rising_edges: 57884
    falling_edges: 57883

    For a 135KHz signal I would have expected more like 270000 rising and falling edges in 2 seconds. The values are 4 to 5 times less than I would have expected.

    Thanks again.

    Adam

Related