时间单位
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(微秒)、tick或cyc的任何单位都可以转换为任何其他单位。并且提供舍入控制:
- floor: 向下取整
- ceil: 向上取整
- near: 接近取证
最后,输出精度可以指定为32位或64位。
例如:
将毫秒输入值转换为向上取整的tick值
k_ms_to_ticks_ceil32()
将cyc转换为向下取整的64位的us值
k_cyc_to_us_floor64()
系统运行时间
k_uptime_get()提供自系统启动以来的正常运行时间值(以毫秒为单位)。
如果需要更高的时间要求,可以使用k_uptime_ticks()来获取自系统启动以来运行的滴答定时器值。
超时设置
Zephyr 提供了许多具有超时参数的 API。从概念上讲,这表示事件发生的时间。例如:
- 内核阻塞操作(如
k_sem_take()或k_queue_get())可能会提供超时 - 内核
k_timer对象必须指定其持续时间和时间段的延迟。 - 内核
k_work_delayableAPI 提供了一个超时参数,指示何时将工作队列项添加到系统队列中。
所有这些超时值都是使用k_timeout_t值指定的。这是一种不透明的结构类型,必须使用一系列内核超时宏之一进行初始化。最常见的K_MSEC ,定义距离当前时间的毫秒值。
| 超时宏 | 描述 |
|---|---|
| K_NSEC | 获取纳秒的超时延迟 |
| K_USEC | 获取微妙的超时延迟 |
| K_MSEC | 获取毫秒的超时延迟 |
| K_SECONDS | 获取秒的超时延迟 |
| K_MINUTES | 获取分钟的超时延迟 |
| K_HOURS | 获取小时的超时延迟 |
| K_CYC | 获取时钟周期的超时延迟 |
| K_TICKS | 获取滴答计数的超时延迟 |
超时循环的示例
当系统需要采取超时和循环,等待它完成,同时执行一些对底层内核代码进行多次阻塞操作的处理。例如:
void my_wait_for_event(struct my_subsys *obj, int32_t timeout_in_ms){while (true) {uint32_t start = k_uptime_get_32();if (is_event_complete(obj)) {return;}/* Wait for notification of state change */k_sem_take(obj->sem, timeout_in_ms);/* Subtract elapsed time */timeout_in_ms -= (k_uptime_get_32() - start);}}
如果需要同时检查超时值,那就需要如下设计:
void my_wait_for_event(struct my_subsys *obj, k_timeout_t timeout_in_ms){/* Compute the end time from the timeout */uint64_t end = sys_clock_timeout_end_calc(timeout_in_ms);while (end > k_uptime_ticks()) {if (is_event_complete(obj)) {return;}/* Wait for notification of state change */k_sem_take(obj->sem, timeout_in_ms);}}
