Contents

[TOC]

V4L2时间戳

时间戳存储在struct v4l2_buffertimestamp字段中:

1
2
3
4
struct v4l2_buffer {
    // ... 其他字段 ...
    struct timeval timestamp;  // 时间戳(秒 + 微秒)
};
  • timestamp.tv_sec:秒部分(自UNIX Epoch以来的时间:自1970年1月1日00:00:00 UTC 以来经过的秒数。)。
  • timestamp.tv_usec:微秒部分(1秒 = 1,000,000微秒)。

系统单调时钟

单调时钟(Monotonic Clock): 时间起点是系统启动时间,单调时钟从系统启动的那一刻开始计时,并且随着时间的推移单调递增,它不会受到系统时间调整(如 NTP 校准或用户手动更改系统时间)的影响 。

硬件时钟(TSC)

TSC 通常在系统启动(或硬件复位)时被初始化为 0,并从那一刻开始计数。TSC 是一个单调递增的计数器,它记录了自系统启动以来 CPU 的时钟周期数。TSC 的精度非常高,因为它基于 CPU 的时钟频率(通常在 GHz 级别),可以提供纳秒级的时间分辨率。

计算ARM硬件计数器(cntvct_el0)与系统单调时钟(CLOCK_MONOTONIC_RAW)之间的时间偏移量(纳秒级)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
uint64_t readOffsetNs()
{
    unsigned long raw_nsec, tsc_ns;
    unsigned long cycles, frq;
    struct timespec tp;

    asm volatile("mrs %0, cntfrq_el0" : "=r"(frq));
    asm volatile("mrs %0, cntvct_el0" : "=r"(cycles));

    clock_gettime(CLOCK_MONOTONIC_RAW, &tp);

    tsc_ns = (cycles * 100 / (frq / 10000)) * 1000;
    raw_nsec = tp.tv_sec * 1000000000 + tp.tv_nsec;

    uint64_t offset_ns = llabs(tsc_ns - raw_nsec);

    return offset_ns;
}

计算系统启动时间(uptime_ms 系统单调时钟)与 Unix 纪元时间(epoch time)之间的偏移量。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
long getEpochTimeShift()
{
    struct timeval epochtime;
    struct timespec vsTime;
    struct timespec realTime;

    gettimeofday(&epochtime, NULL);
    clock_gettime(CLOCK_MONOTONIC_RAW, &vsTime);
    clock_gettime(CLOCK_REALTIME, &realTime);

    long uptime_ms = vsTime.tv_sec * 1000 + (long)round(vsTime.tv_nsec / 1000000.0);
    long epoch_ms = epochtime.tv_sec * 1000 + (long)round(epochtime.tv_usec / 1000.0);
    long x_ms = realTime.tv_sec * 1000 + (long)round(realTime.tv_nsec / 1000000.0);

    printf("toEpochOffset_ms:%ld, toRealMonoOffset-ms: %ld\n", epoch_ms - uptime_ms, x_ms - uptime_ms);
    return epoch_ms - uptime_ms;
}