embassy book

Time Keeping

In embedded programs, delaying tasks is one of the most common operations. In an event loop, if no other I/O operations are performed, delays need to be inserted to ensure that other tasks have a chance to run before the next iteration of the loop is called. Embassy provides abstractions to delay the current task for a specified interval.

The interfaces for time keeping in Embassy are handled by the embassy-time crate. These types can be used with the internal timer queue in embassy-executor or custom timer queue implementations.

Timer

The embassy::time::Timer type provides two methods for timing.

  • Timer::at(Instant): Creates a future that completes at the specified Instant, relative to system startup time. This is useful for scheduling tasks at a specific point in time.
  • Timer::after(Duration): Creates a future that completes after the specified Duration, relative to the time the future is created. This is useful for creating delays or timeouts.

Here is an example of using Timer::after for periodic tasks:

Hint: The dependencies required to run this example can be found here.

use embassy::executor::{task, Executor};
use embassy::time::{Duration, Timer};
use embassy_rp::rprintln; // For Raspberry Pi Pico examples, replace with println! for other targets

#[task]
/// Task that ticks periodically
async fn tick_periodic() -> ! {
    loop {
        rprintln!("tick!");
        // Async sleep primitive, suspends the task for 500ms.
        Timer::after(Duration::from_millis(500)).await;
    }
}

Delay

The embassy::time::Delay type provides implementations of the embedded-hal and embedded-hal-async traits. This allows for generic delay implementations in drivers.

Here is an example of using Delay with a generic delay function:

Hint: The dependencies required to run this example can be found here.

use embassy::executor::{task, Executor};
use embassy_rp::rprintln; // For Raspberry Pi Pico examples, replace with println! for other targets

#[task]
/// Task that ticks periodically
async fn tick_periodic() -> ! {
    loop {
        rprintln!("tick!");
        // Async sleep primitive, suspends the task for 500ms.
        generic_delay(embassy::time::Delay).await;
    }
}

async fn generic_delay<D: embedded_hal_async::delay::DelayNs>(delay: D) {
    delay.delay_ms(500).await;
}

Improvements

Here are the improvements made to the code and documentation:

  1. Code Readability and Maintainability:

    • Added more descriptive comments to the Timer::at and Timer::after methods, explaining their use cases.
    • Replaced println! with rprintln! in code examples, assuming this is for Raspberry Pi Pico examples based on the embassy_rp import. For other targets, users should use println!. Added a comment to clarify this.
  2. Best Practices and Patterns:

    • Emphasized the use of Timer::at for scheduling tasks at specific times and Timer::after for delays and timeouts, providing clearer guidance on when to use each method.
    • Maintained the use of functional and declarative programming patterns, avoiding classes as per best practices.
    • Used descriptive variable names and followed consistent naming conventions.
  3. No Performance Optimization or Error Handling Improvements:

    • The provided code examples are simple and primarily for demonstrating basic time-keeping functionalities. Performance optimization and error handling are not critical aspects in these examples. In more complex scenarios, these aspects would need to be addressed.

This improved documentation provides a clearer explanation of time keeping in Embassy with enhanced code examples and explanations.