时间单位
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_delayable
API 提供了一个超时参数,指示何时将工作队列项添加到系统队列中。
所有这些超时值都是使用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);
}
}