Issue on get ms on zephyr

Hello,

I’m trying to obtain millisecond precision on nRF Connect using the MCU’s internal RTC.

My code:

/*!
* \file      smtc_hal_rtc.c
*
* \brief     RTC Hardware Abstraction Layer implementation
*
*/

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/rtc.h>
#include <zephyr/drivers/counter.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
#include <nrfx_rtc.h>

#include "smtc_hal_rtc.h"
#include "smtc_hal_dbg_trace.h"

LOG_MODULE_REGISTER(smtc_hal_rtc, LOG_LEVEL_INF);

static struct k_sem wake_sem;

static const struct device *const rtc_dev = DEVICE_DT_GET(DT_NODELABEL(rtc2));
static uint32_t freq = 32768;

static uint32_t rtc_wrap_counter = 0;
static hal_lp_timer_irq_t lptim_tmr_irq[2];

static void rtc_handler(const struct device *dev, uint8_t chan_id, uint32_t ticks, void *user_data);
static void rtc_overflow_handler(const struct device *dev, void *user_data);
static void rtc_wakeup_handler(void *obj);

void hal_rtc_init( void ) {
    if (!device_is_ready(rtc_dev)) {
        LOG_ERR("RTC device not ready");
        return;
    }

    struct counter_top_cfg top_cfg = {
        .ticks = 0xFFFFFF, // 24-bit RTC
        .callback = rtc_overflow_handler,
        .user_data = NULL,
        .flags = COUNTER_TOP_CFG_RESET_WHEN_LATE
    };

    int err = counter_set_top_value(rtc_dev, &top_cfg);
    if (err) {
        LOG_ERR("Failed to set top value: %d", err);
    }
    
    counter_start(rtc_dev);
}

static void rtc_overflow_handler(const struct device *dev, void *user_data) {
    ARG_UNUSED(dev);
    ARG_UNUSED(user_data);
    rtc_wrap_counter++;
    LOG_INF("ENTROU");
}

uint32_t hal_rtc_get_time_s(void) {
    uint32_t wrap1, wrap2, ticks;
    uint32_t total_ticks;

    do {
        wrap1 = rtc_wrap_counter;
        int err = counter_get_value(rtc_dev, &ticks);
        if (err) {
            LOG_ERR("Failed to get counter value: %d", err);
            return 0;
        }
        wrap2 = rtc_wrap_counter;
    } while (wrap1 != wrap2);

    total_ticks = ticks + (wrap1 * (1UL << 24));
    return total_ticks / freq;
}

uint32_t hal_rtc_get_time_ms(void) {
    uint32_t ticks;
    int err = counter_get_value(rtc_dev, &ticks);
    if (err) {
        LOG_ERR("Failed to get counter value: %d", err);
        return 0;
    }
    uint32_t total_ticks = ticks + (rtc_wrap_counter * (1UL << 24));
    uint32_t ms = (total_ticks * 1000UL) / freq;
    LOG_INF("ticks: %d", ticks);
    LOG_INF("total_ticks: %d", total_ticks);
    LOG_INF("ms: %d", ms);
    return ms;
}

void hal_lp_timer_start(const uint32_t milliseconds, const hal_lp_timer_irq_t *tmr_irq) {
    hal_lp_timer_irq_enable();
    uint32_t freq = counter_get_frequency(rtc_dev); // 32768
    uint32_t ticks = (milliseconds * freq) / 1000;

    struct counter_alarm_cfg alarm_cfg = {
        .callback = rtc_handler,
        .ticks = ticks,
        .user_data = (void *)tmr_irq,
        .flags = COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE
    };

    int cancel_err = counter_cancel_channel_alarm(rtc_dev, 0);
    if (cancel_err && cancel_err != -ENOTSUP) {
        SMTC_HAL_TRACE_PRINTF("Erro ao cancelar alarme existente: %d", cancel_err);
    }

    int err = counter_set_channel_alarm(rtc_dev, 0, &alarm_cfg);
    if (err != 0) {
        SMTC_HAL_TRACE_PRINTF("Erro ao configurar alarme: %d", err);
        return;
    }

    static hal_lp_timer_irq_t persistent_irq;
    persistent_irq = *tmr_irq;
    lptim_tmr_irq[0] = persistent_irq;
}

void hal_lp_timer_stop(void) {
    hal_lp_timer_irq_disable();
    int err = counter_cancel_channel_alarm(rtc_dev, 0);
    if (err != 0) {
        SMTC_HAL_TRACE_PRINTF("Failed to cancel alarm: %d", err);
    }
    NVIC_ClearPendingIRQ(RTC2_IRQn);
}

void hal_lp_timer_irq_enable( void ) {
    NVIC_EnableIRQ( RTC2_IRQn );
}

void hal_lp_timer_irq_disable( void ) {
    NVIC_DisableIRQ( RTC2_IRQn );
}

static void my_rtc_alarm_callback(const struct device *dev, uint8_t chan_id, uint32_t ticks, void *user_data) {
    // ARG_UNUSED(dev);
    // ARG_UNUSED(chan_id);
    // ARG_UNUSED(ticks);
    // ARG_UNUSED(user_data);
    // // k_sem_give(&wake_sem);
}

void hal_rtc_wakeup_timer_set_ms(int32_t milliseconds) {
    // k_sem_init(&wake_sem, 0, 1);

    // uint32_t freq = counter_get_frequency(rtc_dev);
    // uint32_t ticks = (milliseconds * freq) / 1000;

    // struct counter_alarm_cfg alarm_cfg = {
    //     .flags = COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE,
    //     .ticks = ticks,
    //     .callback = my_rtc_alarm_callback,
    //     .user_data = NULL,
    // };

    // int cancel_err = counter_cancel_channel_alarm(rtc_dev, 1);
    // if (cancel_err && cancel_err != -ENOTSUP) {
    //     LOG_ERR("Erro ao cancelar alarme existente: %d", cancel_err);
    // }

    // int err = counter_set_channel_alarm(rtc_dev, 1, &alarm_cfg);
    // if (err < 0) {
    //     LOG_ERR("Erro ao configurar alarme (%d)", err);
    //     return;
    // }

    // // k_sem_take(&wake_sem, K_FOREVER);
    // counter_cancel_channel_alarm(rtc_dev, 1);
}

static void rtc_handler(const struct device *dev, uint8_t chan_id, uint32_t ticks, void *user_data) {
    hal_lp_timer_irq_t *irq = (hal_lp_timer_irq_t *)user_data;
    if (irq && irq->callback) {
        irq->callback(irq->context);
    }
}

static void rtc_wakeup_handler( void* obj ){
    // Implementação do handler de wakeup se necessário
}

After 131000 ms, a got: 

[00:02:18.097,473] <inf> smtc_hal_rtc: ms: 131023
[00:02:18.107,849] <inf> smtc_hal_rtc: ticks: 4294052
[00:02:18.108,184] <inf> smtc_hal_rtc: total_ticks: 4294052
[00:02:18.118,469] <inf> smtc_hal_rtc: ms: 131044
[00:02:18.123,809] <inf> smtc_hal_rtc: ticks: 4294575
[00:02:18.129,119] <inf> smtc_hal_rtc: total_ticks: 4294575
[00:02:18.134,429] <inf> smtc_hal_rtc: ms: 131060
[00:02:18.139,709] <inf> smtc_hal_rtc: ticks: 4295097
[00:02:18.145,019] <inf> smtc_hal_rtc: total_ticks: 4295097
[00:02:18.150,360] <inf> smtc_hal_rtc: ms: 3
[00:02:18.155,609] <inf> smtc_hal_rtc: ticks: 4295618
[00:02:18.160,919] <inf> smtc_hal_rtc: total_ticks: 4295618
[00:02:18.166,259] <inf> smtc_hal_rtc: ms: 19
[00:02:18.166,534] <inf> smtc_hal_rtc: ticks: 4295976
[00:02:18.171,844] <inf> smtc_hal_rtc: total_ticks: 4295976
[00:02:18.182,128] <inf> smtc_hal_rtc: ms: 30

Could someone help me find where my error is?

  • Hello,

    I encountered an overflow issue when converting ticks to milliseconds using the operation:

    uint32_t ms = (total_ticks * 1000UL) / freq;

    To resolve it, I modified the code as follows:

    uint64_t ms = ((uint64_t)total_ticks * 1000UL) / freq;
    LOG_INF("ticks: %u, wrap_counter: %u, total_ticks: %u, ms: %u", ticks, wrap1, total_ticks, ms);
    return (uint32_t)ms;


    This change prevents the overflow, but I’m not entirely convinced it’s the optimal solution. Could please advise on the best or recommended way to obtain milliseconds from the RTC on Zephyr?
Related