This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

NRFX RTC Driver - How to stop compare interrupt handler disabling interrupts

I'm using the nrfx_rtc drivers on RTC2 to generate a regular interrupt once a second, using compare interrupts.

My problem is that the nrfx_rtc interrupt handler disables compare events and interrupts, so the interrupt only happens once. I want it to repeat by clearing the counter in the compare interrupt handler.

See nrfx_rtc.c irq_handler() which calls nrf_rtc_event_disable() and nrf_rtc_int_disable()

I can work around it by putting the following in the NRFX_RTC_INT_COMPARE0 handler

      NRF_RTC2->EVTENSET = (1UL << 16);
      NRF_RTC2->INTENSET = (1UL << 16);

but this is a real cludge, bypassing nrfx drivers. How should I do this properly, using nrfx drivers?

I'm using nRF52840 (PAN1780 module), SDK 17.0.2, Nordic SES 5.34a

Many thanks

Parents
  • Just to clarify, I am using a compare interrupt, and clearing the counter in the interrupt handler, so it should repeat. However the nrfx_rtc.c irq_handler() calls nrf_rtc_event_disable() and nrf_rtc_int_disable(), so I have to re-enable them in the interrupt handler

    Here's my init code

       nrfx_rtc_config_t config = NRFX_RTC_DEFAULT_CONFIG;
       config.prescaler = 4095;  // gives 8Hz (32kHz/(prescaler + 1)) - note 12 bit so this is max prescaler value
       err_code = nrfx_rtc_init(&rtc_inst, &config, rtc_handler);
       APP_ERROR_CHECK(err_code);
       err_code = nrfx_rtc_cc_set(&rtc_inst, 0, 8, true);    // Compare counter 
       APP_ERROR_CHECK(err_code);
       nrfx_rtc_enable(&rtc_inst);

    and my interrupt handler (with cludge)

    static void rtc_handler(nrfx_rtc_int_type_t int_type)
    {
       if (int_type == NRFX_RTC_INT_COMPARE0)
       {
          nrfx_rtc_counter_clear(&rtc_inst);        // clear counter
          NRF_RTC2->EVTENSET = (1UL << 16);         // cludge to re-enable event and interrupt
          NRF_RTC2->INTENSET = (1UL << 16);
          // 1 second tick timers....
          if (ben_timer)
             ben_timer--;
       }
    }

    My question is how I do this properly, either by persuading the nrfx driver to not disable interrupts, or by re-enabling them each time using nrfx calls rather than my cludge

    I must admit, this seems a really common application (generating a repeating 1 second interrupt), and I was a bit surprised that the nrfx driver behaves this way

  • I was a bit suprised to see it implemented like this too. Typically you have to explitely disable interrupts when using the nRFX drivers. I will report this internally and ask if there is a particular reason for disabling the interrupt from the IRQ handler.

    benmack said:
    I must admit, this seems a really common application (generating a repeating 1 second interrupt), and I was a bit surprised that the nrfx driver behaves this way

     It's very common, but we generally recommend using the app timer library on top the RTC driver for this (Timer library). Is that something you have considered for your application?

  • Thanks Vidar, much appreciated :-)

    I did look at the app_timer stuff, but to be honest I struggled with the extra layer of abstraction. I'm an old school low-level programmer, much happier dealing direct with hardware registers...

    Cheers, Ben

  • I understand, it can be nice to work closer to the HW sometimes Slight smile The benefit of the timer library is that it makes it easy to set up multiple periodic and one-off timer interrupts with just a single RTC instance. However, in this case, when you only need one periodic interrupt source, it should be easy enough to just work directly with the driver.

    I haven't heard back from the driver team yet, but I think another approach you can use it to re-enable the interrupt from your application callback. This way you don't have to patch the driver code.

    /** @brief: Function for handling the RTC0 interrupts.
     * Triggered on TICK and COMPARE0 match.
     */
    static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
    {
        uint32_t err_code; 
    
        if (int_type == NRF_DRV_RTC_INT_COMPARE0)
        {
            nrf_gpio_pin_toggle(COMPARE_EVENT_OUTPUT);
            
            nrf_drv_rtc_counter_clear(&rtc);
            err_code = nrf_drv_rtc_cc_set(&rtc,0,COMPARE_COUNTERTIME * 8,true);
            APP_ERROR_CHECK(err_code);
        }
    }

    Cheers,

    Vidar

  • Hi Vidar, thanks for that

    I did try that, but it is a heavy bit of code to call in every interrupt

    Call me old-school but I think I'll stick with my direct register writes for now, and hope they tweak the drivers in the future.

    Thanks again

Reply Children
  • Hi,

    I understand the concern , but I believe the overhead of re-enabling the interrupt from the driver callback should be negligible considering the CPU is running at 64 MHz. That said, I don't foresee any problems with patching the driver like you did either, it should be fine. I will update the thread once I hear back from the team.

  • I know this thread is old, but currently nrfx interrupts are working very well if you use the following procedure. It is not necessary to manually reenable. I have tried it across multiple NRF52 and NRF53 hardware with consistent results, including Nordic and 3rd-party boards. Under nrf connect v2.0.0, interrupts do not get disabled. Perhaps something strange is going on with your handler. Here is everything...it works, it's tested, and it's quite up to date.

    void timer_event_handler(nrf_timer_event_t event_type, void *p_context)
    {
       switch (event_type) {

         case NRF_TIMER_EVENT_COMPARE0:
    /*
     * Be aware that this event handler callback function, not just the ISR, runs in an
     * interrupt context, so some architectures may behave differently due to varying
     * implementations. Found this out when an led function on thingy52 wouldn't work
     * because a series of improvements had turned a gpio toggle into i2c operations
     * on the thingy52's sx_1509b, which would block, so I had to defer those operations
     * to mainline processing.
     */
              /* Do stuff, turn on light, etc. */
              // Uncomment next line if you implemented ONE-SHOT below, but want to resume timer anyway
              // nrfx_timer_resume(&timer);
              break;

         default:
              break;
       }
    }
    nrfx_err_t err;

    err = nrfx_timer_init(&timer, &config, &timer_event_handler);
    if (err != NRFX_SUCCESS) {
    printk("nrfx_timer_init failed! err = %d\n", err);
    }
    /*
     * The following macro is *NECESSARY* to get an operational timer.
     * If you do not enable this ISR, the timer will not appear to be working.
     * The IRQ_PRIO should match roughly what you set up in the config
     * struct of the init call above (default lowest is a good starting point).
     * Note that the irq name matches exactly the function defined in nrfx_timer.c
     * It seems like that part could have been canned in another macro
     * (maybe it is and I just overlooked it). Unless I set interrupts to true, my
     * function never got triggered, and unless I ran this connect, the default
     * callback never got connected--the system would just crash as soon as
     * my trigger value hit.
     */
    IRQ_CONNECT(TIMER0_IRQn, IRQ_PRIO_LOWEST,
    nrfx_timer_0_irq_handler, NULL, 0);

    /* STOP (with clear) in the MASK below gives a ONE-SHOT timer */
    /* CLEAR (alone) in the MASK gives a REPEATING timer */

    nrfx_timer_extended_compare(&timer,
    NRF_TIMER_CC_CHANNEL0,
    3500U, // Time in ticks, use one of the functions if you wish to pass microseconds or milliseconds
    // NRF_TIMER_SHORT_COMPARE0_STOP_MASK |  // Uncommenting this makes a ONE-SHOT timer
    NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
    true);
    nrfx_timer_clear(&timer);
    nrfx_timer_enable(&timer);

    ............

    Hope this helps someone.

Related