21 个版本 (11 个重大变更)
0.12.1 | 2021年10月2日 |
---|---|
0.12.0 | 2021年5月31日 |
0.11.0 | 2021年5月6日 |
0.10.1 | 2020年11月15日 |
0.8.1 | 2020年7月26日 |
#537 in 嵌入式开发
17,713 每月下载量
在 少于 42 crates 中使用
130KB
2K SLoC
# embedded-time
embedded-time
提供了全面的 Duration
和 Rate
类型库,以及硬件定时器/时钟的 Clock
抽象和用于嵌入式系统的相关 Instant
类型。
此外,还提供了一个与该包中所有类型无缝工作的软件定时器实现。
use embedded_time::{duration::*, rate::*};
let micros = 200_000_u32.microseconds(); // 200_000 ╬╝s
let millis: Milliseconds = micros.into(); // 200 ms
let frequency: Result<Hertz,_> = millis.to_rate(); // 5 Hz
assert_eq!(frequency, Ok(5_u32.Hz()));
动机
嵌入式系统中的时间处理通常与操作系统大不相同。例如,在操作系统上,时间是以任意纪元为基准测量的。嵌入式系统通常不知道(也不关心)实际时间是什么,而是关心自系统启动以来经过了多少时间。
标准库类型的缺点
持续时间
- 存储为
u64
秒和u32
纳秒。 - 这过于夸张,增加了不必要的复杂性,超出了嵌入式系统所需(或期望)的复杂度。
- 任何读取(除秒和纳秒外)都需要进行算术转换到所需单位
- 这比本项目实现的时间单位类似标记联合体慢得多。
瞬间
Instant
类型需要std
。
time
crate 的缺点
《time》crate 是一个出色的库,但并不适合嵌入式系统(尽管它支持 no_std
上下文中的一些功能子集)。它存在一些与核心 Duration
类型相同的缺点(尤其是存储格式),并且依赖于 std
中的 Instant
结构。它还添加了很多在嵌入式环境中很少用到的功能。例如,它提供了全面的日期/时间格式化、时区和日历支持。
背景
什么是 Instant?
在 Rust 生态系统内,似乎有一种惯例是从 Instant 类型调用一个名为 now()
的关联函数。通常没有“时钟”的概念。我认为以这种方式使用 Instant
是违反了 关注点分离 原则的。什么是 Instant
?它是一个读取当前时间的计时实体,还是它本身就是那个时间点。在这种情况下,两者都是。
作为替代,当前的时间点是从一个 时钟 读取的。从 Clock
读取的 Instant
与 Clock
具有相同的精度和宽度(内部类型)。请求两个 Instant
之间的差值会得到一个 Duration
,它可以有不同的精度和/或宽度。
概述
采用的方法类似于 C++ 的 chrono
库。在 Duration
和 Rate
中,使用的是固定点值,即它们由 整数 和 缩放因子 值组成。缩放因子是一个 const
的 Fraction
。这种结构的一个好处是它避免了不必要的算术运算。例如,如果 Duration
类型是 Milliseconds
,调用 Duration::integer()
方法直接返回整数部分,在这种情况下就是由 Duration
表示的毫秒数。转换算术运算仅在显式转换时间单位时(例如,Milliseconds
--> Seconds
)执行。
此外,还提供了广泛的速率类型,包括 Hertz
、BitsPerSecond
、KibibytesPerSecond
、Baud
等。
Duration
类型可以转换为 Rate
类型,反之亦然。
定义
时钟:任何周期性计数的实体(即外部或外围硬件计时器/计数器)。通常,这需要单调。只要满足其他要求,环绕时钟也被视为单调。
环绕时钟:当达到最大值时,下一次计数为最小值的时钟。
计时器:向到期计数的一个实体。
瞬间:从时钟中读取的特定时间点。
持续时间:两个瞬间之间的差异。自某个瞬间以来经过的时间。一段时间。
速率:每单位时间事件的数量,例如频率、数据速率等。
导入
建议的使用语句如下,具体取决于需要什么
use embedded_time::duration::*; // imports all duration-related types and traits
use embedded_time::rate::*; // imports all rate-related types and traits
use embedded_time::clock;
use embedded_time::Instant;
use embedded_time::Timer;
持续时间类型
单位 | 扩展 |
---|---|
小时 | 小时 |
分钟 | 分钟 |
秒 | 秒 |
毫秒 | 毫秒 |
微秒 | 微秒 |
纳秒 | 纳秒 |
- 从
Rate
类型转换
use embedded_time::{duration::*, rate::*};
Microseconds(500_u32).to_rate() == Ok(Kilohertz(2_u32))
- 转换为/从
Generic
Duration
类型
use embedded_time::{duration::*};
Seconds(2_u64).to_generic(Fraction::new(1, 2_000)) == Ok(Generic::new(4_000_u32, Fraction::new(1, 2_000)))
Seconds::<u64>::try_from(Generic::new(2_000_u32, Fraction::new(1, 1_000))) == Ok(Seconds(2_u64))
core
兼容性
- 转换为/从
core::time::Duration
与core
持续时间类型的基准比较
构造和读取毫秒
use embedded_time::duration::*;
let duration = Milliseconds::<u64>(ms); // 8 bytes
let count = duration.integer();
(嵌入式时间持续时间类型的大小仅是内部类型的大小)
use std::time::Duration;
let core_duration = Duration::from_millis(ms); // 12 bytes
let count = core_duration.as_millis();
(核心持续时间类型的大小为12 B)
速率类型
频率
单位 | 扩展 |
---|---|
梅比赫兹 | MiHz |
兆赫兹 | MHz |
基比赫兹 | KiHz |
千赫兹 | kHz |
赫兹 | Hz |
数据速率
单位 | 扩展 |
---|---|
每秒梅比字节 | MiBps |
每秒兆字节 | MBps |
每秒基比字节 | KiBps |
每秒千字节 | KBps |
每秒字节 | Bps |
每秒梅比比特 | Mibps |
每秒兆比特 | Mbps |
每秒基比比特 | Kibps |
每秒千比特 | kbps |
每秒比特 | bps |
符号速率
单位 | 扩展 |
---|---|
梅比波特 | MiBd |
每秒兆波特 | MBd |
每秒基比波特 | KiBd |
每秒千波特 | kBd |
波特 | Bd |
-
在同一类别(频率、数据速率等)和基本(兆、梅比、千、基比)内从/到所有其他速率类型的转换。例如,MiBps(每秒梅比字节)--> Kibps(每秒基比比特)和MBps(每秒兆字节)--> kbps(每秒千比特)。
-
从
Duration
类型转换
use embedded_time::{duration::*, rate::*};
Kilohertz(500_u32).to_duration() == Ok(Microseconds(2_u32))
- 转换为/从
Generic
Rate
类型
use embedded_time::rate::*;
Hertz(2_u64).to_generic(Fraction::new(1,2_000)) == Ok(Generic::new(4_000_u32, Fraction::new(1,2_000)))
Hertz::<u64>::try_from(Generic::new(2_000_u32, Fraction::new(1,1_000))) == Ok(Hertz(2_u64))
硬件抽象
Clock
特质,允许抽象硬件计时器/时钟以进行时间维护。
计时器
- 从
Clock
实现对象产生的软件计时器。 - 单次或周期性/连续的
- 阻塞延迟
- 轮询到期
- 读取经过/剩余的持续时间
可靠性和可用性
- 广泛的测试
- 详细文档,附有示例
- 适用于nRF52_DK板的示例
注释
此crate的一些部分是从各种来源派生的
RTIC
time
(特别是原始整数的time::NumbericalDuration
实现)
许可证:MIT OR Apache-2.0
依赖关系
~405–640KB
~13K SLoC