[Timer0] Unreasonable Behavior and NRFX nrfx_timer_extended_compare function signature

Hi Nordic,

I am implementing a micros() in the arduino using nrfx driver.

Below is test code and I got some questions.

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/types.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/util.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/device.h>
#include <zephyr/sys/poweroff.h>
#include <zephyr/sys/util.h>

#include <nrfx_timer.h>
#include <stdio.h> // Include for printf (or use printk for Zephyr)

#define NRFX_LOG_MODULE EXAMPLE
#define NRFX_EXAMPLE_CONFIG_LOG_ENABLED 1
#define NRFX_EXAMPLE_CONFIG_LOG_LEVEL 3
#include <nrfx_log.h>

#define TIMER_INST_IDX 1

// Timer instance
static nrfx_timer_t timer = NRFX_TIMER_INSTANCE(TIMER_INST_IDX);

// Timer handler
static void timer_handler(nrf_timer_event_t event_type, void *p_context)
{
    printk("timer_handler() \n");

    if (event_type == NRF_TIMER_EVENT_COMPARE0)
    {
        char *p_msg = p_context;
        printk("Timer finished. Context passed to the handler: >%s<", p_msg);
        // NRFX_LOG_INFO("Timer finished.");
    }
    if (event_type == NRF_TIMER_EVENT_COMPARE1)
    {
        char *p_msg = p_context;
        printk("Timer finished. Context passed to the handler: >%s<", p_msg);
        // NRFX_LOG_INFO("Timer finished.");
    }
}

// Initialize timer for high-resolution timing
uint32_t ticks;
void timer_init(void)
{
    nrfx_err_t status;

    /* configure IRQ */
    IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_TIMER_INST_GET(TIMER_INST_IDX)), IRQ_PRIO_LOWEST,
                NRFX_TIMER_INST_HANDLER_GET(TIMER_INST_IDX), 0, 0);

    /* get base frequency of given timer (in our case timer-0) */
    uint32_t base_frequency = NRF_TIMER_BASE_FREQUENCY_GET(timer.p_reg);
    nrfx_timer_config_t config = NRFX_TIMER_DEFAULT_CONFIG(base_frequency);
    config.bit_width = NRF_TIMER_BIT_WIDTH_32; // 32-bit timer
    config.p_context = "Some context";
    
    // printk("config.frequency: %d\n", config.frequency);
    // note: .frequency represents base frequency not a prescaler factor
    // config.frequency = NRF_TIMER_FREQ_1MHz; // Set timer frequency to 1 MHz (1 us resolution)
    // config.frequency = NRF_TIMER_FREQ_500kHz;    // Set timer frequency to 1 MHz (1 us resolution)
    // config.frequency = NRF_TIMER_FREQ_250kHz; // Set timer frequency to 1 MHz (1 us resolution)
    // config.frequency = NRF_TIMER_FREQ_125kHz;    // Set timer frequency to 1 MHz (1 us resolution)

    /* init timer */
    status = nrfx_timer_init(&timer, &config, timer_handler);
    if (status != NRFX_SUCCESS)
    {
        printk("Timer initialization failed: %d\n", status); // Use printk() if in Zephyr
        return;
    }

    nrfx_timer_clear(&timer);

    /* configure prescaler */
    nrf_timer_prescaler_set(timer.p_reg, NRF_TIMER_FREQ_1MHz);
    // nrf_timer_prescaler_set(timer.p_reg, NRF_TIMER_FREQ_16MHz);

    // Convert 3,000,000 microseconds (3 seconds) to ticks
    // uint32_t ticks = nrfx_timer_us_to_ticks(&timer, (uint32_t)3000000);
    ticks = nrfx_timer_us_to_ticks(&timer, (uint32_t)3000000);

    printk("ticks: %d\n", ticks);

    // nrfx_timer_extended_compare(&timer, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_STOP_MASK, true);
    nrfx_timer_extended_compare(&timer, NRF_TIMER_CC_CHANNEL1, ticks, NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK, true);

    // nrfx_timer_clear(&timer);
    nrfx_timer_enable(&timer);
}

// Get current time in microseconds
uint32_t micros(void)
{
    // Capture the current timer value
    return nrfx_timer_capture(&timer, NRF_TIMER_CC_CHANNEL0);
}

int main(void)
{
    set_led_color_cyan();

    /* init button irq */
    int ret = init_button_irq();
    if (ret != 0)
    {
        printk("Error init_button_irq()\n");
        return 0;
    }

    timer_init();

    while (1)
    {

        uint32_t time_us = micros();
        printf("Current time: %u us (%u)\n", time_us, ticks); // Use printk() if in Zephyr

        // if (time_us > ticks)
        // {
        //     nrfx_timer_disable(&timer);
        // }

        // Add a delay to avoid flooding the output
        k_msleep(100); // Sleep for 100 milli second, if using Zephyr
        // k_msleep(1000); // Sleep for 1 second, if using Zephyr
    }
}

[Question 1]

Timer 1 and Timer 2 works perfectly as I expected. You can check it on the Segger RTT Viewer (upper image)

However, Timer 0 doesn't cleared correctly and just keep increasing it's value. Can you please chek on your side?

FYI, I am using SPIM and TWIM simultaneously. Could it impact on Timer 0's unexpected behavior?

[Question 2]: 

A timer could have multiple C/C (=Capture and Compare) channel (=register).

So by having multiple channel, we can capture (=read timer value) and configure compare mode (=configure interrupt on/off or clear/stop a timer).

Please see below method's signature.

// nrfx_timer_extended_compare(&timer, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_STOP_MASK, true);
    nrfx_timer_extended_compare(&timer, NRF_TIMER_CC_CHANNEL1, ticks, NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK, true);

Is there any case that 2nd arg (=channel) and 3rd arg (=action) not to be the same channel?

If not it's prone to make a human error on the application side.

How do you think? ThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinkingThinking

Parents Reply Children
  • Second question has no clear answer, its possible that you can find a very creative use case for this kind of API abuse.

    But I would also always expect a mistake when the channel numbers don't match.

  • Is there any case that 2nd arg (=channel) and 3rd arg (=action) not to be the same channel?

    No this would not work, since the fourth argument (=action) only enables the short and the second argument configures the the intterrupt on the different channel. So if the channels mismatch here, then you configure a cc channel but enable the wrong short which is not what we intend normally to use. But like Turbo_J said, you can be always creative to use the side effects of misuse of API if you are very clear and sure to know what you are doing. But then you are on your own with the side effects.

Related