[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

Related