#clock #timer #delay #no-std

no-std embedded-timers

基于时钟实现的软件定时器和延迟(ms/us)

2 个不稳定版本

0.3.0 2024年7月11日
0.2.0 2024年2月16日

225嵌入式开发


coap-zero 中使用

OLFL-1.3

53KB
520

embedded-timers

此crate为no_std环境中的单调非递减时钟提供了一个通用基础。它定义了ClockInstant特质作为接口,并根据这些特质实现定时器和延迟。

通过将Clock特质作为依赖项,设备驱动程序和应用可以以平台无关的方式开发。因此,此crate对于时钟的作用与embedded-halembedded-nalcrate对于外围或网络抽象的作用相同。

此crate仅关注单调非递减时钟,即与std::time::Instant类型具有相同目的的时钟。它们可以用于测量瞬间之间的持续时间、创建计时器/超时或仅仅等待/延迟程序执行。此crate不涉及时间库可能包含的任何其他内容:此crate不涉及系统时间或墙上的时钟时间,这些时间可能在时钟同步时跳跃。此外,此crate不涵盖时区处理或日历。

设计决策

为了在no_std环境中创建处理时钟的通用接口,此crate旨在做出许多用例和许多用户可接受的决策。在这方面,它试图尽可能简单(枯燥),并避免过于主观或可辩论的决策。例如

  • 使用已定义在core中的任何内容。具体来说,虽然其12字节内存布局似乎对于许多嵌入式应用来说有点过度,但core::time::Duration类型用于持续时间。但它涵盖了从高精度到多年定时等所有用例,并在社区中得到一致同意。
  • 《Clock》和《Instant》特性受《std::time::Instant》的启发,这对于Rust开发者来说应该很熟悉。但与《std::time::Instant》不同,没有假设全球时钟可用。因此,功能被分为两个不同的特性。

使用方法

大多数用户(应用程序、库或设备驱动程序开发者)将依赖于《Clock》特性。然后,可以使用时钟进行计时器或延迟,而不必关心底层的《Instant》类型。

fn application(clock: &impl embedded_timers::clock::Clock) {
    // Get the current instant, the instant type is inferred from the generic Clock
    let earlier = clock.now();
    let later = clock.now();
    // The instant type is guaranteed to support calculations due to the Instant trait
    let time_passed: core::time::Duration = later - earlier;

    // Timers and delays can easily be written manually
    let deadline = clock.now() + core::time::Duration::from_secs(1); // 1 second timer/delay
    loop {
        // By comparing clock.now() with the deadline, we determine if the timer has expired.
        // When doing nothing in the loop, this is a simple busy wait delay.
        if clock.now() > deadline {
            break;
        }
    }

    // Alternatively, the provided helper types for timer and delay can be used
    let mut timer = embedded_timers::timer::Timer::new(clock);
    timer.try_start(core::time::Duration::from_secs(1)).unwrap();
    let is_expired = timer.is_expired().unwrap();
}

在平台级别,需要为时钟类型实现《Clock》特性。《Instant》相关类型需要实现《Instant》特性。如果启用了《std》特性,则《std::time::Instant》实现《Instant》特性,并且可以用于一个非常简单的时钟。

struct StdClock;

impl embedded_timers::clock::Clock for StdClock {
    type Instant = std::time::Instant;
    fn now(&self) -> Self::Instant {
        std::time::Instant::now()
    }
}

在实际的《no_std》环境中,需要手动实现时钟。对于相关的《Instant》类型,它可能使用在instant模块中定义的类型之一。它需要设置适当的中断处理程序,并同步对滴答变量的访问。这可能看起来像这样:

// Global tick counter variable which is incremented in the tick interrupt handler. By using an
// atomic variable, we can do this without unsafe code. Note that using a 32 bit counter for
// milliseconds will wrap around after around 50 days so this might not be feasible in a real
// scenario.
static TICKS: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);

// This tick interrupt handler is assumed to be called once per millisecond
fn tick_handler() {
    TICKS.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}

struct MilliSecondClock32;

impl embedded_timers::clock::Clock for MilliSecondClock32 {
    type Instant = embedded_timers::instant::Instant32<1000>;
    fn now(&self) -> Self::Instant {
        let ticks = TICKS.load(core::sync::atomic::Ordering::Relaxed);
        embedded_timers::instant::Instant32::<1000>::new(ticks)
    }
}

延迟

从时钟中可以创建延迟。这将执行忙等待延迟。

use embedded_timers::clock::Clock;
use embedded_hal::blocking::delay::DelayMs;
#[derive(Debug)]
pub struct MilliSecondClock;

let clock = MilliSecondClock;
let mut delay = embedded_timers::delay::Delay::new(&clock);

loop {
    println!("This shows every second");
    delay.delay_ms(1000_u32);
}

计时器

该库提供了一个方便的计时器接口,具有检查计时器是否《is_running》或《is_expired》以及《duration_left》剩余时间的功能。

use embedded_timers::clock::Clock;
use embedded_timers::timer::Timer;
use embedded_hal::timer::CountDown;
#[derive(Debug)]
pub struct MilliSecondClock;

let clock = MilliSecondClock;
let mut timer = embedded_timers::timer::Timer::new(&clock);

timer.start(core::time::Duration::from_secs(1));

loop {
    if let Ok(expired) = timer.is_expired() {
        if expired {
            println!("This shows every second");
            timer.start(core::time::Duration::from_secs(1));
        }
    }
}

《embedded_timers::Timer》还实现了《embedded_hal::timer::CountDown》,因为这通常是嵌入式计时器的通用接口。

use embedded_timers::clock::Clock;
use embedded_timers::timer::Timer;
use embedded_hal::timer::CountDown;
use embedded_hal::blocking::delay::DelayMs;
#[derive(Debug)]
pub struct MilliSecondClock;

let clock = MilliSecondClock;
let mut timer = embedded_timers::timer::Timer::new(&clock);
let mut delay = embedded_timers::delay::Delay::new(&clock);

timer.start(core::time::Duration::from_secs(1));

loop {
    match timer.wait() {
        Err(nb::Error::WouldBlock) => {
            println!("Timer still running");
            delay.delay_ms(50_u32);
        }
        Err(_) => panic!("TIMER ERROR"),
        Ok(_) => {
            println!("This shows every second");
            timer.start(core::time::Duration::from_secs(1));
        }
    }
}

许可证

开放物流基金会许可证
版本 1.3,2023 年 1 月

请参阅顶级目录中的《LICENSE》文件。

联系方式

弗劳恩霍费尔 IML 嵌入式 Rust 团队 - [email protected]

依赖项

~71KB