clock_time.md 12 KB

Clock Time Device

Introduction

The clock_time subsystem provides a unified framework for time management in RT-Thread, consolidating the functionality of the legacy hwtimer, ktime, and cputime subsystems into a single, coherent API.

Key Features

  • Clocksource: Free-running counter for timekeeping and timestamps
  • Clockevent: Programmable timeout events for high-precision timing
  • High-Resolution Timers (hrtimer): Efficient timer management with linked-list scheduling
  • POSIX Clock Support: Compatible with standard POSIX clock APIs
  • Boottime Tracking: Monotonic time since system boot
  • Backward Compatibility: Compatibility layers for legacy APIs

Design Philosophy

The clock_time subsystem follows RT-Thread's C-OOP (Object-Oriented Programming in C) design pattern:

  1. Unified Device Abstraction: rt_clock_time_device encapsulates hardware timer capabilities
  2. Operations Structure: rt_clock_time_ops defines hardware-specific operations
  3. Capability Flags: Explicitly indicates clocksource and clockevent support
  4. Flexible Integration: Works with both MCU and MPU platforms

Architecture

┌─────────────────────────────────────────────────────────┐
│                   Application Layer                      │
│  (POSIX APIs, Delays, Timekeeping, High-Res Timers)    │
└──────────────────┬──────────────────────────────────────┘
                   │
┌──────────────────▼──────────────────────────────────────┐
│              clock_time Subsystem                        │
│  ┌─────────────┐  ┌──────────┐  ┌──────────────────┐   │
│  │ Clocksource │  │ Boottime │  │ High-Res Timers │   │
│  └─────────────┘  └──────────┘  └──────────────────┘   │
│  ┌─────────────┐  ┌──────────────────────────────┐     │
│  │ Clockevent  │  │    Compatibility Layers      │     │
│  └─────────────┘  └──────────────────────────────┘     │
└──────────────────┬──────────────────────────────────────┘
                   │
┌──────────────────▼──────────────────────────────────────┐
│           Hardware Timer Abstraction                     │
│        (rt_clock_time_device + ops)                     │
└──────────────────┬──────────────────────────────────────┘
                   │
┌──────────────────▼──────────────────────────────────────┐
│              BSP Timer Driver                           │
│   (SysTick, ARM Arch Timer, RISC-V Timer, etc.)        │
└─────────────────────────────────────────────────────────┘

Usage

Registering a Clock Time Device

BSP drivers should implement the rt_clock_time_ops and register a device:

#include <rtdevice.h>

static rt_uint64_t my_timer_get_freq(void)
{
    return 1000000; /* 1MHz timer */
}

static rt_uint64_t my_timer_get_counter(void)
{
    return MY_TIMER->CNT; /* Read hardware counter */
}

static rt_err_t my_timer_set_timeout(rt_uint64_t delta)
{
    if (delta == 0)
    {
        /* Cancel timeout */
        MY_TIMER->CR &= ~TIMER_ENABLE;
        return RT_EOK;
    }
    
    MY_TIMER->CMP = MY_TIMER->CNT + delta;
    MY_TIMER->CR |= TIMER_ENABLE | TIMER_INT_ENABLE;
    return RT_EOK;
}

static const struct rt_clock_time_ops my_timer_ops =
{
    .get_freq = my_timer_get_freq,
    .get_counter = my_timer_get_counter,
    .set_timeout = my_timer_set_timeout,
};

static struct rt_clock_time_device my_clock_device;

int my_timer_init(void)
{
    /* Initialize hardware timer */
    my_clock_device.ops = &my_timer_ops;
    
    /* Register with both clocksource and clockevent capabilities */
    rt_clock_time_device_register(&my_clock_device, 
                                  "hw_timer",
                                  RT_CLOCK_TIME_CAP_CLOCKSOURCE | 
                                  RT_CLOCK_TIME_CAP_CLOCKEVENT);
    
    return 0;
}
INIT_DEVICE_EXPORT(my_timer_init);

/* Timer ISR should call rt_clock_hrtimer_process() */
void MY_TIMER_IRQHandler(void)
{
    /* Clear interrupt flag */
    MY_TIMER->SR = 0;
    
    /* Process hrtimer timeouts */
    rt_clock_hrtimer_process();
}

Using Clocksource APIs

Get Time Information

/* Get clock resolution (in ns, scaled by RT_CLOCK_TIME_RESMUL) */
rt_uint64_t res = rt_clock_time_getres();

/* Get clock frequency in Hz */
rt_uint64_t freq = rt_clock_time_getfreq();

/* Get current counter value */
rt_uint64_t cnt = rt_clock_time_getcnt();

Get Boottime

struct timespec ts;
rt_clock_time_boottime_ns(&ts);
rt_kprintf("Boot time: %d.%09d seconds\n", ts.tv_sec, ts.tv_nsec);

struct timeval tv;
rt_clock_time_boottime_us(&tv);

time_t t;
rt_clock_time_boottime_s(&t);

Time Conversion

rt_uint64_t cnt = 1000000; /* counter ticks */

/* Convert to time units */
rt_uint64_t ns = rt_clock_time_cnt_to_ns(cnt);
rt_uint64_t us = rt_clock_time_cnt_to_us(cnt);
rt_uint64_t ms = rt_clock_time_cnt_to_ms(cnt);

/* Convert from time units */
cnt = rt_clock_time_ns_to_cnt(1000000); /* 1ms in nanoseconds */
cnt = rt_clock_time_us_to_cnt(1000);    /* 1ms in microseconds */
cnt = rt_clock_time_ms_to_cnt(1);       /* 1ms */

Using High-Resolution Timers

Create and Start a Timer

#include <rtdevice.h>

static void timeout_callback(void *param)
{
    rt_kprintf("Timer timeout!\n");
}

void hrtimer_example(void)
{
    struct rt_clock_hrtimer timer;
    
    /* Initialize timer */
    rt_clock_hrtimer_init(&timer, "my_timer", 
                         RT_TIMER_FLAG_ONE_SHOT,
                         timeout_callback, 
                         RT_NULL);
    
    /* Start timer with 1ms delay */
    rt_uint64_t delay_cnt = rt_clock_time_ms_to_cnt(1);
    rt_clock_hrtimer_start(&timer, delay_cnt);
    
    /* ... */
    
    /* Stop timer if needed */
    rt_clock_hrtimer_stop(&timer);
    
    /* Cleanup */
    rt_clock_hrtimer_detach(&timer);
}

Periodic Timer

static void periodic_callback(void *param)
{
    rt_kprintf("Periodic tick\n");
}

void periodic_timer_example(void)
{
    struct rt_clock_hrtimer timer;
    
    rt_clock_hrtimer_init(&timer, "periodic", 
                         RT_TIMER_FLAG_PERIODIC,
                         periodic_callback, 
                         RT_NULL);
    
    /* Start with 100ms period */
    rt_uint64_t period_cnt = rt_clock_time_ms_to_cnt(100);
    rt_clock_hrtimer_start(&timer, period_cnt);
}

High-Precision Delays

/* Delay in nanoseconds */
rt_clock_ndelay(1000);  /* 1 microsecond */

/* Delay in microseconds */
rt_clock_udelay(1000);  /* 1 millisecond */

/* Delay in milliseconds */
rt_clock_mdelay(100);   /* 100 milliseconds */

/* Using a custom timer for delays */
struct rt_clock_hrtimer timer;
rt_clock_hrtimer_mdelay(&timer, 50);  /* 50ms delay */

Migration Guide

From ktime

The clock_time subsystem provides direct API compatibility when RT_CLOCK_TIME_COMPAT_KTIME is enabled:

Old ktime API New clock_time API
rt_ktime_boottime_get_ns() rt_clock_time_boottime_ns()
rt_ktime_cputimer_getres() rt_clock_time_getres()
rt_ktime_cputimer_getcnt() rt_clock_time_getcnt()
rt_ktime_hrtimer_init() rt_clock_hrtimer_init()
rt_ktime_hrtimer_start() rt_clock_hrtimer_start()

From cputime

When RT_CLOCK_TIME_COMPAT_CPUTIME is enabled:

Old cputime API New clock_time API
clock_cpu_gettime() rt_clock_time_getcnt()
clock_cpu_getres() rt_clock_time_getres()
rt_cputime_sleep() rt_clock_mdelay() or use hrtimer
rt_cputime_udelay() rt_clock_udelay()

From hwtimer

Hardware timer devices should be migrated to the new clock_time device model:

Old hwtimer approach:

static const struct rt_hwtimer_ops my_ops = { ... };
static struct rt_hwtimer_device my_device;
my_device.ops = &my_ops;
rt_device_hwtimer_register(&my_device, "timer0", RT_NULL);

New clock_time approach:

static const struct rt_clock_time_ops my_ops = { ... };
static struct rt_clock_time_device my_device;
my_device.ops = &my_ops;
rt_clock_time_device_register(&my_device, "timer0",
    RT_CLOCK_TIME_CAP_CLOCKSOURCE | RT_CLOCK_TIME_CAP_CLOCKEVENT);

Configuration

Kconfig Options

RT_USING_CLOCK_TIME              - Enable clock_time subsystem
RT_CLOCK_TIME_COMPAT_KTIME       - Enable ktime compatibility layer
RT_CLOCK_TIME_COMPAT_CPUTIME     - Enable cputime compatibility layer
RT_CLOCK_TIME_COMPAT_HWTIMER     - Enable hwtimer compatibility layer

Typical Configuration

For new projects:

CONFIG_RT_USING_CLOCK_TIME=y
# Compatibility layers not needed

For migrating projects:

CONFIG_RT_USING_CLOCK_TIME=y
CONFIG_RT_CLOCK_TIME_COMPAT_KTIME=y
CONFIG_RT_CLOCK_TIME_COMPAT_CPUTIME=y
CONFIG_RT_CLOCK_TIME_COMPAT_HWTIMER=y

Best Practices

For MCU Scenarios

  1. Use SysTick or similar: For basic timekeeping, SysTick provides adequate resolution
  2. Hardware timers for precision: Use dedicated hardware timers for microsecond-level precision
  3. Consider power: Minimize timer wakeups in low-power applications

For MPU Scenarios

  1. Architecture timers: Use ARM Generic Timer or RISC-V timer for best performance
  2. SMP safety: The hrtimer implementation uses spinlocks for SMP systems
  3. High-frequency sources: MPUs can handle higher frequency timers (MHz range)

General Guidelines

  1. Single default device: Register one primary clock_time device as the system default
  2. ISR efficiency: Keep rt_clock_hrtimer_process() call in ISR short
  3. Resolution vs. overhead: Higher resolution timers have higher overhead
  4. Fallback support: The tick-based fallback ensures functionality even without hardware timer

API Reference

See the complete API documentation in /components/drivers/include/drivers/clock_time.h.

Examples

Complete examples can be found in:

  • /examples/clock_time/ - Basic usage examples
  • BSP timer driver implementations for reference

Troubleshooting

Timer not firing

  • Verify hardware timer is properly configured
  • Check that ISR calls rt_clock_hrtimer_process()
  • Ensure timer interrupt is enabled

Poor precision

  • Verify timer frequency is appropriate for your use case
  • Check for interrupt latency issues
  • Consider using a higher frequency timer

Compatibility issues

  • Enable appropriate compatibility layer in Kconfig
  • Check for API signature changes in migration
  • Verify BSP-specific timer implementation

See Also