时间单位

Zephyr内核里有两种时间单位:

  • 时钟周期计数器-cyc
  • 滴答计数器-tick

    时钟周期计数器

    内核通过k_cycle_get_32()k_cycle_get_64()API提供时钟周期计数
    一般时钟周期计数器的单位是由CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC配置,此配置为硬件的时钟频率。

滴答计数器

其实每种RTOS系统都有滴答计数器的概念,滴答计数器的单位也是系统资源可以使用的最小时间单位,例如:软件定时器、队列的超时时间等。
滴答计时器可以通过CONFIG_SYS_CLOCK_TICKS_PER_SEC进行配置。大多数硬件平台(支持设置任意中断超时的硬件平台)上的默认值预计在10 kHz的范围内,软件仿真平台和传统驱动程序使用更传统的100 Hz值。

时间转换

Zephyr 提供了一个转换库,具有对所有时间单位的转换控制。ms(毫秒)、us(微秒)、tickcyc的任何单位都可以转换为任何其他单位。并且提供舍入控制:

  • floor: 向下取整
  • ceil: 向上取整
  • near: 接近取证

最后,输出精度可以指定为32位或64位。
例如:
毫秒输入值转换为向上取整的tick

  1. k_ms_to_ticks_ceil32()

cyc转换为向下取整的64位的us

  1. k_cyc_to_us_floor64()

系统运行时间

k_uptime_get()提供自系统启动以来的正常运行时间值(以毫秒为单位)。
如果需要更高的时间要求,可以使用k_uptime_ticks()来获取自系统启动以来运行的滴答定时器值。

超时设置

Zephyr 提供了许多具有超时参数的 API。从概念上讲,这表示事件发生的时间。例如:

  • 内核阻塞操作(如k_sem_take()k_queue_get())可能会提供超时
  • 内核k_timer对象必须指定其持续时间和时间段的延迟。
  • 内核k_work_delayable API 提供了一个超时参数,指示何时将工作队列项添加到系统队列中。

所有这些超时值都是使用k_timeout_t值指定的。这是一种不透明的结构类型,必须使用一系列内核超时宏之一进行初始化。最常见的K_MSEC ,定义距离当前时间的毫秒值。

超时宏 描述
K_NSEC 获取纳秒的超时延迟
K_USEC 获取微妙的超时延迟
K_MSEC 获取毫秒的超时延迟
K_SECONDS 获取秒的超时延迟
K_MINUTES 获取分钟的超时延迟
K_HOURS 获取小时的超时延迟
K_CYC 获取时钟周期的超时延迟
K_TICKS 获取滴答计数的超时延迟

超时循环的示例

当系统需要采取超时和循环,等待它完成,同时执行一些对底层内核代码进行多次阻塞操作的处理。例如:

  1. void my_wait_for_event(struct my_subsys *obj, int32_t timeout_in_ms)
  2. {
  3. while (true) {
  4. uint32_t start = k_uptime_get_32();
  5. if (is_event_complete(obj)) {
  6. return;
  7. }
  8. /* Wait for notification of state change */
  9. k_sem_take(obj->sem, timeout_in_ms);
  10. /* Subtract elapsed time */
  11. timeout_in_ms -= (k_uptime_get_32() - start);
  12. }
  13. }

如果需要同时检查超时值,那就需要如下设计:

  1. void my_wait_for_event(struct my_subsys *obj, k_timeout_t timeout_in_ms)
  2. {
  3. /* Compute the end time from the timeout */
  4. uint64_t end = sys_clock_timeout_end_calc(timeout_in_ms);
  5. while (end > k_uptime_ticks()) {
  6. if (is_event_complete(obj)) {
  7. return;
  8. }
  9. /* Wait for notification of state change */
  10. k_sem_take(obj->sem, timeout_in_ms);
  11. }
  12. }