Zephyr nrfx_timer_compare

Hi, I am using Zephyr and want to use the nrfx_timer_compare functions.
It seems that the system is working with Timer4 and channel 0 to 4. However, the compare of channel 5 is never fired.

I used IRQ_DIRECT_CONNECT(TIMER4_IRQn, IRQ_PRIO_LOWEST, bus_timer_event, 0); and in my interrupt handler I test NRF_TIMER4->EVENTS_COMPARE[0] up to NRF_TIMER4->EVENTS_COMPARE[5].
All events (NRF_TIMER4->EVENTS_COMPARE[0] to NRF_TIMER4->EVENTS_COMPARE[4]) are detected and handled, but NRF_TIMER4->EVENTS_COMPARE[5] will never be set.

The initialisation looks like:

nrfx_timer_compare( &bus_timer, NRF_TIMER_CC_CHANNEL0, delay, true ); 
nrfx_timer_compare( &bus_timer, NRF_TIMER_CC_CHANNEL1, delay+417, true );
nrfx_timer_compare( &bus_timer, NRF_TIMER_CC_CHANNEL2, 2400, true ); 
nrfx_timer_compare( &bus_timer, NRF_TIMER_CC_CHANNEL3, 5500, true ); 
nrfx_timer_compare( &bus_timer, NRF_TIMER_CC_CHANNEL4, 13500, true );
nrfx_timer_compare( &bus_timer, NRF_TIMER_CC_CHANNEL5, 16300, true );

Any idea why the 5th channel will never be set?

  • Hi Bob

    There is no reason why CC5 shouldn't work. 

    One thing I notice in your code is that you refer to your own event handler in the call to IRQ_DIRECT_CONNECT, rather than the interrupt handler of the nrfx_timer library. 

    Could you change this to point to the nrfx handler instead, like shown here, and see if it works better?

    Best regards
    Torbjørn

  • Hi Torbjørn,

    Thanks for the reply.

    I changed it into:

    {
        IRQ_DIRECT_CONNECT(TIMER4_IRQn, IRQ_PRIO_LOWEST, bus_timer_event, 0);
        
        const nrfx_timer_config_t bus_timer_config =
            {
                .frequency          = NRF_TIMER_FREQ_1MHz,
                .mode               = NRF_TIMER_MODE_TIMER,
                .bit_width          = NRF_TIMER_BIT_WIDTH_32,
                .interrupt_priority = 3,
                .p_context          = NULL
            } ;
    
    
        nrfx_timer_init( &bus_timer, &bus_timer_config, bus_timer_event );
        
        nrfx_timer_compare( &bus_timer, NRF_TIMER_CC_CHANNEL0, _measdelay, true );  
        nrfx_timer_compare( &bus_timer, NRF_TIMER_CC_CHANNEL1, _measdelay+417, true );
        nrfx_timer_compare( &bus_timer, NRF_TIMER_CC_CHANNEL2, 2400, true );          
        nrfx_timer_compare( &bus_timer, NRF_TIMER_CC_CHANNEL3, 5500, true );          
        nrfx_timer_compare( &bus_timer, NRF_TIMER_CC_CHANNEL4, 13500, true );         
        nrfx_timer_compare( &bus_timer, NRF_TIMER_CC_CHANNEL5, 16300, true );         
            
        nrfx_ppi_channel_alloc( &ppi_channel );
        nrfx_timer_enable( &bus_timer );
    }
    
    void bus_timer_event(nrf_timer_event_t event_type, void * p_context)
    {
        switch( event_type )
        {
        case NRF_TIMER_EVENT_COMPARE0 : 
            ...
            break;
        case NRF_TIMER_EVENT_COMPARE1 : 
            ...
            break;
        case NRF_TIMER_EVENT_COMPARE2 : 
            ...
            break;
        case NRF_TIMER_EVENT_COMPARE3 : 
            ...
            break; 
        case NRF_TIMER_EVENT_COMPARE4 : 
            ...
            break;    
        case NRF_TIMER_EVENT_COMPARE5 : 
            ...
            break;    
        default:
            LOG_INF("Unknow event type %d",event_type);
        }
    }
    
    
    

    There is a continuous event generated with event_type = 0. It seems that the system is never clearing the interrupt flag?  

    removing the nrfx_timer_init does not change anything. Removing the IRQ_DIRECT_CONNECT will result in kernel errors.

  • Hi Bob

    I still don't think it is correct to refer to the bus_timer_event in the call to IRQ_DIRECT_CONNECT. Did you try to replace this with nrfx_timer_4_irq_handler instead?

    I tried to set up a simple example with all 6 compare events, and it seems to work fine here: 

    /*
     * Copyright (c) 2016 Intel Corporation
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr.h>
    #include <device.h>
    #include <devicetree.h>
    #include <drivers/gpio.h>
    #include <nrfx_timer.h>
    
    /* 1000 msec = 1 sec */
    #define SLEEP_TIME_MS   1000
    
    /* The devicetree node identifier for the "led0" alias. */
    #define LED0_NODE DT_ALIAS(led0)
    
    #if DT_NODE_HAS_STATUS(LED0_NODE, okay)
    #define LED0	DT_GPIO_LABEL(LED0_NODE, gpios)
    #define PIN	DT_GPIO_PIN(LED0_NODE, gpios)
    #define FLAGS	DT_GPIO_FLAGS(LED0_NODE, gpios)
    #else
    /* A build error here means your board isn't set up to blink an LED. */
    #error "Unsupported board: led0 devicetree alias is not defined"
    #define LED0	""
    #define PIN	0
    #define FLAGS	0
    #endif
    
    // Get a reference to the TIMER1 instance
    static const nrfx_timer_t my_timer = NRFX_TIMER_INSTANCE(4);
    
    // Interrupt handler for the timer
    // NOTE: This callback is triggered by an interrupt. Many drivers or modules in Zephyr can not be accessed directly from interrupts, 
    //		 and if you need to access one of these from the timer callback it is necessary to use something like a k_work item to move execution out of the interrupt context. 
    void timer1_event_handler(nrf_timer_event_t event_type, void * p_context)
    {
    	static int counter = 0;
    	switch(event_type) {
    		case NRF_TIMER_EVENT_COMPARE0:
    			printk("Timer 1 callback 0. Counter = %d\n", counter++);
    			break;
    		case NRF_TIMER_EVENT_COMPARE1:
    			printk("Timer 1 callback 1. Counter = %d\n", counter++);
    			break;
    		case NRF_TIMER_EVENT_COMPARE2:
    			printk("Timer 1 callback 2. Counter = %d\n", counter++);
    			break;
    		case NRF_TIMER_EVENT_COMPARE3:
    			printk("Timer 1 callback 3. Counter = %d\n", counter++);
    			break;
    		case NRF_TIMER_EVENT_COMPARE4:
    			printk("Timer 1 callback 4. Counter = %d\n", counter++);
    			break;
    		case NRF_TIMER_EVENT_COMPARE5:
    			printk("Timer 1 callback 5. Counter = %d\n", counter++);
    			break;		
    		default:
    			printk("Unknown timer event!\n");
    			break;
    	}
    }
    
    // Function for initializing the TIMER1 peripheral using the nrfx driver
    static void timer4_init(void)
    {
    	nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG;
    	timer_config.bit_width = NRF_TIMER_BIT_WIDTH_32;
    	timer_config.frequency = NRF_TIMER_FREQ_1MHz;
    
    	int err = nrfx_timer_init(&my_timer, &timer_config, timer1_event_handler);
    	if (err != NRFX_SUCCESS) {
    		printk("Error initializing timer: %x\n", err);
    	}
    
    	IRQ_DIRECT_CONNECT(TIMER4_IRQn, 0, nrfx_timer_4_irq_handler, 0);
    	irq_enable(TIMER4_IRQn);
    }
    
    // Function for scheduling repeated callbacks from TIMER1
    static void timer4_repeated_timer_start(void)
    {
    	nrfx_timer_enable(&my_timer);
    	nrfx_timer_extended_compare(&my_timer, NRF_TIMER_CC_CHANNEL0, 500000, 
                                    0, true);	
    	nrfx_timer_extended_compare(&my_timer, NRF_TIMER_CC_CHANNEL1, 1000000, 
                                    0, true);
    	nrfx_timer_extended_compare(&my_timer, NRF_TIMER_CC_CHANNEL2, 1500000, 
                                    0, true);
    	nrfx_timer_extended_compare(&my_timer, NRF_TIMER_CC_CHANNEL3, 2000000, 
                                    0, true);	
    	nrfx_timer_extended_compare(&my_timer, NRF_TIMER_CC_CHANNEL4, 2500000, 
                                    0, true);
    	nrfx_timer_extended_compare(&my_timer, NRF_TIMER_CC_CHANNEL5, 3000000, 
                                    0, true);
    }
    
    void main(void)
    {
    	const struct device *dev;
    	bool led_is_on = true;
    	int ret;
    
    	dev = device_get_binding(LED0);
    	if (dev == NULL) {
    		return;
    	}
    
    	ret = gpio_pin_configure(dev, PIN, GPIO_OUTPUT_ACTIVE | FLAGS);
    	if (ret < 0) {
    		return;
    	}
    
    	// Initialize TIMER1
    	timer4_init();
    
    	timer4_repeated_timer_start();
    
    	while (1) {
    		gpio_pin_set(dev, PIN, (int)led_is_on);
    		led_is_on = !led_is_on;
    		k_msleep(SLEEP_TIME_MS);
    	}
    }
    

    I get the following log output, as expected:

    *** Booting Zephyr OS build v2.7.99-ncs1-1  ***
    Timer 1 callback 0. Counter = 0
    Timer 1 callback 1. Counter = 1
    Timer 1 callback 2. Counter = 2
    Timer 1 callback 3. Counter = 3
    Timer 1 callback 4. Counter = 4
    Timer 1 callback 5. Counter = 5

    Please ignore that it says "Timer 1" in the log, I based the code on an existing sample using timer 1 instead of timer 4. 

    Best regards
    Torbjørn

  • Hi Torbjørn,

    I implemented your suggestion and was able to narrow down the problem. In the original code, there is a statement under NRF_TIMER_EVENT_COMPARE4 event, that changed the NRF_TIMER_CC_CHANNEL5: nrfx_timer_extended_compare( &bus_timer, NRF_TIMER_CC_CHANNEL5, 16300 + rand(), 0, true );  

    This will be a source of the problem, but only if the log level is LOG_LEVEL_DBG. When I change the log level to LOG_LEVEL_ERR it seems to work....

  • Hi Bob

    It is good to hear you found the problem. I didn't quite understand what the problem was though...

    Do you mean the problem is in one of the Zephyr drivers, or in your own application code? 

    If you think there is a problem in our code we should follow it up, and see if this is something that can be fixed in a future update. 

    Best regards
    Torbjørn

Related